groupby関数を使ってデータフレームを分割して処理する方法

#本記事について
Pythonのpandasを使ったデータ分析において、groupby関数はグループごとに演算してくれる便利な関数です。
私がよく利用するのは、
df.groupby(df['col1'])['col2']**.mean()**　や　**.describe()**　
といったオーソドックスな関数ですが、
「分割されたデータフレームごとに処理したい」ことがあり、
**for**文~~と**get_group**を組み合わせると~~**だけ**で、都合よく処理できることが分かりましたので紹介します。
#データの準備
```python:groupby_get_group.py
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**　としました。

```python:groupby_get_group.py
gp = df_iris.groupby('target_names')
```
#分割されたオブジェクトの属性を調べてみる
```python
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文を使う
```python
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**）として収納されているようです。
ここで、タプルの中身を確認するために、以下を打ち込んでみると、

```python
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)」をプロットしたもの。

```python:groupby_get_group.py
#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()
```
![Figure setosa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328121/27ac9a7a-77e6-906f-5e94-9530a3a0d5b7.png)
![Figure Versicolor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328121/4e67c52a-6520-22e3-6074-e5aeca2335c3.png)
![Figure Virginica.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328121/7b9a4bda-e8b4-7403-70b3-be8169fb4b06.png)

以上です。
#おわりに
初めてQiitaに投稿してみます。
Qiitaには助けられることばかりでしたので、誰かの助けになればと思います。
#参考文献
Pythonデータ分析/機械学習のための基本コーディング! pandasライブラリ活用入門 (impress top gear) (日本語) 単行本（ソフトカバー）
**ISBN-10: 4295005657
ISBN-13: 978-4295005650**