データサイエンス100本ノック(構造化データ加工編)- Python Part 2 (Q1 to Q20)の解説です。
参照(Reference) : 「データサイエンティスト協会スキル定義委員」の「データサイエンス100本ノック(構造化データ加工編)」
The Data Scientist Society Github :
Data Science 100 Knocks (Structured Data Processing) URL :
はじめに
- 初めに以下のセルを実行してください
- 必要なライブラリのインポートとデータベース(PostgreSQL)からのデータ読み込みを行います
- pandas等、利用が想定されるライブラリは以下セルでインポートしています
- その他利用したいライブラリがあれば適宜インストールしてください(”!pip install ライブラリ名”でインストールも可能)
- 処理は複数回に分けても構いません
- 名前、住所等はダミーデータであり、実在するものではありません
import os
import pandas as pd
import numpy as np
from datetime import datetime, date
from dateutil.relativedelta import relativedelta
import math
import psycopg2
from sqlalchemy import create_engine
from sklearn import preprocessing
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
from sklearn.model_selection import TimeSeriesSplit
from imblearn.under_sampling import RandomUnderSampler
if 'PG_PORT' in os.environ:
pgconfig = {
'host': 'db',
'port': os.environ['PG_PORT'],
'database': os.environ['PG_DATABASE'],
'user': os.environ['PG_USER'],
'password': os.environ['PG_PASSWORD'],
}
# pd.read_sql用のコネクタ
conn = psycopg2.connect(**pgconfig)
df_customer = pd.read_sql(sql='select * from customer', con=conn)
df_category = pd.read_sql(sql='select * from category', con=conn)
df_product = pd.read_sql(sql='select * from product', con=conn)
df_receipt = pd.read_sql(sql='select * from receipt', con=conn)
df_store = pd.read_sql(sql='select * from store', con=conn)
df_geocode = pd.read_sql(sql='select * from geocode', con=conn)
else:
if not os.path.exists('../data/'):
!git clone https://github.com/The-Japan-DataScientist-Society/100knocks-preprocess
os.chdir('100knocks-preprocess/docker/work/answer')
dtype = {
'customer_id': str,
'gender_cd': str,
'postal_cd': str,
'application_store_cd': str,
'status_cd': str,
'category_major_cd': str,
'category_medium_cd': str,
'category_small_cd': str,
'product_cd': str,
'store_cd': str,
'prefecture_cd': str,
'tel_no': str,
'postal_cd': str,
'street': str
}
df_customer = pd.read_csv("../data/customer.csv", dtype=dtype)
df_category = pd.read_csv("../data/category.csv", dtype=dtype)
df_product = pd.read_csv("../data/product.csv", dtype=dtype)
df_receipt = pd.read_csv("../data/receipt.csv", dtype=dtype)
df_store = pd.read_csv("../data/store.csv", dtype=dtype)
df_geocode = pd.read_csv("../data/geocode.csv", dtype=dtype)
演習問題
P-021: レシート明細データ(df_receipt)に対し、件数をカウントせよ。
len(df_receipt)
104681
解説:
コード len(df_receipt) は、DataFrame df_receipt の長さを返します。
以下、各構成要素について説明します。
lens(): : Pythonの組み込み関数で、リストや文字列などのオブジェクトの長さを返します。
df_receipt : df_receipt:pandasでDataFrameオブジェクトを表す変数です。df_receipt という名前は、任意の有効な変数名である可能性があります。
len() と df_receipt の組み合わせです。len()関数の引数としてDataFrameのdf_receiptを渡すことで、df_receiptの行数を返すようにPythonに依頼しています。
まとめると、このコードは単にDataFrameの行数を返しているに過ぎない。
P-022: レシート明細データ(df_receipt)の顧客ID(customer_id)に対し、ユニーク件数をカウントせよ。
len(df_receipt['customer_id'].unique())
8307
解説:
len(df_receipt['customer_id'].unique()) は、df_receipt DataFrame の customer_id 列に含まれるユニークな顧客 ID の数を返すコードです。
以下、各構成要素について説明します。
len() : Pythonの組み込み関数で、リストや文字列などのオブジェクトの長さを返します。
df_receipt : df_receipt:pandasでDataFrameオブジェクトを表す変数です。df_receipt という名前は、任意の有効な変数名である可能性があります。
['customer_id']: これは、df_receipt から customer_id というラベルを持つカラムを選択する DataFrame インデックス作成操作です。結果のオブジェクトは、pandas Seriesです。
.unique(): これはpandasのSeriesメソッドで、Series内のユニークな値をnumpy配列で返します。この場合、ユニークな顧客 ID の numpy 配列を返します。
len()、df_receipt['customer_id'].unique()、()を組み合わせています。len()関数の引数にユニークな顧客IDのnumpy配列を渡すことで、ユニークな顧客IDの数を返すようにPythonに求めています。
要約すると、このコードはDataFrame内のユニークな顧客IDの数を返していることになります。
P-023: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)と売上数量(quantity)を合計せよ。
# コード例1
df_receipt.groupby('store_cd').agg({'amount':'sum',
'quantity':'sum'}).reset_index()
store_cd | amount | quantity | |
---|---|---|---|
0 | S12007 | 638761 | 2099 |
1 | S12013 | 787513 | 2425 |
2 | S12014 | 725167 | 2358 |
3 | S12029 | 794741 | 2555 |
4 | S12030 | 684402 | 2403 |
5 | S13001 | 811936 | 2347 |
6 | S13002 | 727821 | 2340 |
7 | S13003 | 764294 | 2197 |
8 | S13004 | 779373 | 2390 |
9 | S13005 | 629876 | 2004 |
10 | S13008 | 809288 | 2491 |
11 | S13009 | 808870 | 2486 |
12 | S13015 | 780873 | 2248 |
13 | S13016 | 793773 | 2432 |
14 | S13017 | 748221 | 2376 |
15 | S13018 | 790535 | 2562 |
16 | S13019 | 827833 | 2541 |
17 | S13020 | 796383 | 2383 |
18 | S13031 | 705968 | 2336 |
19 | S13032 | 790501 | 2491 |
20 | S13035 | 715869 | 2219 |
21 | S13037 | 693087 | 2344 |
22 | S13038 | 708884 | 2337 |
23 | S13039 | 611888 | 1981 |
24 | S13041 | 728266 | 2233 |
25 | S13043 | 587895 | 1881 |
26 | S13044 | 520764 | 1729 |
27 | S13051 | 107452 | 354 |
28 | S13052 | 100314 | 250 |
29 | S14006 | 712839 | 2284 |
30 | S14010 | 790361 | 2290 |
31 | S14011 | 805724 | 2434 |
32 | S14012 | 720600 | 2412 |
33 | S14021 | 699511 | 2231 |
34 | S14022 | 651328 | 2047 |
35 | S14023 | 727630 | 2258 |
36 | S14024 | 736323 | 2417 |
37 | S14025 | 755581 | 2394 |
38 | S14026 | 824537 | 2503 |
39 | S14027 | 714550 | 2303 |
40 | S14028 | 786145 | 2458 |
41 | S14033 | 725318 | 2282 |
42 | S14034 | 653681 | 2024 |
43 | S14036 | 203694 | 635 |
44 | S14040 | 701858 | 2233 |
45 | S14042 | 534689 | 1935 |
46 | S14045 | 458484 | 1398 |
47 | S14046 | 412646 | 1354 |
48 | S14047 | 338329 | 1041 |
49 | S14048 | 234276 | 769 |
50 | S14049 | 230808 | 788 |
51 | S14050 | 167090 | 580 |
解説:
df_receipt.groupby('store_cd').agg({'amount':'sum', 'quantity':'sum'}).reset_index() というコードは、df_receipt DataFrame の行を store_cd 列でグループ化して、各グループ内の該当列に二つの集計関数(金額と数量に対する sum() )をかけています。最後に、結果のDataFrameのインデックスをリセットしています。
以下は、各構成要素の説明である。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、有効な変数名であれば何でも構いません。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.agg({'amount':'sum', 'quantity':'sum'}): これはpandasのGroupByオブジェクトのメソッドで、GroupByオブジェクトの各グループのamount列とquantity列にsum()集計関数を適用しています。結果は、各グループのこれらの列の合計を含む、amountとquantityの2つの列を持つDataFrameになります。
.reset_index(): これはpandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。
要約すると、このコードは、特定の列の値によってDataFrameの行をグループ化し、各グループについて他の2つの列の合計を計算し、各グループについてこれらの列の合計とデフォルトのインデックスを持つDataFrameを返しています。
# コード例2
df_receipt.groupby('store_cd')[['amount','quantity']].agg('sum').reset_index()
store_cd | amount | quantity | |
---|---|---|---|
0 | S12007 | 638761 | 2099 |
1 | S12013 | 787513 | 2425 |
2 | S12014 | 725167 | 2358 |
3 | S12029 | 794741 | 2555 |
4 | S12030 | 684402 | 2403 |
5 | S13001 | 811936 | 2347 |
6 | S13002 | 727821 | 2340 |
7 | S13003 | 764294 | 2197 |
8 | S13004 | 779373 | 2390 |
9 | S13005 | 629876 | 2004 |
10 | S13008 | 809288 | 2491 |
11 | S13009 | 808870 | 2486 |
12 | S13015 | 780873 | 2248 |
13 | S13016 | 793773 | 2432 |
14 | S13017 | 748221 | 2376 |
15 | S13018 | 790535 | 2562 |
16 | S13019 | 827833 | 2541 |
17 | S13020 | 796383 | 2383 |
18 | S13031 | 705968 | 2336 |
19 | S13032 | 790501 | 2491 |
20 | S13035 | 715869 | 2219 |
21 | S13037 | 693087 | 2344 |
22 | S13038 | 708884 | 2337 |
23 | S13039 | 611888 | 1981 |
24 | S13041 | 728266 | 2233 |
25 | S13043 | 587895 | 1881 |
26 | S13044 | 520764 | 1729 |
27 | S13051 | 107452 | 354 |
28 | S13052 | 100314 | 250 |
29 | S14006 | 712839 | 2284 |
30 | S14010 | 790361 | 2290 |
31 | S14011 | 805724 | 2434 |
32 | S14012 | 720600 | 2412 |
33 | S14021 | 699511 | 2231 |
34 | S14022 | 651328 | 2047 |
35 | S14023 | 727630 | 2258 |
36 | S14024 | 736323 | 2417 |
37 | S14025 | 755581 | 2394 |
38 | S14026 | 824537 | 2503 |
39 | S14027 | 714550 | 2303 |
40 | S14028 | 786145 | 2458 |
41 | S14033 | 725318 | 2282 |
42 | S14034 | 653681 | 2024 |
43 | S14036 | 203694 | 635 |
44 | S14040 | 701858 | 2233 |
45 | S14042 | 534689 | 1935 |
46 | S14045 | 458484 | 1398 |
47 | S14046 | 412646 | 1354 |
48 | S14047 | 338329 | 1041 |
49 | S14048 | 234276 | 769 |
50 | S14049 | 230808 | 788 |
51 | S14050 | 167090 | 580 |
解説:
このコードでは、df_receiptデータフレームに対してstore_cdカラムを使用してgroupby操作を行い、amountとquantityカラムを合計して集計しています。
結果のデータフレームは、store_cdカラムのユニークな値ごとに1行と、amountとquantityの2つのカラムを持ち、どちらも同じstore_cd値を持つすべての行のそれぞれの値の合計を含んでいます。
次に、.reset_index()メソッドが結果のデータフレームで呼び出され、store_cdカラムをインデックスから通常のカラムに変換します。
P-024: レシート明細データ(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上年月日(sales_ymd)を求め、10件表示せよ。
df_receipt.groupby('customer_id').agg({'sales_ymd': 'max'}).reset_index().head(10)
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 |
解説:
このコードでは、df_receiptというDataFrameに対して、customer_idカラムに基づいたgroupbyオペレーションを実行しています。次に、各グループのsales_ymdカラムの値をmax関数で集計し、各顧客の最新の購入日を求めています。
集計の結果、customer_idとsales_ymdの2つのカラムを持つ新しいDataFrameが生成されます。次に、reset_index関数を使用して、結果のDataFrameのインデックスをデフォルトにリセットし、head(10)関数を使用して、結果のDataFrameの最初の10行を表示させます。
つまり、このコードの出力は、各 customer_id の最大購入日(sales_ymd)を示す DataFrame であり、最初の 10 行のみが表示されます。
P-025: レシート明細データ(df_receipt)に対し、顧客ID(customer_id)ごとに最も古い売上年月日(sales_ymd)を求め、10件表示せよ。
# 024と同じ書き方もできるがあえて違う書き方で解答例を紹介
df_receipt.groupby('customer_id').sales_ymd.min().reset_index().head(10)
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 |
解説:
df_receipt.groupby('customer_id').agg({'sales_ymd': 'max'}).reset_index().head(10) は、df_receipt DataFrame の行を customer_id 列でグループ化し、各グループの sales_ymd 列に max() 集約関数を適用しています。最後に、結果のDataFrameのインデックスをリセットして、最初の10行を返します。
以下は、各コンポーネントの説明です。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、任意の有効な変数名とすることができます。
.groupby('customer_id'): これはpandasのDataFrameメソッドで、DataFrameの行をcustomer_idカラムの値でグループ化します。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.agg({'sales_ymd': 'max'}): これはpandasのGroupByオブジェクトのメソッドで、GroupByオブジェクトの各グループのsales_ymdカラムにmax()集計関数を適用しています。結果は、各グループのsales_ymdカラムの最大値を含むsales_ymdカラムを1つ持つDataFrameとなる。
.reset_index() : これはpandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットするものです。
.head(10): これは、DataFrameの最初の10行を返す、pandasのDataFrameメソッドです。
要約すると、このコードは、DataFrameの行を特定の列の値でグループ化し、各グループに対して別の列の最大値を求め、最大値とデフォルトインデックスを持つDataFrameを返し、そのDataFrameの最初の10行を返しています。このコードは、各顧客の直近の購入日を特定するのに便利です。
P-026: レシート明細データ(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上年月日(sales_ymd)と古い売上年月日を求め、両者が異なるデータを10件表示せよ。
df_tmp = df_receipt.groupby('customer_id'). \
agg({'sales_ymd':['max','min']}).reset_index()
# マルチインデックス(項目)の階層を"_"でつなぎながら1階層のインデックス(項目)にする
# df_tmp.columns = ['customer_id', 'sales_ymd_max', 'sales_ymd_min'] としても良い
df_tmp.columns = ["_".join(pair) for pair in df_tmp.columns]
df_tmp.query('sales_ymd_max != sales_ymd_min').head(10)
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 |
解説:
コード df_tmp = df_receipt.groupby('customer_id').agg({'sales_ymd':['max','min']}).reset_index() df_tmp.query('sales_ymd_max != sales_ymd_min'). head(10) は、df_receipt DataFrame の行を customer_id 列でグループ化し、各グループの sales_ymd 列に2つの集約関数(max() と min() )を適用して、各顧客の購入日の最大値と最小値を含む DataFrame を返します。そして、query()メソッドで最大購入日と最小購入日が異なる行のみを選択し、最初の10行を返します。
以下、各コンポーネントの説明です。
df_receipt: df_receipt:pandasでDataFrameオブジェクトを表す変数です。df_receiptという名前は、任意の有効な変数名とすることができます。
.groupby('customer_id'): これはpandasのDataFrameメソッドで、DataFrameの行をcustomer_idカラムの値でグループ化します。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.agg({'sales_ymd':['max','min']}): これはpandasのGroupByオブジェクトのメソッドで、GroupByオブジェクトの各グループのsales_ymdカラムに2つの集計関数(max()とmin())を適用しています。結果は、各グループのsales_ymdカラムの最大値と最小値をそれぞれ含むsales_ymd_maxとsales_ymd_minという2つのカラムを持つDataFrameになります。
.reset_index() : これは、結果のDataFrameのインデックスをデフォルトのインデックスにリセットするpandas DataFrameメソッドです。
.query('sales_ymd_max != sales_ymd_min'): これはpandasのDataFrameメソッドで、sales_ymd_max列の値がsales_ymd_min列の値と等しくないDataFrameの行のみを選択する。これは、query()メソッドの引数として渡される文字列の中のブーリアン式を使用して行われます。
.head(10)です。これはpandasのDataFrameメソッドで、DataFrameの最初の10行を返します。
要約すると、このコードは、DataFrameの行を特定の列の値でグループ化し、各グループに対して別の列の最大値と最小値を求め、最大値と最小値、およびデフォルトインデックスを含むDataFrameを返しています。次に、最大値と最小値が異なる行のみを選択し、結果のDataFrameの最初の10行を返します。このコードは、異なる日付に購入した顧客を特定するのに便利です。
P-027: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の平均を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').agg({'amount':'mean'}).reset_index(). \
sort_values('amount', ascending=False).head(5)
store_cd | amount | |
---|---|---|
28 | S13052 | 402.867470 |
12 | S13015 | 351.111960 |
7 | S13003 | 350.915519 |
30 | S14010 | 348.791262 |
5 | S13001 | 348.470386 |
解説:
df_receipt.groupby('store_cd').agg({'amount':'mean'}).reset_index().sort_values('amount', ascending=False).head(5) はdf_receipt DataFrame の行を store_cd 列でグルーピングし、各グループについて amount 列の平均値を計算して平均値および store_cd 列で DataFrame を返しているコード。次に、得られたDataFrameのインデックスをリセットし、金額の平均値で降順にソートする。最後に、結果のDataFrameの最初の5行を返します。
以下は、各コンポーネントの説明です。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、有効な変数名であれば何でも構いません。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.agg({'amount':'mean'}): これはpandasのGroupByオブジェクトのメソッドで、GroupByオブジェクトの各グループのamountカラムにmean()集計関数を適用しています。結果は、各グループの金額列の平均値を含むamountという1つの列を持つDataFrameとなります。
.reset_index(): これは、結果のDataFrameのインデックスをデフォルトのインデックスにリセットするpandasのDataFrameメソッドです。
.sort_values('amount', ascending=False): これは、DataFrameの行をamount列の値で降順にソートするpandasのDataFrameメソッドです。ascending=Falseパラメータは、ソート順を降順にすることを指定します。
.head(5): これは、DataFrameの最初の5行を返す、pandasのDataFrameメソッドです。
要約すると、このコードは、特定の列の値によってDataFrameの行をグループ化し、各グループに対して別の列の平均値を求め、平均値とグループ化列を含むDataFrameを返しています。そして、得られたDataFrameのインデックスをリセットし、平均値で降順にソートし、得られたDataFrameの最初の5行を返します。このコードは、平均売上が最も高い店舗を特定するのに便利です。
P-028: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の中央値を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').agg({'amount':'median'}).reset_index(). \
sort_values('amount', ascending=False).head(5)
store_cd | amount | |
---|---|---|
28 | S13052 | 190.0 |
30 | S14010 | 188.0 |
51 | S14050 | 185.0 |
44 | S14040 | 180.0 |
7 | S13003 | 180.0 |
解説:
df_receipt.groupby('store_cd').agg({'amount':'median'}).reset_index().sort_values('amount', ascending=False).head(5) はdf_receipt DataFrame の行を store_cd 列でグルーピングし、各グループについて amount 列の中央値を計算して store_cd 列と中央値で DataFrame を返しています。次に、結果のDataFrameのインデックスをリセットし、金額の中央値で降順にソートします。最後に、結果のDataFrameの最初の5行を返します。
以下は、各コンポーネントの説明です。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、有効な変数名であれば何でも構いません。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.agg({'amount':'median'}): これはpandasのGroupByオブジェクトのメソッドで、GroupByオブジェクトの各グループのamountカラムにmedian()集計関数を適用しています。結果は、各グループのamount列の中央値を含むamountという1つの列を持つDataFrameになります。
.reset_index(): これはpandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットしています。
.sort_values('amount', ascending=False): これは、DataFrameの行をamount列の値で降順にソートするpandasのDataFrameメソッドです。ascending=Falseパラメータは、ソート順を降順にすることを指定します。
.head(5): これは、DataFrameの最初の5行を返す、pandasのDataFrameメソッドです。
要約すると、このコードは、DataFrameの行を特定の列の値でグループ化し、各グループに対して別の列の中央値を求め、中央値とグループ化列を含むDataFrameを返しています。そして、得られたDataFrameのインデックスをリセットし、中央値で降順にソートし、得られたDataFrameの最初の5行を返します。このコードは、売上の中央値が最も高い店舗を特定するのに便利です。
P-029: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに商品コード(product_cd)の最頻値を求め、10件表示させよ。
df_receipt.groupby('store_cd').product_cd. \
apply(lambda x: x.mode()).reset_index().head(10)
store_cd | level_1 | product_cd | |
---|---|---|---|
0 | S12007 | 0 | P060303001 |
1 | S12013 | 0 | P060303001 |
2 | S12014 | 0 | P060303001 |
3 | S12029 | 0 | P060303001 |
4 | S12030 | 0 | P060303001 |
5 | S13001 | 0 | P060303001 |
6 | S13002 | 0 | P060303001 |
7 | S13003 | 0 | P071401001 |
8 | S13004 | 0 | P060303001 |
9 | S13005 | 0 | P040503001 |
解説:
df_receipt.groupby('store_cd').product_cd.apply(lambda x: x.mode()).reset_index().head(10) は、df_receipt DataFrame の行を store_cd 列でグループ化して、各グループに対して product_cd 列のモード値を求めているコード。そして、そのモードの値とstore_cd列を含むDataFrameを返します。reset_index()メソッドは、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。
以下は、各コンポーネントの説明です。
df_receipt: df_receipt:pandasでDataFrameオブジェクトを表す変数です。df_receiptという名前は、任意の有効な変数名である可能性があります。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これは、各グループに個別に関数を適用するために使用できるGroupByオブジェクトを作成します。
.product_cd : これは、DataFrameのproduct_cdカラムにアクセスするpandas DataFrame属性です。
.apply(lambda x: x.mode()): これは、GroupByオブジェクトの各グループにmode()関数を適用する、pandas GroupByオブジェクトのメソッドです。mode()関数は、各グループの中で最も頻出する値を返します。結果として得られるオブジェクトは、元のグループと同じ長さを持つシリーズです。
.reset_index(): これは、pandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。
要約すると、このコードは、特定の列の値によってDataFrameの行をグループ化し、各グループに対して別の列のモード値を求め、モード値とグループ化列を含むDataFrameを返しています。mode()関数は、各店舗で最も頻出する商品コードを見つけるために使用されます。このコードは、各店舗で販売されている最も人気のある商品を特定するのに便利です。
P-030: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の分散を計算し、降順で5件表示せよ。
df_receipt.groupby('store_cd').amount.var(ddof=0).reset_index(). \
sort_values('amount', ascending=False).head(5)
store_cd | amount | |
---|---|---|
28 | S13052 | 440088.701311 |
31 | S14011 | 306314.558164 |
42 | S14034 | 296920.081011 |
5 | S13001 | 295431.993329 |
12 | S13015 | 295294.361116 |
解説:
df_receipt.groupby('store_cd').amount.var(ddof=0).reset_index().sort_values('amount', ascending=False).head(5) はdf_receipt DataFrameの行をstore_cd列でグルーピングし、ddof=0で各グループに対して金額列に対する分散を計算して、その分散値とstore_cd列のデータフレームを返すコードです.reset_index()メソッドは、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。そして、分散値の降順で結果のDataFrameをソートして、最初の5行を返します。
以下、各構成要素について説明します。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、任意の有効な変数名である可能性があります。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これにより、各グループに個別に関数を適用するために使用できるGroupByオブジェクトが作成されます。
.amount.var(ddof=0): これは、GroupByオブジェクトの各グループに対して、ddof=0でamount列の分散を計算するpandas DataFrameメソッドです。ddofパラメータは、標本分散の計算で使用する除数である自由度のデルタを指定します。ddofに0を指定すると、標本分散ではなく、母集団分散を計算する必要があることを示します。結果は、分散値と store_cd 値をインデックスとする Series です。
.reset_index() : これはpandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットするものです。
.sort_values('amount', ascending=False): これは、DataFrameの行をamount列の値で降順にソートするpandasのDataFrameメソッドです。ascending=Falseパラメータは、ソート順を降順にすることを指定します。
.head(5): これは、DataFrameの最初の5行を返す、pandasのDataFrameメソッドです。
要約すると、このコードは、特定の列の値によってDataFrameの行をグループ化し、各グループについて別の列の母分散を求め、分散値とグループ化列を含むDataFrameを返しています。そして、得られたDataFrameのインデックスをリセットし、分散値の降順でソートし、得られたDataFrameの最初の5行を返します。このコードは、売上の変動が大きい店舗を特定するのに便利です。
P-031: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の標準偏差を計算し、降順で5件表示せよ。
TIPS:
PandasとNumpyでddofのデフォルト値が異なることに注意しましょうPandas: DataFrame.std(self, axis=None, skipna=None, level=None, ddof=1, numeric_only=None, **kwargs) Numpy: numpy.std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=)
df_receipt.groupby('store_cd').amount.std(ddof=0).reset_index(). \
sort_values('amount', ascending=False).head(5)
store_cd | amount | |
---|---|---|
28 | S13052 | 663.391816 |
31 | S14011 | 553.456916 |
42 | S14034 | 544.903736 |
5 | S13001 | 543.536561 |
12 | S13015 | 543.409938 |
解説:
df_receipt.groupby('store_cd').amount.std(ddof=0).reset_index().sort_values('amount', ascending=False).head(5) は、df_receipt DataFrame の行を store_cd 列でグルーピングし、各グループについて ddof=0 で amount 列の標準偏差を算出して、標準偏差値と store_cd 列のあるデータフレームを返すコードです。reset_index()メソッドは、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。そして、得られたDataFrameを標準偏差の値で降順にソートして、最初の5行を返します。
以下、各構成要素について説明します。
df_receipt: df_receipt:これはpandasのDataFrameオブジェクトを表す変数です。df_receiptという名前は、任意の有効な変数名である可能性があります。
.groupby('store_cd'): これはpandasのDataFrameメソッドで、DataFrameの行をstore_cdカラムの値でグループ化するものです。これにより、各グループに個別に関数を適用するために使用できるGroupByオブジェクトが作成されます。
.amount.std(ddof=0) : これは、GroupByオブジェクトの各グループのamount列の標準偏差をddof=0で計算するpandas DataFrameメソッドです。ddofパラメータは、標本標準偏差の計算で使用する除数である自由度(delta degrees of freedom)を指定しています。ddofに0を指定すると、標本標準偏差ではなく、母集団の標準偏差が計算されることになります。結果は、標準偏差の値とstore_cdの値をインデックスとするSeriesとなる。
.reset_index() : これはpandasのDataFrameメソッドで、結果のDataFrameのインデックスをデフォルトのインデックスにリセットします。
.sort_values('amount', ascending=False): これは、DataFrameの行をamount列の値で降順にソートするpandasのDataFrameメソッドです。ascending=Falseパラメータは、ソート順を降順にすることを指定します。
.head(5): これは、DataFrameの最初の5行を返す、pandasのDataFrameメソッドです。
要約すると、このコードは、DataFrameの行を特定の列の値でグループ化し、各グループに対して別の列の母標準偏差を求め、標準偏差の値とグループ化した列を含むDataFrameを返しています。そして、得られたDataFrameのインデックスをリセットし、標準偏差の値で降順にソートし、得られたDataFrameの最初の5行を返します。このコードは、売上のばらつきが大きい店舗を特定するのに便利です。
P-032: レシート明細データ(df_receipt)の売上金額(amount)について、25%刻みでパーセンタイル値を求めよ。
# コード例1
np.percentile(df_receipt['amount'], q=np.arange(1, 5) * 25)
array([ 102., 170., 288., 10925.])
解説:
np.percentile(df_receipt['amount'], q=np.arange(1, 5) * 25) というコードは、NumPyライブラリの np.percentile() 関数を使用して、df_receipt DataFrame の amount 列の値の四分位数を計算する。
以下、各要素について説明します。
np.percentile() : これは、配列の百分率を計算するNumPy関数である。第1引数はパーセンタイルを計算する配列、第2引数は計算するパーセンタイル値です。qパラメータは、計算するパーセンタイルを指定するために使用されます。例えば、q=np.arange(1, 5) * 25は、データの分布における25、50、75の位置のパーセンタイルを計算する。これは、np.arange(1, 5) * 25式が、分布におけるパーセンタイルの位置である値[25, 50, 75]の配列を生成するからです。
df_receipt['amount'] : df_receipt['amount']: df_receipt DataFrame の amount 列を表す pandas Series オブジェクトです。df_receipt DataFrameは、現在の環境に存在するものとします。
q=np.arange(1, 5) * 25: これは、計算するパーセンタイルを指定するNumPyの配列である。np.arange(1, 5) * 25 式は、分布におけるパーセンタイルの位置である値 [25, 50, 75] を持つ配列を生成します。
このコードでは、df_receipt DataFrameのamount列の四分位を計算します。これは、分布を4等分する値です。第一四分位数(Q1)は25パーセンタイル、第二四分位数(Q2)は50パーセンタイル(中央値ともいう)、第三四分位数(Q3)は75パーセンタイルとなります。このコードの出力は四分位値の配列で、これを用いて金額欄の分布を要約することができます。
# コード例2
df_receipt.amount.quantile(q=np.arange(1, 5) / 4)
0.25 102.0 0.50 170.0 0.75 288.0 1.00 10925.0 Name: amount, dtype: float64
解説:
このコードは、pandasのquantile()関数を使用して、df_receipt dataframeのamount列の四分位を計算するものです。qパラメータは、1/4、2/4(中央値)、3/4、4/4(最大値)である計算する分位数を指定します。
このコードでは、df_receiptのamount列に対してquantile()を呼び、qパラメータに四分位数1、2、3、4にそれぞれ対応する値[0.25, 0.5, 0.75, 1.0] の配列をセットして四分位を計算しています。
このコードの結果、出力は4つの値の配列となり、それぞれが対応する四分位点の値を表します。
P-033: レシート明細データ(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の平均を計算し、330以上のものを抽出せよ。
df_receipt.groupby('store_cd').amount.mean(). \
reset_index().query('amount >= 330')
store_cd | amount | |
---|---|---|
1 | S12013 | 330.194130 |
5 | S13001 | 348.470386 |
7 | S13003 | 350.915519 |
8 | S13004 | 330.943949 |
12 | S13015 | 351.111960 |
16 | S13019 | 330.208616 |
17 | S13020 | 337.879932 |
28 | S13052 | 402.867470 |
30 | S14010 | 348.791262 |
31 | S14011 | 335.718333 |
38 | S14026 | 332.340588 |
46 | S14045 | 330.082073 |
48 | S14047 | 330.077073 |
解説:
df_receipt.groupby('store_cd').amount.mean().reset_index().query('amount >= 330') は、df_receipt DataFrame を store_cd 列でグループ化して、各グループの amount 列の平均値を計算、結果の DataFrame のインデックスをリセット、そして結果の DataFrame を、amount の平均値が 330 以上の行だけを含むようにフィルターするコード。
以下、各構成要素について説明します。
df_receipt.groupby('store_cd'): df_receiptのDataFrameをstore_cdカラムの値でグループ化します。
.amount.mean(): 各グループのamount列の平均値を計算します。
.reset_index(): 結果のDataFrameのインデックスをリセットします。
.query('amount >= 330'): これは、amountの平均値が330以上の行のみを含むように、結果のDataFrameをフィルタリングします。
まとめると、このコードは、平均取引額が330円以上の店舗のみを選択する。
P-034: レシート明細データ(df_receipt)に対し、顧客ID(customer_id)ごとに売上金額(amount)を合計して全顧客の平均を求めよ。ただし、顧客IDが”Z”から始まるものは非会員を表すため、除外して計算すること。
# コード例1: queryを使わない書き方
df_receipt[~df_receipt['customer_id'].str.startswith("Z")]. \
groupby('customer_id').amount.sum().mean()
2547.742234529256
解説:
df_receipt[~df_receipt['customer_id'].str.startswith("Z")].groupby('customer_id').amount.sum().mean() というコードでは、以下のようになります。
df_receipt[~df_receipt['customer_id'].str.startswith("Z")]: これは、df_receipt DataFrameをフィルタリングして、customer_id列が文字 "Z "で始まる行を除外します。チルダ(~)文字は、str.startswith()メソッドが返すブーリアン値を反転させるための否定演算子として使用されます。
.groupby('customer_id').amount.sum(): フィルタリングされたDataFrameをcustomer_idカラムでグループ化し、各グループのamountカラムの合計を計算する。
.mean(): これは、各顧客(customer_idが "Z "で始まる顧客を除く)が使用した合計金額を表す、結果のシリーズの平均値を計算します。
要約すると、このコードは、df_receipt DataFrameのcustomer_idが "Z "で始まらない各顧客が使った金額の平均を計算する。これは、常連客の購買行動を把握するのに役立つと思われます。
# コード例2: queryを使う書き方
df_receipt.query('not customer_id.str.startswith("Z")',
engine='python').groupby('customer_id').amount.sum().mean()
2547.742234529256
解説:
このコードは、匿名でない顧客の平均消費額を計算する。以下はその仕組みです。
df_receiptは、トランザクションに関する情報を含むDataFrameです。
queryメソッドを使用して、customer_idが文字 "Z "で始まるトランザクションをフィルタリングしています。これは、文字列が特定の文字または部分文字列で始まっているかどうかをチェックするstr.startswith()メソッドを使用して行われます。
出来上がったDataFrameは、groupbyメソッドを使ってcustomer_idでグループ化されます。
金額列は、sumメソッドで選択され、各顧客が使った金額の合計が計算されます。
次に、meanメソッドを使用して、前のステップで計算された金額の平均を計算します。これが、このコードの最終結果です。
P-035: レシート明細データ(df_receipt)に対し、顧客ID(customer_id)ごとに売上金額(amount)を合計して全顧客の平均を求め、平均以上に買い物をしている顧客を抽出し、10件表示せよ。ただし、顧客IDが”Z”から始まるものは非会員を表すため、除外して計算すること。
df_amount_sum = df_receipt[~df_receipt['customer_id'].str.startswith("Z")].\
groupby('customer_id').amount.sum()
amount_mean = df_amount_sum.mean()
df_amount_sum = df_amount_sum.reset_index()
df_amount_sum[df_amount_sum['amount'] >= amount_mean].head(10)
customer_id | amount | |
---|---|---|
2 | CS001115000010 | 3044 |
4 | CS001205000006 | 3337 |
13 | CS001214000009 | 4685 |
14 | CS001214000017 | 4132 |
17 | CS001214000052 | 5639 |
21 | CS001215000040 | 3496 |
30 | CS001304000006 | 3726 |
32 | CS001305000005 | 3485 |
33 | CS001305000011 | 4370 |
53 | CS001315000180 | 3300 |
解説:
コード df_amount_sum = df_receipt[~df_receipt['customer_id'].str.startswith("Z")].groupby('customer_id').amount.sum(), amount_mean = df_amount_sum. mean(), df_amount_sum = df_amount_sum.reset_index(), df_amount_sum[df_amount_sum['amount'] >= amount_mean].head(10) は、以下のようにしています。
df_receipt[~df_receipt['customer_id'].str.startswith("Z")].groupby('customer_id').amount.sum(): df_receiptのDataFrameからcustomer_idカラムが "Z "で始まる行を除外し、customer_idカラムでグループ化し、各グループのamountカラムの合計を計算します。結果として得られるオブジェクトは、各顧客(customer_idが "Z "で始まる顧客を除く)が使った金額の合計を表すSeriesである。
amount_mean = df_amount_sum.mean(): df_receipt DataFrameのcustomer_idが "Z "で始まらない顧客の平均支出額を表すdf_amount_sum Seriesの平均値を計算する。
df_amount_sum = df_amount_sum.reset_index(): df_amount_sumシリーズのインデックスをリセットし、customer_idとamountの2つのカラムを持つDataFrameに変換しています。
df_amount_sum[df_amount_sum['amount'] >= amount_mean].head(10): これは、df_amount_sumのDataFrameをフィルタリングして、amount列がamount_mean以上の行のみを含むようにし、結果のDataFrameの最初の10行を返します。
要約すると、このコードは、df_receipt DataFrameのcustomer_idが "Z "で始まらない全ての顧客の平均支出額以上の支出額を持つ顧客を選択する。結果として得られるDataFrameには、選択された各顧客のcustomer_idと支出合計額が、支出合計額の降順でソートされて格納されます。
P-036: レシート明細データ(df_receipt)と店舗データ(df_store)を内部結合し、レシート明細データの全項目と店舗データの店舗名(store_name)を10件表示せよ。
pd.merge(df_receipt, df_store[['store_cd','store_name']],
how='inner', on='store_cd').head(10)
sales_ymd | sales_epoch | store_cd | receipt_no | receipt_sub_no | customer_id | product_cd | quantity | amount | store_name | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 20181103 | 1541203200 | S14006 | 112 | 1 | CS006214000001 | P070305012 | 1 | 158 | 葛が谷店 |
1 | 20181116 | 1542326400 | S14006 | 112 | 2 | ZZ000000000000 | P080401001 | 1 | 48 | 葛が谷店 |
2 | 20170118 | 1484697600 | S14006 | 1162 | 1 | CS006815000006 | P050406035 | 1 | 220 | 葛が谷店 |
3 | 20190524 | 1558656000 | S14006 | 1192 | 1 | CS006514000034 | P060104003 | 1 | 80 | 葛が谷店 |
4 | 20190419 | 1555632000 | S14006 | 112 | 2 | ZZ000000000000 | P060501002 | 1 | 148 | 葛が谷店 |
5 | 20181119 | 1542585600 | S14006 | 1152 | 2 | ZZ000000000000 | P050701001 | 1 | 88 | 葛が谷店 |
6 | 20171211 | 1512950400 | S14006 | 1132 | 2 | CS006515000175 | P090903001 | 1 | 80 | 葛が谷店 |
7 | 20191021 | 1571616000 | S14006 | 1112 | 2 | CS006415000221 | P040602001 | 1 | 405 | 葛が谷店 |
8 | 20170710 | 1499644800 | S14006 | 1132 | 2 | CS006411000036 | P090301051 | 1 | 330 | 葛が谷店 |
9 | 20190805 | 1564963200 | S14006 | 112 | 1 | CS006211000012 | P050104001 | 1 | 115 | 葛が谷店 |
解説:
pd.merge(df_receipt, df_store[['store_cd','store_name']], how='inner', on='store_cd').head(10) というコードでは、以下のようにしています。
df_store[['store_cd','store_name']] : これは、df_store DataFrameのうち、store_cdとstore_nameという2つのカラムを持つサブセットを選択するものです。
pd.merge(df_receipt, df_store[['store_cd','store_name']], how='inner', on='store_cd'): df_receipt DataFrameとdf_store['store_cd','store_name']間の内部結合を実行します。store_cdカラムのDataFrameを作成します。これは、store_cdカラムの2つのDataFrameをマージし、両方のDataFrameで一致する値を持つ行のみを含めます。
.head(10): これは、マージされたDataFrameの最初の10行を返します。
要約すると、このコードは、df_receipt DataFrameとdf_store DataFrameをstore_cd列でマージし、df_store DataFrameのstore_name列をマージしたDataFrameに追加しています。結果として得られるDataFrameには、df_receiptのトランザクションが発生した店舗に関する情報(store_cd、store_name、その他df_receipt DataFrameの全ての列が含まれる)が含まれる。
P-037: 商品データ(df_product)とカテゴリデータ(df_category)を内部結合し、商品データの全項目とカテゴリデータのカテゴリ小区分名(category_small_name)を10件表示せよ。
pd.merge(df_product
, df_category[['category_small_cd','category_small_name']]
, how='inner', on='category_small_cd').head(10)
product_cd | category_major_cd | category_medium_cd | category_small_cd | unit_price | unit_cost | category_small_name | |
---|---|---|---|---|---|---|---|
0 | P040101001 | 04 | 0401 | 040101 | 198.0 | 149.0 | 弁当類 |
1 | P040101002 | 04 | 0401 | 040101 | 218.0 | 164.0 | 弁当類 |
2 | P040101003 | 04 | 0401 | 040101 | 230.0 | 173.0 | 弁当類 |
3 | P040101004 | 04 | 0401 | 040101 | 248.0 | 186.0 | 弁当類 |
4 | P040101005 | 04 | 0401 | 040101 | 268.0 | 201.0 | 弁当類 |
5 | P040101006 | 04 | 0401 | 040101 | 298.0 | 224.0 | 弁当類 |
6 | P040101007 | 04 | 0401 | 040101 | 338.0 | 254.0 | 弁当類 |
7 | P040101008 | 04 | 0401 | 040101 | 420.0 | 315.0 | 弁当類 |
8 | P040101009 | 04 | 0401 | 040101 | 498.0 | 374.0 | 弁当類 |
9 | P040101010 | 04 | 0401 | 040101 | 580.0 | 435.0 | 弁当類 |
解説:
コード pd.merge(df_product , df_category[['category_small_cd','category_small_name']] です。, how='inner', on='category_small_cd').head(10) では、以下のようになります。
df_category[['category_small_cd','category_small_name']]: これは、df_category DataFrameのうち、category_small_cdとcategory_small_nameという2つのカラムを持つサブセットを選択するものです。
pd.merge(df_product , df_category[['category_small_cd','category_small_name']]) , how='inner', on='category_small_cd'): df_product DataFrameとdf_category[['category_small_cd','category_small_name']間の内部結合を実行するものである。] category_small_cdカラムのDataFrameを作成します。これは、category_small_cdカラムの2つのDataFrameをマージし、両方のDataFrameで一致する値を持つ行のみを含めます。
.head(10): これは、マージされたDataFrameの最初の10行を返します。
要約すると、このコードは、df_product DataFrameとdf_category DataFrameをcategory_small_cd列でマージし、df_category DataFrameからcategory_small_name列をマージしたDataFrameに追加しています。結果として得られるDataFrameは、df_productの商品に関する情報を含み、category_small_cd、category_small_name、およびdf_product DataFrameの他のすべてのカラムを含む。
P-038: 顧客データ(df_customer)とレシート明細データ(df_receipt)から、顧客ごとの売上金額合計を求め、10件表示せよ。ただし、売上実績がない顧客については売上金額を0として表示させること。また、顧客は性別コード(gender_cd)が女性(1)であるものを対象とし、非会員(顧客IDが”Z”から始まるもの)は除外すること。
df_amount_sum = df_receipt.groupby('customer_id').amount.sum().reset_index()
df_tmp = df_customer. \
query('gender_cd == "1" and not customer_id.str.startswith("Z")',
engine='python')
pd.merge(df_tmp['customer_id'], df_amount_sum,
how='left', on='customer_id').fillna(0).head(10)
customer_id | amount | |
---|---|---|
0 | CS021313000114 | 0.0 |
1 | CS031415000172 | 5088.0 |
2 | CS028811000001 | 0.0 |
3 | CS001215000145 | 875.0 |
4 | CS015414000103 | 3122.0 |
5 | CS033513000180 | 868.0 |
6 | CS035614000014 | 0.0 |
7 | CS011215000048 | 3444.0 |
8 | CS009413000079 | 0.0 |
9 | CS040412000191 | 210.0 |
解説:
コード df_amount_sum = df_receipt.groupby('customer_id').amount.sum().reset_index() df_tmp = df_customer.query('gender_cd == "1" and not customer_id. str.startswith("Z")', engine='python') pd.merge(df_tmp['customer_id'], df_amount_sum, how='left', on='customer_id').fillna(0).head(10) では以下のようになっています。
df_amount_sum = df_receipt.groupby('customer_id').amount.sum().reset_index(): これは、df_receipt DataFrameをcustomer_idでグループ化し、各顧客のamount列を合計して、新しいDataFrame df_amount_sumを作成する。生成されるDataFrameは、customer_idとamountの2つのカラムを持ちます。
df_customer.query('gender_cd == "1" and not customer_id.str.startswith("Z")', engine='python'): これは、df_customer DataFrameのうち、gender_cdが1であり、customer_idが "Z "で始まらないサブセットを選択します。engine='python'引数は、startswithのような文字列メソッドを使用できるようにするために使用されています。
pd.merge(df_tmp['customer_id'], df_amount_sum, how='left', on='customer_id'): これは、df_tmp DataFrameのcustomer_idカラムとdf_amount_sum DataFrameのcustomer_idカラムを左結合で結合します。これは、df_tmp DataFrameの全ての行を含み、df_amount_sum DataFrameの金額カラムを追加します(利用可能な場合)。
.fillna(0): amountカラムの欠損値を0にします。
.head(10): マージされたDataFrameの最初の10行を返します。
要約すると、このコードは、df_receipt DataFrameの各顧客が使った合計金額を計算し、次にdf_customer DataFrameから男性で有効なcustomer_id値を持つ顧客のサブセットを選択する。次に、このコードは左結合を使ってcustomer_id列で2つのDataFrameを結合し、結果のDataFrameに各顧客が使った金額の合計を含めます。結果のDataFrameには、選択された男性顧客のcustomer_idカラムとamountカラムが含まれます。
P-039: レシート明細データ(df_receipt)から、売上日数の多い顧客の上位20件を抽出したデータと、売上金額合計の多い顧客の上位20件を抽出したデータをそれぞれ作成し、さらにその2つを完全外部結合せよ。ただし、非会員(顧客IDが”Z”から始まるもの)は除外すること。
df_data = df_receipt \
.query('not customer_id.str.startswith("Z")', engine='python')
df_cnt = df_data[~df_data.duplicated(subset=['customer_id', 'sales_ymd'])] \
.groupby('customer_id').sales_ymd.count().reset_index() \
.sort_values('sales_ymd', ascending=False).head(20)
df_sum = df_data.groupby('customer_id').amount.sum().reset_index() \
.sort_values('amount', ascending=False).head(20)
pd.merge(df_cnt, df_sum, how='outer', on='customer_id')
customer_id | sales_ymd | amount | |
---|---|---|---|
0 | CS040214000008 | 23.0 | NaN |
1 | CS015415000185 | 22.0 | 20153.0 |
2 | CS010214000010 | 22.0 | 18585.0 |
3 | CS010214000002 | 21.0 | NaN |
4 | CS028415000007 | 21.0 | 19127.0 |
5 | CS017415000097 | 20.0 | 23086.0 |
6 | CS016415000141 | 20.0 | 18372.0 |
7 | CS031414000051 | 19.0 | 19202.0 |
8 | CS014214000023 | 19.0 | NaN |
9 | CS022515000226 | 19.0 | NaN |
10 | CS021515000172 | 19.0 | NaN |
11 | CS039414000052 | 19.0 | NaN |
12 | CS021514000045 | 19.0 | NaN |
13 | CS022515000028 | 18.0 | NaN |
14 | CS030214000008 | 18.0 | NaN |
15 | CS021515000056 | 18.0 | NaN |
16 | CS014415000077 | 18.0 | NaN |
17 | CS021515000211 | 18.0 | NaN |
18 | CS032415000209 | 18.0 | NaN |
19 | CS031414000073 | 18.0 | NaN |
20 | CS001605000009 | NaN | 18925.0 |
21 | CS006515000023 | NaN | 18372.0 |
22 | CS011414000106 | NaN | 18338.0 |
23 | CS038415000104 | NaN | 17847.0 |
24 | CS035414000024 | NaN | 17615.0 |
25 | CS021515000089 | NaN | 17580.0 |
26 | CS032414000072 | NaN | 16563.0 |
27 | CS016415000101 | NaN | 16348.0 |
28 | CS011415000006 | NaN | 16094.0 |
29 | CS034415000047 | NaN | 16083.0 |
30 | CS007514000094 | NaN | 15735.0 |
31 | CS009414000059 | NaN | 15492.0 |
32 | CS030415000034 | NaN | 15468.0 |
33 | CS015515000034 | NaN | 15300.0 |
解説:
このコードは、以下のステップを実行しています。
.query()メソッドを使用して、df_receiptデータフレーム内のcustomer_id列が "Z "で始まらない行をフィルタリングしてdf_dataに保存します。
df_dataからcustomer_id列とsales_ymd列に基づいて重複行を削除し、customer_idでグループ化して販売日の数を数え、df_cntに保存します。
df_cntをsales_ymdカラムで降順にソートし、上位20行を取得する。
df_dataをcustomer_idでグループ化し、amountカラムを合計し、amountカラムで降順にソートして上位20行を取り出し、df_sumに保存します。
df_cntとdf_sumのデータフレームを、how='outer'パラメータを持つpd.merge()メソッドを使ってcustomer_id列でマージし、df_dataに保存する(つまり、両方のデータフレームのすべてのレコードをマージ後のデータフレームに含める)。結果として得られるデータフレームには、販売日のカウントと購入金額の合計に基づく上位20名の顧客が含まれます。
P-040: 全ての店舗と全ての商品を組み合わせたデータを作成したい。店舗データ(df_store)と商品データ(df_product)を直積し、件数を計算せよ。
df_store_tmp = df_store.copy()
df_product_tmp = df_product.copy()
df_store_tmp['key'] = 0
df_product_tmp['key'] = 0
len(pd.merge(df_store_tmp, df_product_tmp, how='outer', on='key'))
531590
解説:
このコードは、外部結合を使用して2つのデータフレームdf_storeとdf_productを結合します。
まず、2つの一時的なデータフレームdf_store_tmpとdf_product_tmpが、それぞれdf_storeとdf_productのコピーとして作成されます。これは、外部結合がマージするための共通カラムを必要とするためです。
最後に、pd.merge()関数を使用して2つのデータフレームをマージします。how='outer'引数は外側結合を希望することを示し、on='key'はマージするための共通のカラムを指定します。結果のデータフレームは、両方のデータフレームのすべての行を含み、もう一方のデータフレームに対応するデータがないセルには欠損値(NaN)が含まれる。
最後に、len() を使って、マージされたデータフレーム内の行数を取得します。どちらのデータフレームも同じ行数なので、内側結合、左結合、右結合、外側結合のいずれを使用しても出力は同じになります。この場合、マージされたデータフレーム内の行の総数が出力されます。
Comment