政府統計e-StatからデータをAPIで取得、Pythonデータ分析アプリ

# 完成したWEBアプリ 
![Animation2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722467/1fd0e85d-4d94-64dd-a803-8ee4f1ca3bcb.gif)

政府統計e-Statを使ってデータ分析してみました。
APIで統計データを取得して知りたい情報を加工してグラフとして表示します。
https://otaruit-estat-app-streamlit-app-lzpvdr.streamlit.app/

# データ取得（APIを使用）
統計局からAPIを使ってデータをもらってきます。
APIKEYはe-Statで登録してもらってきます。
https://www.e-stat.go.jp/mypage/user/preregister

APIキーは公開できない情報なので隠します。
streamlitで隠したいファイルはsecrets.tomlファイルを作成してそこに下記のように格納します。
```
API_KEY = "e-Statで作ったきみだけのAPIキー"
```

下記でAPIキーを取得します。

```python

with open('env.json', 'r') as f:
    env_vars = json.load(f)

# API_KEY を取得する
api_key = env_vars['API_KEY']
df = jdr.get_data_estat_statsdata(api_key, statsDataId="0003293502")

```

statsDataIdはそれぞれの統計データに紐づいています。
リクエストのパラメータに記載されていることもあります。

```python
import jpy_datareader as jdr
```
はe-StatのAPIを使いやすくするためのライラブリです。
長いコードを書く必要がないので本当に便利です。


# 試しにデータを絞って表示
```python
df_filtered = df[
    (df['性別'] == '女子') &
    (df['体格測定・テスト項目'] == 'テスト_10m障害物歩行（秒）')
]
```

例えば性別は女子のみ、体格測定かテスト項目の条件では「テスト_10m障害物歩行（秒）」を指定してみます。

```
         体格測定・テスト項目 運動部・スポーツクラブ所属  性別 運動テスト年齢 時間軸(年度次)   平均値    標本数  標準偏差
0   テスト_10m障害物歩行（秒）       所属していない  女子  65-69歳   2014年度  6.96  488.0  1.20
1   テスト_10m障害物歩行（秒）       所属していない  女子  65-69歳   2015年度  6.78  486.0  1.18
2   テスト_10m障害物歩行（秒）       所属していない  女子  65-69歳   2016年度  7.13  533.0  1.34
3   テスト_10m障害物歩行（秒）       所属していない  女子  65-69歳   2017年度  7.22  497.0  1.25
4   テスト_10m障害物歩行（秒）       所属していない  女子  70-74歳   2014年度  7.43  466.0  1.23
5   テスト_10m障害物歩行（秒）       所属していない  女子  70-74歳   2015年度  7.37  456.0  1.43
6   テスト_10m障害物歩行（秒）       所属していない  女子  70-74歳   2016年度  7.76  445.0  1.41
7   テスト_10m障害物歩行（秒）       所属していない  女子  70-74歳   2017年度  7.59  468.0  1.36
8   テスト_10m障害物歩行（秒）       所属していない  女子  75-79歳   2014年度  8.09  472.0  1.53
9   テスト_10m障害物歩行（秒）       所属していない  女子  75-79歳   2015年度  7.95  469.0  1.60
10  テスト_10m障害物歩行（秒）       所属していない  女子  75-79歳   2016年度  8.38  461.0  1.71
11  テスト_10m障害物歩行（秒）       所属していない  女子  75-79歳   2017年度  8.23  488.0  1.50
24  テスト_10m障害物歩行（秒）        所属している  女子  65-69歳   2014年度  6.51  366.0  1.10
25  テスト_10m障害物歩行（秒）        所属している  女子  65-69歳   2015年度  6.51  346.0  1.20
26  テスト_10m障害物歩行（秒）        所属している  女子  65-69歳   2016年度  6.89  339.0  1.33
27  テスト_10m障害物歩行（秒）        所属している  女子  65-69歳   2017年度  6.93  380.0  1.23
28  テスト_10m障害物歩行（秒）        所属している  女子  70-74歳   2014年度  6.97  375.0  1.25
29  テスト_10m障害物歩行（秒）        所属している  女子  70-74歳   2015年度  6.98  376.0  1.25
30  テスト_10m障害物歩行（秒）        所属している  女子  70-74歳   2016年度  7.46  380.0  1.41
31  テスト_10m障害物歩行（秒）        所属している  女子  70-74歳   2017年度  7.24  401.0  1.28
32  テスト_10m障害物歩行（秒）        所属している  女子  75-79歳   2014年度  7.65  344.0  1.35
33  テスト_10m障害物歩行（秒）        所属している  女子  75-79歳   2015年度  7.50  335.0  1.46
34  テスト_10m障害物歩行（秒）        所属している  女子  75-79歳   2016年度  7.90  359.0  1.52
35  テスト_10m障害物歩行（秒）        所属している  女子  75-79歳   2017年度  7.90  351.0  1.44
```

このような形で取得してきてくれます。

# データを整理する

以下でデータの項目名、その値などを取得します。
uniquie()　で項目のリストを取得します。
st.selectbox　でドロップダウンリストを作成します。
df['運動テスト年齢'].unique().tolist()　で年齢のリストを取得します。

age_selection のドロップダウンリストで指定された年齢の平均値も
mean()　で
計算します。


```python
df_test_items_unique = df['体格測定・テスト項目'].unique()
selected_test_item = st.selectbox('体格測定・テスト項目を選択してください', df_test_items_unique)

comparison_item = st.selectbox('比較する項目を選択してください', ['性別', '運動部・スポーツクラブ所属'])

age_options = df['運動テスト年齢'].unique().tolist()
age_options.append('全年齢の平均')
age_selection = st.selectbox('年齢を選択してください', age_options)

df_filtered = df[df['体格測定・テスト項目'] == selected_test_item]

if age_selection == '全年齢の平均':
    df_grouped = df_filtered.groupby([comparison_item, '時間軸(年度次)'])['平均値'].mean().unstack()
    title_suffix = '全年齢平均'
else:
    df_filtered = df_filtered[df_filtered['運動テスト年齢'] == age_selection]
    df_grouped = df_filtered.groupby([comparison_item, '時間軸(年度次)'])['平均値'].mean().unstack()
    title_suffix = f'{age_selection} 別'
```
# データを表示する

下記のようにデータを表示するコードを書いていきます。

```python
if df_grouped.empty:
    st.write("データがありません。条件を変更して再試行してください。")

# ↑データが空でないかチェック
    
else:
    fig, ax = plt.subplots(figsize=(12, 6))

    # ↑figsize でグラフのサイズを指定します。

    df_grouped.T.plot(ax=ax, marker='o')

    # ↑データをプロットします

    ax.set_xlabel('年度', fontproperties=font_prop)
    ax.set_ylabel('平均値', fontproperties=font_prop)
    ax.set_title(f'{selected_test_item} の {comparison_item} 別平均値 ({title_suffix})', fontproperties=font_prop)

    # ↑軸のラベルとタイトルを設定します。文字化けしないようにフォントも設定。
    
    legend = ax.legend(title=comparison_item, prop=font_prop, loc='best')
    plt.setp(legend.get_texts(), fontproperties=font_prop)

    # ↑データが空でないかチェック

    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)

    # ↑軸の目盛りのフォントを設定

    st.pyplot(fig)

    # ↑グラフを表示する
```

# 結果

![Screenshot 2024-08-19 155257.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722467/08ac26f9-bb73-ce6c-883e-72e3f6e6b184.png)


# matplotlibの文字化け対策

Git上に日本語フォントをアップロードします。
fontsフォルダを作成してそこに好きな日本語フォントを格納してください。

```python
from matplotlib import font_manager

# 日本語フォントを設定
font_path = 'fonts/ipaexg.ttf'
font_prop = font_manager.FontProperties(fname=font_path)


```
fontpropertiesにフォントを設定します。
```python
    ax.set_xlabel('年度', fontproperties=font_prop)
    ax.set_ylabel('平均値', fontproperties=font_prop)
    ax.set_title(f'{selected_test_item} の {comparison_item} 別平均値 ({title_suffix})', fontproperties=font_prop)
    
    legend = ax.legend(title=comparison_item, prop=font_prop, loc='best')
    plt.setp(legend.get_texts(), fontproperties=font_prop)

    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)
```

# 結果考察のような所感

コロナの影響なのか若年層で運動能力が若干落ちている気がする。（完全に所感）
データ分析結果の考察もPythonでできればいいのですが、今度いいモジュール探してみようと思います。