当ページのリンクには広告が含まれています。
目次
この記事の対象者
・ データサイエンティストを目指している人
・ Pythonで縦横変換(pivot_table
関数とstack
メソッド)の方法を学びたい人
以降はデータサイエンス100本ノックの問題を題材に、縦横変換の方法について学んでいきます。
データサイエンス100本ノックの始め方は、以下の記事を参考にしていただければと思います。
>>データサイエンス100本ノックの始め方を確認する
第43問目: 縦並びのデータを横並びに変換
P-043: レシート明細データフレーム(df_receipt)と顧客データフレーム(df_customer)を結合し、性別(gender)と年代(ageから計算)ごとに売上金額(amount)を合計した売上サマリデータフレーム(df_sales_summary)を作成せよ。性別は0が男性、1が女性、9が不明を表すものとする。
ただし、項目構成は年代、女性の売上金額、男性の売上金額、性別不明の売上金額の4項目とすること(縦に年代、横に性別のクロス集計)。また、年代は10歳ごとの階級とすること。
問題文で触れられているdf_receipt
とdf_customer
がどのようなデータフレームなのかを把握しましょう。
まずはdf_receipt
から確認します。
出力1 2 3 4 5 6 7
| 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
|
続いて、df_customer
です。
出力1 2 3 4 5 6
| customer_id customer_name gender_cd gender birth_day age postal_cd address application_store_cd application_date status_cd 0 CS021313000114 大野 あや子 1 女性 1981-04-29 37 259-1113 神奈川県伊勢原市粟窪********** S14021 20150905 0-00000000-0 1 CS037613000071 六角 雅彦 9 不明 1952-04-01 66 136-0076 東京都江東区南砂********** S13037 20150414 0-00000000-0 2 CS031415000172 宇多田 貴美子 1 女性 1976-10-04 42 151-0053 東京都渋谷区代々木********** S13031 20150529 D-20100325-C 3 CS028811000001 堀井 かおり 1 女性 1933-03-27 86 245-0016 神奈川県横浜市泉区和泉町********** S14028 20160115 0-00000000-0 4 CS001215000145 田崎 美紀 1 女性 1995-03-29 24 144-0055 東京都大田区仲六郷********** S13001 20170605 6-20090929-2
|
データフレームの結合で着目するのは「共通するカラムの確認」だったね!
両者のデータフレームを見る限り、共通するカラムはcustomer_id
であることがわかります。
これを踏まえ、df_receipt
とdf_customer
をcustomer_id
列を基準に内部結合したいと思います。
>>【問36〜問37 回答】内部結合について復習する
内部結合したデータフレームはdf_tmp
とします。
df_receiptとdf_customerをcustomer_id列を基準に内部結合1 2
| df_tmp = pd.merge(df_receipt, df_customer, how ='inner', on='customer_id') df_tmp.head(5)
|
出力1 2 3 4 5 6 7
| sales_ymd sales_epoch store_cd receipt_no receipt_sub_no customer_id product_cd quantity amount customer_name gender_cd gender birth_day age postal_cd address application_store_cd application_date status_cd 0 20181103 1541203200 S14006 112 1 CS006214000001 P070305012 1 158 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 1 20170509 1494288000 S14006 112 1 CS006214000001 P071401004 1 1100 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 2 20170608 1496880000 S14006 112 1 CS006214000001 P060104021 1 120 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 3 20170608 1496880000 S14006 112 2 CS006214000001 P080403001 1 175 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 4 20181028 1540684800 S14006 112 2 CS006214000001 P050102004 1 188 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F
|
年代毎に売上金額を合計した値を表示する必要があるので、df_tmp
に新しい列として年代era
列を作成します。
年代は、age
列の値を1/10倍し、floor
関数で小数点を切り捨てたあとに10倍すれば、年代を算出することが出来ます。例えば22という値を、2.2にし、小数点を切り捨てて2.0にした後10倍すれば20になりますよね?つまり、22歳は20代であることを算出することができます。
この処理に関してはlambda式を用いて実施します。lambda式を忘れてしまった方は、以下のページで復習しましょう。
>>lambda式の復習をする
era列を新設し年代を算出1 2
| df_tmp['era'] = df_tmp['age'].apply(lambda x: math.floor(x / 10) * 10) df_tmp
|
出力1 2 3 4 5 6 7 8 9 10 11 12
| sales_ymd sales_epoch store_cd receipt_no receipt_sub_no customer_id product_cd quantity amount customer_name gender_cd gender birth_day age postal_cd address application_store_cd application_date status_cd era 0 20181103 1541203200 S14006 112 1 CS006214000001 P070305012 1 158 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 20 1 20170509 1494288000 S14006 112 1 CS006214000001 P071401004 1 1100 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 20 2 20170608 1496880000 S14006 112 1 CS006214000001 P060104021 1 120 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 20 3 20170608 1496880000 S14006 112 2 CS006214000001 P080403001 1 175 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 20 4 20181028 1540684800 S14006 112 2 CS006214000001 P050102004 1 188 志水 佳乃 1 女性 1996-12-08 22 224-0057 神奈川県横浜市都筑区川和町********** S14006 20150201 E-20100908-F 20 ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 65677 20171202 1512172800 S13004 1152 1 CS004613000146 P071302002 1 308 玉木 恵麻 1 女性 1953-02-01 66 165-0032 東京都中野区鷺宮********** S13004 20160813 4-20081202-1 60 65678 20180421 1524268800 S13002 1142 2 CS002314000037 P070703051 1 228 島崎 愛 1 女性 1979-01-07 40 185-0012 東京都国分寺市本町********** S13002 20151202 2-20090421-1 40 65679 20180421 1524268800 S13002 1142 1 CS002314000037 P060702014 1 108 島崎 愛 1 女性 1979-01-07 40 185-0012 東京都国分寺市本町********** S13002 20151202 2-20090421-1 40 65680 20190416 1555372800 S14040 1182 2 CS040311000022 P071401017 1 2200 五十嵐 南朋 1 女性 1986-08-11 32 194-0014 東京都町田市高ヶ坂********** S14040 20150419 B-20100416-4 30 65681 20190416 1555372800 S14040 1182 1 CS040311000022 P071101020 1 980 五十嵐 南朋 1 女性 1986-08-11 32 194-0014 東京都町田市高ヶ坂********** S14040 20150419 B-20100416-4 30
|
このデータフレームから、最終的にどのようなデータフレームを出力したいかを改めて考えましょう。
問題文より、この問題で出力したいデータフレームは、カラムとしては「年代」と「性別」になります。
すなわち、era
, male
, female
, unknown
という4つのカラムが必要になります。
そして、era
カラムには、10代、20代、30代、、、と年代が連なり、各年代のレコードには、性別ごとの売上金額がまとめられているテーブルになるかと思います。例えば以下のような形です。
|
era |
male |
female |
unknown |
0 |
10 |
1000 |
2000 |
4000 |
1 |
20 |
2000 |
3000 |
2000 |
2 |
30 |
3000 |
2000 |
5000 |
今まで学んできた手法を駆使すれば、答えとなるデータフレームを作れそう!
確かに、これまで学んできた複数の手法を活用してもOKですが、pivot_table
関数を使えば、たった1行のpythonコードで上記のデータフレームを作成することが出来ます。
pivot_table関数の使い方
pd.pivot_table(対象とするデータフレーム, index='インデックスとするカラム(縦方向のデータ)', columns='カラムとして設定する(横方向のデータ)', values='計算対象のカラム名', aggfunc='計算方法)
という使い方になります。
少しわかりにくかと思うので、実際にこの問題を解きながら、pivot_table
関数の使い方を学んでいきましょう。
1 2
| df_sales_summary = pd.pivot_table(df_tmp, index='era', columns='gender_cd', values='amount', aggfunc='sum').reset_index() df_sales_summary
|
出力1 2 3 4 5 6 7 8 9 10
| gender_cd era 0 1 9 0 10 1591.0 149836.0 4317.0 1 20 72940.0 1363724.0 44328.0 2 30 177322.0 693047.0 50441.0 3 40 19355.0 9320791.0 483512.0 4 50 54320.0 6685192.0 342923.0 5 60 272469.0 987741.0 71418.0 6 70 13435.0 29764.0 2427.0 7 80 46360.0 262923.0 5111.0 8 90 NaN 6260.0 NaN
|
最後にカラム名を0, 1, 9ではなくmale
, female
, unknown
に変換します。
1 2
| df_sales_summary.columns = ['era', 'male', 'female', 'unknown'] df_sales_summary
|
出力1 2 3 4 5 6 7 8 9 10
| era male female unknown 0 10 1591.0 149836.0 4317.0 1 20 72940.0 1363724.0 44328.0 2 30 177322.0 693047.0 50441.0 3 40 19355.0 9320791.0 483512.0 4 50 54320.0 6685192.0 342923.0 5 60 272469.0 987741.0 71418.0 6 70 13435.0 29764.0 2427.0 7 80 46360.0 262923.0 5111.0 8 90 NaN 6260.0 NaN
|
これで完了です。
リンク
第44問目: 横並びのデータを縦並びに変換
P-044: 前設問で作成した売上サマリデータフレーム(df_sales_summary)は性別の売上を横持ちさせたものであった。このデータフレームから性別を縦持ちさせ、年代、性別コード、売上金額の3項目に変換せよ。ただし、性別コードは男性を’00’、女性を’01’、不明を’99’とする。
まずは第43問目で作成したデータフレームであるdf_sales_summary
のera
列をset_index
メソッドを用いて、indexに変換します。
>>set_indexメソッドの使い方
1 2
| df_sales_summary = df_sales_summary.set_index('era') df_sales_summary
|
出力1 2 3 4 5 6 7 8 9 10 11
| male female unknown era 10 1591.0 149836.0 4317.0 20 72940.0 1363724.0 44328.0 30 177322.0 693047.0 50441.0 40 19355.0 9320791.0 483512.0 50 54320.0 6685192.0 342923.0 60 272469.0 987741.0 71418.0 70 13435.0 29764.0 2427.0 80 46360.0 262923.0 5111.0 90 NaN 6260.0 NaN
|
次にstack
メソッドを使って、male
, female
, unknown
カラムの各値を、各年代ごとに縦持ちにさせます。
>>stackメソッドの使い方
具体的には以下のような形式に変換します。
era |
|
|
|
|
10 |
male |
1000 |
|
|
|
female |
2000 |
|
|
|
unknown |
3000 |
|
|
20 |
male |
123 |
|
|
|
female |
456 |
|
|
|
unknown |
789 |
|
以下のコードを実行します。
1 2
| df_sales_summary = df_sales_summary.stack().reset_index() df_sales_summary
|
出力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
| era level_1 0 0 10 male 1591.0 1 10 female 149836.0 2 10 unknown 4317.0 3 20 male 72940.0 4 20 female 1363724.0 5 20 unknown 44328.0 6 30 male 177322.0 7 30 female 693047.0 8 30 unknown 50441.0 9 40 male 19355.0 10 40 female 9320791.0 11 40 unknown 483512.0 12 50 male 54320.0 13 50 female 6685192.0 14 50 unknown 342923.0 15 60 male 272469.0 16 60 female 987741.0 17 60 unknown 71418.0 18 70 male 13435.0 19 70 female 29764.0 20 70 unknown 2427.0 21 80 male 46360.0 22 80 female 262923.0 23 80 unknown 5111.0 24 90 female 6260.0
|
replace
メソッドでmale
を00
, female
を01
, unknown
を99
に変更します。
1 2
| df_sales_summary = df_sales_summary.replace({'female': '01', 'male': '00', 'unknown': '99'}) df_sales_summary
|
出力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
| era level_1 0 0 10 00 1591.0 1 10 01 149836.0 2 10 99 4317.0 3 20 00 72940.0 4 20 01 1363724.0 5 20 99 44328.0 6 30 00 177322.0 7 30 01 693047.0 8 30 99 50441.0 9 40 00 19355.0 10 40 01 9320791.0 11 40 99 483512.0 12 50 00 54320.0 13 50 01 6685192.0 14 50 99 342923.0 15 60 00 272469.0 16 60 01 987741.0 17 60 99 71418.0 18 70 00 13435.0 19 70 01 29764.0 20 70 99 2427.0 21 80 00 46360.0 22 80 01 262923.0 23 80 99 5111.0 24 90 01 6260.0
|
最後にカラム名を適切にするためにrename
メソッドを用いてgender_cd
とamount
に変更します。
1 2
| df_sales_summary = df_sales_summary.rename(columns={'level_1':'gender_cd', 0: 'amount'}) df_sales_summary
|
出力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
| era gender_cd amount 0 10 00 1591.0 1 10 01 149836.0 2 10 99 4317.0 3 20 00 72940.0 4 20 01 1363724.0 5 20 99 44328.0 6 30 00 177322.0 7 30 01 693047.0 8 30 99 50441.0 9 40 00 19355.0 10 40 01 9320791.0 11 40 99 483512.0 12 50 00 54320.0 13 50 01 6685192.0 14 50 99 342923.0 15 60 00 272469.0 16 60 01 987741.0 17 60 99 71418.0 18 70 00 13435.0 19 70 01 29764.0 20 70 99 2427.0 21 80 00 46360.0 22 80 01 262923.0 23 80 99 5111.0 24 90 01 6260.0
|
これで完成です。
リンク
まとめ: 縦から横、横から縦にデータの並びを変換する方法を学びました。
本記事は、「【Python】縦横変換の方法を学ぶ | データサイエンス100本ノック【問43〜問44 回答】」というテーマでまとめました。
本記事のポイント
・縦から横: pivot_table
関数
・横から縦: stuck
メソッド
なお、データサイエンティストに必要な知識は、TechAcademyのデータサイエンスコースでの学習がおすすめです。