はじめに
pandasを触り始めました。
groupごとにmaxを求める上で気になったことがあったので手を動かして調べてみました。
(2018/01/28)もくもく会で質問した内容をもとに暫定解をアップデートしました。
今後はatsushi_bgさんからいただいたコメントをもとにアップデートします。
やりたいこと
データを列Aでグループ化し、各グループを列Bの最大値の行で代表させたい。
例えば、select_max_sample-3.csvとして保存された以下のようなデータ1を
config_idでグループ化し、各グループはsalesdateの最大値(最新値)の行で代表させたい。
| id | no | config_id | salesdate |
|---|---|---|---|
| 1 | 1 | 1 | 2011-11-12 |
| 2 | 3 | 3 | 2011-12-03 |
| 3 | 2 | 1 | 2012-02-10 |
| 4 | 1 | 2 | 2012-01-04 |
| 5 | 2 | 3 | 2012-06-03 |
| 6 | 1 | 3 | 2011-08-04 |
以下の結果がほしいわけです。
| id | no | config_id | salesdate |
|---|---|---|---|
| 3 | 2 | 1 | 2012-02-10 |
| 4 | 1 | 2 | 2012-01-04 |
| 5 | 2 | 3 | 2012-06-03 |
暫定解(2018/01/28更新)
import pandas as pd
df3 = pd.read_csv('select_max_sample-3.csv', sep=',', header=0, index_col=None)
salesdate_max = df3.groupby('config_id').max()['salesdate']
pd.DataFrame([df3[(df3['config_id'] == i) & (df3['salesdate'] == salesdate_max[i])].values[0] for i in range(1, len(salesdate_max)+1)], columns=df3.columns)
暫定解の動き
salesdate_maxはconfig_idのグループごとのsalesdateの最大値を格納したSeries。
salesdate_maxの各要素にはfor文でアクセス可能。
df3[(df3['config_id'] == i) & (df3['salesdate'] == salesdate_max[i])では
config_idがiでかつ、config_idがiのグループのsalesdateにsalesdateが等しい行がdf3から抽出される。
抽出結果のうちの1番目にアクセスし、それをリストに集める。
iが3のときはarray([5, 2, 3, '2012-06-03'], dtype=object)がリストに入る。
リストをもとにDataFrameを作成する。作成時に、df3と同じカラム名を設定する。
私の解答
import pandas as pd
df3 = pd.read_csv('select_max_sample-3.csv', sep=',', header=0, index_col=None)
df3_configid_group = df3.groupby('config_id')
max_by_group = []
for i in range(1, 4):
max_by_group.append(df3[(df3['config_id']==i) & (df3['salesdate']==df3_configid_group['salesdate'].max().loc[i])])
pd.concat(max_by_group)
抽出条件が複雑になってしまっているんですよね。。
config_idの最大値に合わせてfor文のrangeを書き直す必要があるのでいけていません。
(抽出条件についてはもっといい書き方が見つかったら更新します。改善案をコメントいただけると大変嬉しいです)
私の解答の動き
df3[(df3['config_id']==i) & (df3['salesdate']==df3_configid_group['salesdate'].max().loc[i])]について:
例えば、i=3で考えると、config_idが3のdf3の行で、かつ、
config_idが3のレコードの中の最大のsalesdateにsalesdateが等しいdf3の行を取り出しています。
df3_configid_group['salesdate'].max()はconfig_idごとの最大のsalesdateを返します。(*)
config_id
1 2012-02-10
2 2012-01-04
3 2012-06-03
Name: salesdate, dtype: object
(*)からconfig_idごとにsalesdateの最大値を取り出すため、config_idを逐次locで指定しました。
こうしてconfig_idごとに取得したsalesdateが最大となるdf3の行をconcatで一つにまとめています。
単にmaxを使わなかった理由
maxはidの最大値、noの最大値、salesdateの最大値からなる行を新たに作って返すため。
df3_max_salesdate1 = df3_configid_group.max()
df3_max_salesdate1.head()
これを実行すると次のような出力となります。(太字箇所に注目)
| id | no | config_id | salesdate |
|---|---|---|---|
| 3 | 2 | 1 | 2012-02-10 |
| 4 | 1 | 2 | 2012-01-04 |
| 6 | 3 | 3 | 2012-06-03 |
maxは以下のような動きと理解しました。2(config_id=3の例)
動作環境
- Python 3.5.1
-
pip install pandas jupyterを実行し、pandas==0.22.0、jupyter==1.0.0をインストール
脚注
-
このデータはorder byでハマったこと -order by、group byの処理順序-を参考にしました。 ↩
-
「groupby の初歩と python(DataFrame)によるサンプル」というサイトの "max を適用すると"が参考になりました。 ↩