#本記事について
Pythonのpandasを使ったデータ分析において、groupby関数はグループごとに演算してくれる便利な関数です。
私がよく利用するのは、
df.groupby(df['col1'])['col2'].mean() や .describe()
といったオーソドックスな関数ですが、
「分割されたデータフレームごとに処理したい」ことがあり、
for文とget_groupを組み合わせるとだけで、都合よく処理できることが分かりましたので紹介します。
#データの準備
import pandas as pd
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np
iris_dataset = load_iris()
df_iris=pd.DataFrame(iris_dataset.data,columns=iris_dataset.feature_names)
#targetの列を追加
df_iris.loc[:,'target']=iris_dataset.target
#品種名のdictionaryを作成
iris_map=dict(zip([0,1,2],iris_dataset.target_names))
#DataFrameとdictionaryをmap関数でつなぎtarget_namesの列を追加
df_iris.loc[:,'target_names']=df_iris['target'].map(iris_map)
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | target | target_names |
---|---|---|---|---|---|
5.1 | 3.5 | 1.4 | 0.2 | 0 | setosa |
4.9 | 3.0 | 1.4 | 0.2 | 0 | setosa |
- | - | - | - | - | - |
5.7 | 2.8 | 4.1 | 1.3 | 1 | versicolor |
- | - | - | - | - | - |
6.3 | 3.3 | 6.0 | 2.5 | 2 | virginica |
#target_namesにgroupby関数を適用してみる | |||||
品種('target_names')ごとにデータフレーム(df_iris)を分割します。 | |||||
分割したものを gp としました。 |
gp = df_iris.groupby('target_names')
#分割されたオブジェクトの属性を調べてみる
In[0]:type(gp)
Out[0]:pandas.core.groupby.generic.DataFrameGroupBy
In[1]:print(gp)
Out[1]:<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000028788A33708>
groupbyを使って分割したデータセットをそのまま使うことはできません。
そこで、for文を使って属性などを調査してみます。
#for文を使う
In[2]:for d_gp in gp:
print(d_gp)
Out[2]:
147 6.5 3.0 ... 2 virginica
148 6.2 3.4 ... 2 virginica
149 5.9 3.0 ... 2 virginica
[50 rows x 6 columns])
In[3]:type(d_gp)
out[3]:tuple
分割されたデータフレームはタプル型変数(d_gp)として収納されているようです。
ここで、タプルの中身を確認するために、以下を打ち込んでみると、
In[4]:d_gp[0]
Out[4]:'virginica'
In[5]:d_gp[1]
Out[5]:
sepal length (cm) sepal width (cm) ... target target_names
100 6.3 3.3 ... 2 virginica
101 5.8 2.7 ... 2 virginica
102 7.1 3.0 ... 2 virginica
103 6.3 2.9 ... 2 virginica
147 6.5 3.0 ... 2 virginica
148 6.2 3.4 ... 2 virginica
149 5.9 3.0 ... 2 virginica
[50 rows x 6 columns]
と書き出されるので、for文の実行後の状態は、"target_names"の3つ目の水準'virginica'のデータフレームが d_gp に代入されていることが確認できます。
したがって、**d_gp[1]だけを繰り返し処理してもいいのですが、ここでは~~d_gp[0]**を活用して、get_group関数によって~~イテレータの性質をつかって、forループのみで特定のデータセットを取り出して処理していきます。
#get_groupによりタプルに収納されたデータを取り出す。
#forループでデータフレームを抽出する
for文で取り出せるのは2つのタプルであり、
タプルの1つ目にはgroupbyにかけた列の水準(品種:setosa,versicolor,virginica)が収納され、
2つ目にはそれぞれのデータフレームが収納されています。
このタプルの1つ目に収納された水準を変数として、get_groupでタプルの2つ目に収納されたデータフレームを取り出して、水準毎に処理をしていきます。
これをそれぞれ取り出すのは、forループでforとinの間に2つの変数を用意してあげると、取り出すことができます。つまり、2つ目の変数が分割されたデータフレームです。
以下はsetosa,versicolor,virginicaの品種ごとに分割されたデータフレームを
品種を指定してデータフレームを取り出し、
「ガクの長さ(sepal length)」と「ガクの幅(sepal width)」をプロットしたもの。
#2021/8/12修正 forループでget_groupを使わない方法に修正
#以下でd_gp[0]はidxとなる
#for d_gp in gp:
# df_g=gp.get_group(d_gp[0])
for idx,df_g in gp:
##ここ以下に分割したデータフレームを使って処理したいことを書く
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
X=df_g[df_g.columns[0]].values
y=df_g[df_g.columns[1]].values
#ax.set_title(str.capitalize(d_gp[0])+" "+\
ax.set_title(str.capitalize(idx+" "+\
str.capitalize(df_g.columns[0])+\
' vs '+str.capitalize(df_g.columns[1]))
ax.scatter(X,y,marker='o',color='darkblue',edgecolor="")
cor=np.corrcoef(X, y)[0,1]
ax.set_xlabel(str.capitalize(df_g.columns[0]))
ax.set_ylabel(str.capitalize(df_g.columns[1]))
ax.text(0.99, 0.01,"correlation:{0:.2}".format(cor),
horizontalalignment='right', verticalalignment='bottom',
fontsize=12,color="blue",transform=ax.transAxes)
plt.show()
以上です。
#おわりに
初めてQiitaに投稿してみます。
Qiitaには助けられることばかりでしたので、誰かの助けになればと思います。
#参考文献
Pythonデータ分析/機械学習のための基本コーディング! pandasライブラリ活用入門 (impress top gear) (日本語) 単行本(ソフトカバー)
ISBN-10: 4295005657
ISBN-13: 978-4295005650