Python
pandas
Python3

知りたいのです、pandasで、ある列の最大値の行でグループを代表させる方法を

はじめに

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_maxconfig_idのグループごとのsalesdateの最大値を格納したSeries。
salesdate_maxの各要素にはfor文でアクセス可能。

df3[(df3['config_id'] == i) & (df3['salesdate'] == salesdate_max[i])では
config_idiでかつ、config_idiのグループのsalesdatesalesdateが等しい行がdf3から抽出される。
抽出結果のうちの1番目にアクセスし、それをリストに集める。
i3のときは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_id3df3の行で、かつ、
config_id3のレコードの中の最大のsalesdatesalesdateが等しい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は以下のような動きと理解しました。2config_id=3の例)

pandas_max動作_20180127.png

動作環境

  • Python 3.5.1
  • pip install pandas jupyterを実行し、pandas==0.22.0jupyter==1.0.0をインストール

脚注