当ページのリンクには広告が含まれています。
目次
この記事の対象者
・ データサイエンティストを目指している人
・ Pythonのgroupby, agg, reset_index, min, maxメソッドの使い方を知りたい人
本記事で学ぶpythonのメソッドと関数
本記事では以下のメソッドを学びます。
- groupbyメソッド: データフレームの特定の列のユニークな値でグルーピングするメソッド。
- aggメソッド: aggはgroupbyでグルーピングされた列に対して統計量を取るためのメソッドです。
- reset_indexメソッド: reset_indexメソッドは、インデックスが存在しないデータフレームにインデックスを付与するメソッドです。
- min関数: 最小の要素を抽出する関数。
- max関数: 最大の要素を抽出する関数。
では早速データサイエンス100本ノックの問題を題材に、これらの使い方を学んでいきたいと思います。
データサイエンス100本ノックの始め方は、以下の記事を参考にしていただければと思います。
>>データサイエンス100本ノックの始め方
第23問目: groupby()
メソッド
P-023: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)と売上数量(quantity)を合計せよ。
まずはdf_receipt
というデータフレーム全体を確認します。
出力1 2 3 4 5 6 7 8 9 10 11 12 13 14
| sales_ymd sales_epoch store_cd receipt_no receipt_sub_no customer_id product_cd quantity amount 0 20181103 1541203200 S14006 112 1 CS006214000001 P070305012 1 158 1 20181118 1542499200 S13008 1132 2 CS008415000097 P070701017 1 81 2 20170712 1499817600 S14028 1102 1 CS028414000014 P060101005 1 170 3 20190205 1549324800 S14042 1132 1 ZZ000000000000 P050301001 1 25 4 20180821 1534809600 S14025 1102 2 CS025415000050 P060102007 1 90 ... ... ... ... ... ... ... ... ... ... 104676 20180221 1519171200 S13043 1132 2 ZZ000000000000 P050101001 1 40 104677 20190911 1568160000 S14047 1132 2 ZZ000000000000 P071006005 1 218 104678 20170311 1489190400 S14040 1122 1 CS040513000195 P050405003 1 168 104679 20170331 1490918400 S13002 1142 1 CS002513000049 P060303001 1 148 104680 20190423 1555977600 S13016 1102 2 ZZ000000000000 P050601001 1 138 104681 rows × 9 columns
|
まずは、store_cd
において、ユニークな値でグルーピングをしてみます。
グルーピングをしたあと、ちゃんとグルーピングが出来ているかの確認をするために、グループ数がどの程度あるかを確認します。一旦以下のコードで、グループの数を確認します。
1
| len(df_receipt['store_cd'].unique())
|
unique()
メソッドを忘れてしまった方は、以下の記事で復習をしましょう。
>>uniqueメソッドの使い方
52
という数値が出力されました。
すなわちグループの数は52つであることが分かりました。
グループの数が確認できたので、早速groupby()
メソッドを使ってstore_cd
カラムのデータをグループ分けしたいと思います。
グループ分けしたいカラムは、store_cd
なので、groupby()
引数にstore_cd
を指定してグルーピングしていきます。
最後にlen(df_group)
のコードで、グルーピングの数を確認します。
1 2
| df_group = df_receipt.groupby('store_cd') len(df_group)
|
52
という出力が得られました。先程unique
メソッドで調べたグループの数と一致しているので、グルーピングがうまく出来ていることが確認できました。
続いて、各グループごとに売上金額(amount)と売上数量(quantity)の合計を算出していきます。
各グループごとに統計量を取る方法は、agg()
というメソッドを使うことで実現できます。
1 2
| df_group_sum = df_group.agg({'amount': 'sum', 'quantity': 'sum'}) df_group_sum
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| amount quantity store_cd S12007 638761 2099 S12013 787513 2425 S12014 725167 2358 S12029 794741 2555 S12030 684402 2403 S13001 811936 2347 S13002 727821 2340 S13003 764294 2197 S13004 779373 2390 S13005 629876 2004 S13008 809288 2491 S13009 808870 2486 S13015 780873 2248 S13016 793773 2432 S13017 748221 2376 S13018 790535 2562 S13019 827833 2541 S13020 796383 2383 S13031 705968 2336 S13032 790501 2491 S13035 715869 2219 S13037 693087 2344 S13038 708884 2337 S13039 611888 1981 S13041 728266 2233 S13043 587895 1881 S13044 520764 1729 S13051 107452 354 S13052 100314 250 S14006 712839 2284 S14010 790361 2290 S14011 805724 2434 S14012 720600 2412 S14021 699511 2231 S14022 651328 2047 S14023 727630 2258 S14024 736323 2417 S14025 755581 2394 S14026 824537 2503 S14027 714550 2303 S14028 786145 2458 S14033 725318 2282 S14034 653681 2024 S14036 203694 635 S14040 701858 2233 S14042 534689 1935 S14045 458484 1398 S14046 412646 1354 S14047 338329 1041 S14048 234276 769 S14049 230808 788 S14050 167090 580
|
一応これでstore_cd毎にamountとquantityの合計を得ることは出来ましたが、もともと各レコードに付いていたindex番号が失われていることが分かります。
各レコードにindex番号を割り当てるためには、reset_index
というメソッドを使用すればOKです。
1
| df_group_sum.reset_index().head()
|
出力1 2 3 4 5 6 7
| store_cd amount quantity 0 S12007 638761 2099 1 S12013 787513 2425 2 S12014 725167 2358 3 S12029 794741 2555 4 S12030 684402 2403
|
これで完成です。
第24問目: max
関数
P-024: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上日(sales_ymd)を求め、10件表示せよ。
最も新しい売上日は、max
関数を用いることで抽出できます。「最も新しい売上日」=「yyyymmdd」の値が最も大きい値と読み替えることができるわけですね。
1 2
| df_max = df_receipt.groupby('customer_id').sales_ymd.max() df_max
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13
| customer_id CS001113000004 20190308 CS001114000005 20190731 CS001115000010 20190405 CS001205000004 20190625 CS001205000006 20190224 ... CS051513000004 20190719 CS051515000002 20191025 CS052212000002 20191017 CS052514000001 20190822 ZZ000000000000 20191031 Name: sales_ymd, Length: 8307, dtype: int64
|
こちらもindexが失われているのでreset_index()
メソッドでindex番号を付与していきたいと思います。
1
| df_max.reset_index().head(10)
|
出力1 2 3 4 5 6 7 8 9 10 11
| customer_id sales_ymd 0 CS001113000004 20190308 1 CS001114000005 20190731 2 CS001115000010 20190405 3 CS001205000004 20190625 4 CS001205000006 20190224 5 CS001211000025 20190322 6 CS001212000027 20170127 7 CS001212000031 20180906 8 CS001212000046 20170811 9 CS001212000070 20191018
|
これで完成です。
第25問目: min
関数
P-025: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も古い売上日(sales_ymd)を求め、10件表示せよ。
この問題に関しては、第24問目で用いたmax
関数を、min
に変更すればOKです。
1 2
| df_min = df_receipt.groupby('customer_id').sales_ymd.min() df_min
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13
| customer_id CS001113000004 20190308 CS001114000005 20180503 CS001115000010 20171228 CS001205000004 20170914 CS001205000006 20180207 ... CS051513000004 20190719 CS051515000002 20191025 CS052212000002 20191017 CS052514000001 20190822 ZZ000000000000 20170101 Name: sales_ymd, Length: 8307, dtype: int64
|
indexが失われているのでreset_index()
メソッドでindex番号を付与していきたいと思います。
1
| df_min.reset_index().head(10)
|
出力1 2 3 4 5 6 7 8 9 10 11
| customer_id sales_ymd 0 CS001113000004 20190308 1 CS001114000005 20180503 2 CS001115000010 20171228 3 CS001205000004 20170914 4 CS001205000006 20180207 5 CS001211000025 20190322 6 CS001212000027 20170127 7 CS001212000031 20180906 8 CS001212000046 20170811 9 CS001212000070 20191018
|
第26問目: 総合問題
P-026: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上日(sales_ymd)と古い売上日を求め、両者が異なるデータを10件表示せよ。
まずは、問23のコードである、f_group_sum = df_group.agg({'amount': 'sum', 'quantity': 'sum'})
を真似てコードを書いてみましょう。
1
| df_receipt.groupby('customer_id').agg({'sales_ymd':'max', 'sales_ymd':'min'}).reset_index()
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13
| customer_id sales_ymd 0 CS001113000004 20190308 1 CS001114000005 20180503 2 CS001115000010 20171228 3 CS001205000004 20170914 4 CS001205000006 20180207 ... ... ... 8302 CS051513000004 20190719 8303 CS051515000002 20191025 8304 CS052212000002 20191017 8305 CS052514000001 20190822 8306 ZZ000000000000 20170101 8307 rows × 2 columns
|
max
列とmin
列の2つが欲しかったのですが、min
しか作成されませんでした。sales_ymd
において、max
列とmin
列の2つを設けたい場合は、以下のようにすればOKです。
1 2
| df_salesymd_maxmin = df_receipt.groupby('customer_id').agg({'sales_ymd':['max', 'min']}).reset_index() df_salesymd_maxmin
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13 14
| customer_id sales_ymd max min 0 CS001113000004 20190308 20190308 1 CS001114000005 20190731 20180503 2 CS001115000010 20190405 20171228 3 CS001205000004 20190625 20170914 4 CS001205000006 20190224 20180207 ... ... ... ... 8302 CS051513000004 20190719 20190719 8303 CS051515000002 20191025 20191025 8304 CS052212000002 20191017 20191017 8305 CS052514000001 20190822 20190822 8306 ZZ000000000000 20191031 20170101 8307 rows × 3 columns
|
出力をみると、sales_ymd
の配下に、max
とmin
というカラム名が設定されています。
これを、sales_ymd_max
とsales_ymd_min
という2つのカラム名に変更したいと思います。
まずは、このデータフレームからカラム名だけ抽出してみます。カラム名の抽出は、データフレームに対して.columns
を指定すればよかったですね。
第19問目でも使っているので、忘れてしまった方は復習しましょう。
>>第19問目の回答を確認する
1
| df_salesymd_maxmin.columns
|
出力1 2 3 4
| MultiIndex([('customer_id', ''), ( 'sales_ymd', 'max'), ( 'sales_ymd', 'min')], )
|
マルチインデックスになっていることが分かります。
マルチインデックスを解除し、改めて各カラムに一意のカラム名を割り当てるために、for文とjoin
関数を用いて、各インデックスを連結させたいと思います。
まずはdf_salesymd_maxmin.columns
をfor文で回して出力されるとどうなるのかを確認します。
1 2
| for pair in df_salesymd_maxmin.columns: print(pair)
|
出力1 2 3
| ('customer_id', '') ('sales_ymd', 'max') ('sales_ymd', 'min')
|
ここで出力された各タプルを解除し、文字列を連結させて新たな配列に組み込んでいきたいと思います。
その前にjoin関数について説明します。
join関数の引数には、結合させたい文字列をそのまま設定するわけではなく、結合させたい文字列が格納されたリストまたはタプルを指定します。
今回でいうと、for文で1つ1つ抽出される各タプルをjoin関数の引数として指定して連結させていきます。
連結する文字列は_
を指定します。
1 2 3 4 5
| df_tmp = [] for pair in df_salesymd_maxmin.columns: column = "_".join(pair) df_tmp.append(column) df_tmp
|
出力1
| ['customer_id_', 'sales_ymd_max', 'sales_ymd_min']
|
作成できた新たなカラム名のリストであるdf_tmp
を、df_salesymd_maxmin.columns
に代入したいと思います。
1 2
| df_salesymd_maxmin.columns = df_tmp df_salesymd_maxmin
|
出力1 2 3 4 5 6 7 8 9 10 11 12 13
| customer_id_ sales_ymd_max sales_ymd_min 0 CS001113000004 20190308 20190308 1 CS001114000005 20190731 20180503 2 CS001115000010 20190405 20171228 3 CS001205000004 20190625 20170914 4 CS001205000006 20190224 20180207 ... ... ... ... 8302 CS051513000004 20190719 20190719 8303 CS051515000002 20191025 20191025 8304 CS052212000002 20191017 20191017 8305 CS052514000001 20190822 20190822 8306 ZZ000000000000 20191031 20170101 8307 rows × 3 columns
|
カラム名の修正が完了しました。
最後に、問題文に従って、最も新しい売上日であるsales_ymd_max
と古い売上日であるsales_ymd_min
が異なるデータを10件表示します。これは以前取り組んだ.query
を使用します。
>>queryメソッドの使い方を復習する
1
| df_salesymd_maxmin.query('sales_ymd_max != sales_ymd_min').head(10)
|
出力1 2 3 4 5 6 7 8 9 10 11
| customer_id_ sales_ymd_max sales_ymd_min 1 CS001114000005 20190731 20180503 2 CS001115000010 20190405 20171228 3 CS001205000004 20190625 20170914 4 CS001205000006 20190224 20180207 13 CS001214000009 20190902 20170306 14 CS001214000017 20191006 20180828 16 CS001214000048 20190929 20171109 17 CS001214000052 20190617 20180208 20 CS001215000005 20181021 20170206 21 CS001215000040 20171022 20170214
|
これで完成です。
リンク
まとめ: pythonのgroupby, agg, reset_index, min関数, max関数の使い方を学びました
本記事は、「【Python】手を動かしながらグループ分け、最大最小値の抽出方法を学ぶ | データサイエンス100本ノック【問23〜問26 回答】」というテーマでまとめました。
最後の問題は、今まで取り組んできた内容の総復習の位置づけにもなって、良い問題だったかなとおもいます。
少しずつデータの扱いを学び、スキルを伸ばしていきましょう。
>> 続きはこちら