[Python]Matplotlibで対象者数と平均を1つのグラフに表示(x軸が連続値の場合)

学術系のデータ分析をPythonで行いました。

**データを視覚化するのに便利な関数**を作成したので共有します。

:::note warn
投稿内容は個人の見解であり、所属する組織の公式見解ではありません。
:::

## 記事の対象者
今回の記事は、次の悩みを持つ人を対象に執筆しています。

- データ分析をPythonで実施したい
- 連続変数と被説明変数の関係性を可視化したい

## この記事を読むうえで、必要な知識

- Pythonによるデータ分析の基礎知識を習得している
- Matplotlibの基礎知識を習得している

## ざっくりとした要件定義
###作成したもの
create_graph関数
(関数名は、改善の余地があるかもしれません。)

###Input
1. x軸の変数(連続変数)の列名
2. y軸の変数の列名
3. 対象となるDataFrame
4. x値を四捨五入で切り捨てする桁数 etc

###Output
一定範囲内のx値ごとの、対象者数とy値の平均をグラフとして出力

<img width="600" alt="plt_graph" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522259/0406d842-eaff-f31b-3320-681b91f8961b.png">

##コード
データセットは、Airbnbが公開しているデータセットの中から、2021年9月29日時点での'Detailed Listings data for Tokyo'を使用しました。
このデータセットの中には、Airbnbに登録されている物件情報が格納されています。
なお今回の分析では、この物件情報の中の'Price'(価格)と'Reviews per month'(月ごとのレビュー値)の関係性を見ていきます。


2021年10月18日時点では、Airbnbのデータセットは[こちら](http://insideairbnb.com/get-the-data.html)から誰でもダウンロードできます。

###ライブラリのインポート
```python
import pandas as pd
import matplotlib.pyplot as plt

from matplotlib import font_manager
import japanize_matplotlib
```

### データ前処理のコード
```python
data_dir = 'dataset/'
df=pd.read_csv(data_dir + 'listings.csv')

df_f = df[['price','reviews_per_month']]

def convert_price(df,col):
    '''
    airbnbの'price'列をintに変換するための関数
    '''
    df_temp = df[[col]].copy()
    # 1文字目の'$'と最後の3文字の'.00'を削除
    df_temp['temp'] = df[col].str[1:-3]
    # ','を削除し、intへ変換
    df_temp['temp'] = df_temp['temp'].str.replace(',', '')
    df_temp['temp'] = df_temp['temp'].astype('int')
    return df_temp['temp']

df_f.loc[:,'price'] = convert_price(df_f,'price')

#reviewがついていない物件を削除
df_f = df_f[df_f['reviews_per_month'].notnull()]

```

前処理後のoutputは次の通りです。

```python
#code
df_f.head()

#output
_	price	reviews_per_month
0	11000	1.50
1	7816	2.33
2	30000	0.78
3	14036	2.73
4	3000	0.98


#code
df_f.shape

#output
(8582, 2)

```

###グラフを表示するコード
```python
def col_rounded(df,col,digit):
    '''
    連続値の列を四捨五入する。
    四捨五入は、1の位より大きい値で行う。
    ex. 百の位で切り捨てる場合、digit=3
    '''
    #切り捨て除算により四捨五入の切り捨てを実施
    df['temp'] = df[col]//(10**digit)
    df['temp'] = df['temp']*(10**digit)
    return df['temp']

def df_grouped(x_col, y_col, col_min=0, col_max=float('inf'), df=df):
    '''
    y軸の平均のdf_mと、対象者カウントのdf_cを作成する。
    '''
    df_f=df[(df[x_col]>=col_min)&(df[x_col]<=col_max)]
    
    df_m=df_f.groupby(x_col)[[y_col]].mean()
    df_c=df_f.groupby(x_col)[[y_col]].count()
    return df_m, df_c

def show_graph(x_col, y_col, df_m, df_c, g_width, col_min=0, col_max=float('inf'), x_label='x'):
    '''
    対象者数の棒グラフとyの平均の折れ線グラフを表示する。
    '''
    # プロットエリアを作成
    fig, ax1 = plt.subplots(1,1,figsize=(10,8))
    ax2 = ax1.twinx()
    
    #対象者数の棒グラフを作成
    ax1.bar(df_m.index,df_c[y_col],width=g_width, color='lightblue',label='対象者数')
    #対象者数の一定範囲内での平均を作成
    ax2.plot(df_m[y_col],linestyle='solid',color='k',marker='',label='平均')
    
    #legendを作成
    handler1, label1 = ax1.get_legend_handles_labels()
    handler2, label2 = ax2.get_legend_handles_labels()
    ax1.legend(handler1+handler2,label1+label2,loc=1)
    
    #ラベルを作成
    ax1.set_xlabel(x_label)
    ax1.set_ylabel('対象者数')
    ax1.grid(True)
    ax2.set_ylabel('平均')
    
    plt.title('対象者数と平均')
    
    #グラフを表示
    fig.show()

def create_graph(x_col, y_col, df, digit, col_min=0, col_max=float('inf'), x_label='x'):
    '''
    関数を組み合わせる。
    dfを入力すると、グラフが表示される。
    '''
    df_temp = df.copy()
    
    x_col_r = x_col + 'rounded'
    df_temp[x_col_r] = col_rounded(df_temp,x_col,digit)
    
    df_m, df_c=df_grouped(x_col_r, y_col, col_min, col_max,df_temp)
    
    g_width = 10**digit - 10**(digit-1)*2
    show_graph(x_col_r, y_col, df_m, df_c, g_width, col_min=col_min, col_max=col_max, x_label=x_label)

create_graph('price', 'reviews_per_month', df_f, 3, col_min=0, col_max=20000, x_label='価格')
```

このコードを実施すると、次のグラフが表示されます。

<img width="600" alt="plt_graph" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522259/0406d842-eaff-f31b-3320-681b91f8961b.png">

##まとめ
今回は、x軸が連続値である場合の視覚化方法を提案しました。
学術系のデータ分析では細かい修正が多いので、このような関数を作っておくと便利です。

興味があれば試してみてください！

##参考文献
[[Python]matplotlibで左右に２つの軸があるグラフを書く方法](https://qiita.com/supersaiakujin/items/e2ee4019adefce08e381
)