はじめに
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 を適用すると"が参考になりました。 ↩