【解説】縦横変換の方法を学ぶ | データサイエンス100本ノック【問43〜問44 回答】

【解説】縦横変換の方法を学ぶ | データサイエンス100本ノック【問43〜問44 回答】

当ページのリンクには広告が含まれています。





目次

この記事の対象者



・ データサイエンティストを目指している人

・ 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_receiptdf_customerがどのようなデータフレームなのかを把握しましょう。

まずはdf_receiptから確認します。

1
df_receipt.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
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
df_customer.head(5)


出力
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_receiptdf_customercustomer_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_summaryera列を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メソッドでmale00, female01, unknown99に変更します。


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_cdamountに変更します。


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のデータサイエンスコースでの学習がおすすめです。

コメント