参照(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-061: レシート明細データ(df_receipt)の売上金額(amount)を顧客ID(customer_id)ごとに合計し、売上金額合計を常用対数化(底10)して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")',
engine='python'). \
groupby('customer_id'). \
agg({'amount':'sum'}).reset_index()
df_sales_amount['log_amount'] = np.log10(df_sales_amount['amount'] + 0.5)
df_sales_amount.head(10)
customer_id | amount | log_amount | |
---|---|---|---|
0 | CS001113000004 | 1298 | 3.113442 |
1 | CS001114000005 | 626 | 2.796921 |
2 | CS001115000010 | 3044 | 3.483516 |
3 | CS001205000004 | 1988 | 3.298526 |
4 | CS001205000006 | 3337 | 3.523421 |
5 | CS001211000025 | 456 | 2.659441 |
6 | CS001212000027 | 448 | 2.651762 |
7 | CS001212000031 | 296 | 2.472025 |
8 | CS001212000046 | 228 | 2.358886 |
9 | CS001212000070 | 456 | 2.659441 |
解説:
このコードでは、以下の動作を行います。
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")', engine='python')
df_receiptというDataFrameから、customer_id列が "Z "で始まらない行をすべて選択する。出来上がったDataFrameはdf_sales_amountとして保存されます。
df_sales_amount = df_sales_amount.groupby('customer_id').agg({'amount':'sum'}).reset_index()
df_sales_amountのDataFrameをcustomer_idでグループ化し、aggメソッドで各グループの金額列の合計を算出します。結果のDataFrameはdf_sales_amountとして保存されます。
df_sales_amount['log_amount'] = np.log10(df_sales_amount['amount'] + 0.5)
df_sales_amount DataFrameのamount列の対数(底10)を計算します。0または負の値の対数を取ることを避けるために、まずamountの値に0.5を加算しています。結果の対数値はlog_amountという新しいカラムに保存されます。
df_sales_amount.head(10)
headメソッドを使用して、結果のdf_sales_amount DataFrameの最初の10行を表示します。
要約すると、このコードは、顧客IDが "Z "で始まらないDataFrameから行を選択し、それらの行を顧客IDでグループ化し、各顧客の売上金額の合計を計算し、売上金額の対数(ベース10)をとり、結果のDataFrameの最初の10行を表示します。
P-062: レシート明細データ(df_receipt)の売上金額(amount)を顧客ID(customer_id)ごとに合計し、売上金額合計を自然対数化(底e)して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")',
engine='python'). \
groupby('customer_id'). \
agg({'amount':'sum'}).reset_index()
df_sales_amount['log_amount'] = np.log(df_sales_amount['amount'] + 0.5)
df_sales_amount.head(10)
customer_id | amount | log_amount | |
---|---|---|---|
0 | CS001113000004 | 1298 | 7.168965 |
1 | CS001114000005 | 626 | 6.440149 |
2 | CS001115000010 | 3044 | 8.021092 |
3 | CS001205000004 | 1988 | 7.595136 |
4 | CS001205000006 | 3337 | 8.112977 |
5 | CS001211000025 | 456 | 6.123589 |
6 | CS001212000027 | 448 | 6.105909 |
7 | CS001212000031 | 296 | 5.692047 |
8 | CS001212000046 | 228 | 5.431536 |
9 | CS001212000070 | 456 | 6.123589 |
解説:
このコードは、df_receiptというpandasのDataFrameに対していくつかのデータ処理を実行します。
まず、customer_id 列が文字 "Z" で始まる行をフィルタリングしています。これは、str.startswith()メソッドを使用した条件付きでquery()メソッドを使用しています。engine='python'引数は、文字列操作にデフォルトのpandasパーサではなく、Pythonパーサを使用するために提供されています。
得られたDataFrameは、groupby()メソッドを用いてcustomer_idカラムでグループ化されます。
agg()メソッドは、各グループの金額カラムに関数を適用するために使用されます。この場合、sum()関数を使用して、各顧客の売上金額の合計を計算します。
次に、reset_index()を使用して、結果のDataFrameをリセットし、新しいインデックスを持たせます。
最後に、amount 列の自然対数に 0.5 を加えた値(ゼロまたは負の値の対数を取るのを避けるため)を取ることで log_amount という新しい列を作成します。
結果のDataFrameの最初の10行は、head()メソッドを使用して表示されます。
P-063: 商品データ(df_product)の単価(unit_price)と原価(unit_cost)から各商品の利益額を算出し、結果を10件表示せよ。
df_tmp = df_product.copy()
df_tmp['unit_profit'] = df_tmp['unit_price'] - df_tmp['unit_cost']
df_tmp.head(10)
product_cd | category_major_cd | category_medium_cd | category_small_cd | unit_price | unit_cost | unit_profit | |
---|---|---|---|---|---|---|---|
0 | P040101001 | 04 | 0401 | 040101 | 198.0 | 149.0 | 49.0 |
1 | P040101002 | 04 | 0401 | 040101 | 218.0 | 164.0 | 54.0 |
2 | P040101003 | 04 | 0401 | 040101 | 230.0 | 173.0 | 57.0 |
3 | P040101004 | 04 | 0401 | 040101 | 248.0 | 186.0 | 62.0 |
4 | P040101005 | 04 | 0401 | 040101 | 268.0 | 201.0 | 67.0 |
5 | P040101006 | 04 | 0401 | 040101 | 298.0 | 224.0 | 74.0 |
6 | P040101007 | 04 | 0401 | 040101 | 338.0 | 254.0 | 84.0 |
7 | P040101008 | 04 | 0401 | 040101 | 420.0 | 315.0 | 105.0 |
8 | P040101009 | 04 | 0401 | 040101 | 498.0 | 374.0 | 124.0 |
9 | P040101010 | 04 | 0401 | 040101 | 580.0 | 435.0 | 145.0 |
解説:
このコードでは、以下の動作を行います。
df_tmp = df_product.copy()
df_productというDataFrameのコピーを作成し、df_tmpという新しい変数に代入しています。コピーは、元のDataFrameを変更しないように作成されています。
df_tmp['unit_profit'] = df_tmp['unit_price'] - df_tmp['unit_cost'].
これは、単価から単価を引くことによって、各商品の単位あたりの利益を計算し、その結果をunit_profitという新しいカラムに保存します。出来上がったDataFrameはdf_tmpとして保存されます。
df_tmp.head(10)
headメソッドを使用して、df_tmp DataFrameの最初の10行を表示しています。
要約すると、このコードは、DataFrameのコピーを作成し、単価から単価を引いて各商品の単位当たりの利益を計算し、コピーしたDataFrameに単位当たりの利益を持つ新しい列を追加し、結果のDataFrameの最初の10行を表示します。
P-064: 商品データ(df_product)の単価(unit_price)と原価(unit_cost)から、各商品の利益率の全体平均を算出せよ。ただし、単価と原価には欠損が生じていることに注意せよ。
df_tmp = df_product.copy()
df_tmp['unit_profit_rate'] = \
(df_tmp['unit_price'] - df_tmp['unit_cost']) / df_tmp['unit_price']
df_tmp['unit_profit_rate'].mean(skipna=True)
0.24911389885176904
解説:
このコードでは、以下の動作を行います。
df_tmp = df_product.copy()
df_productというDataFrameのコピーを作成し、df_tmpという新しい変数に代入しています。コピーは、元のDataFrameを変更しないように作成されています。
df_tmp['unit_profit_rate'] = (df_tmp['unit_price'] - df_tmp['unit_cost']) / df_tmp['unit_price'].
利益(単価から単価を引いたもの)を単価で割って、各商品の単位あたりの利益率を計算し、その結果をunit_profit_rateという新しいカラムに保存しています。出来上がったDataFrameはdf_tmpとして保存されます。
df_tmp['unit_profit_rate'].mean(skipna=True)
df_tmp DataFrameのunit_profit_rate列の平均値をmean法で計算し、欠損値(NaN)があればスキップして結果を表示します。
要約すると、DataFrameのコピーを作成し、利益(単価から単価を引いたもの)を単価で割って各商品の単位あたりの利益率を計算し、コピーしたDataFrameに単位あたりの利益率の新しい列を追加し、利益率列の平均を計算し、結果の平均値を表示する、というコードです。
P-065: 商品データ(df_product)の各商品について、利益率が30%となる新たな単価を求めよ。ただし、1円未満は切り捨てること。そして結果を10件表示させ、利益率がおよそ30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には欠損が生じていることに注意せよ。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_tmp['new_price'] = np.floor(df_tmp['unit_cost'] / 0.7)
df_tmp['new_profit_rate'] = \
(df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price']
df_tmp.head(10)
product_cd | unit_price | unit_cost | new_price | new_profit_rate | |
---|---|---|---|---|---|
0 | P040101001 | 198.0 | 149.0 | 212.0 | 0.297170 |
1 | P040101002 | 218.0 | 164.0 | 234.0 | 0.299145 |
2 | P040101003 | 230.0 | 173.0 | 247.0 | 0.299595 |
3 | P040101004 | 248.0 | 186.0 | 265.0 | 0.298113 |
4 | P040101005 | 268.0 | 201.0 | 287.0 | 0.299652 |
5 | P040101006 | 298.0 | 224.0 | 320.0 | 0.300000 |
6 | P040101007 | 338.0 | 254.0 | 362.0 | 0.298343 |
7 | P040101008 | 420.0 | 315.0 | 450.0 | 0.300000 |
8 | P040101009 | 498.0 | 374.0 | 534.0 | 0.299625 |
9 | P040101010 | 580.0 | 435.0 | 621.0 | 0.299517 |
解説:
このコードでは、以下の動作を行います。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_tmpという新しいDataFrameを作成し、product_cd, unit_price, unit_costという3つの列のみを含むようにし、元のdf_product DataFrameからこれらの列のコピーを作成します。
df_tmp['new_price'] = np.floor(df_tmp['unit_cost'] / 0.7)
これは、単価を0.7(マークアップ係数1.43)で割り、floorメソッドで小数点以下を切り捨てて各商品の新しい価格を計算し、結果をnew_priceという新しいカラムに保存しています。結果のDataFrameは、df_tmpとして保存されます。
df_tmp['new_profit_rate'] = (df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price'].
前のステップで計算した新しい価格を用いて、各商品の単位あたりの利益率を計算し、その結果をnew_profit_rateという新しいカラムに保存しています。結果のDataFrameはdf_tmpとして保存されます。
df_tmp.head(10)
headメソッドを使用して、df_tmp DataFrameの最初の10行を表示しています。
要約すると、このコードは、3つの列を持つ新しいDataFrameを作成し、マークアップ係数1.43に基づいて各商品の新しい価格を計算し、新しい価格を使用して各商品の単位あたりの利益率を計算し、コピーしたDataFrameに新しい2列を追加し、結果のDataFrameの最初の10行を表示します。
P-066: 商品データ(df_product)の各商品について、利益率が30%となる新たな単価を求めよ。今回は、1円未満を丸めること(四捨五入または偶数への丸めで良い)。そして結果を10件表示させ、利益率がおよそ30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には欠損が生じていることに注意せよ。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_tmp['new_price'] = np.round(df_tmp['unit_cost'] / 0.7)
df_tmp['new_profit_rate'] = \
(df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price']
df_tmp.head(10)
product_cd | unit_price | unit_cost | new_price | new_profit_rate | |
---|---|---|---|---|---|
0 | P040101001 | 198.0 | 149.0 | 213.0 | 0.300469 |
1 | P040101002 | 218.0 | 164.0 | 234.0 | 0.299145 |
2 | P040101003 | 230.0 | 173.0 | 247.0 | 0.299595 |
3 | P040101004 | 248.0 | 186.0 | 266.0 | 0.300752 |
4 | P040101005 | 268.0 | 201.0 | 287.0 | 0.299652 |
5 | P040101006 | 298.0 | 224.0 | 320.0 | 0.300000 |
6 | P040101007 | 338.0 | 254.0 | 363.0 | 0.300275 |
7 | P040101008 | 420.0 | 315.0 | 450.0 | 0.300000 |
8 | P040101009 | 498.0 | 374.0 | 534.0 | 0.299625 |
9 | P040101010 | 580.0 | 435.0 | 621.0 | 0.299517 |
解説:
このコードでは、以下の動作を行います。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_tmpという新しいDataFrameを作成し、product_cd, unit_price, unit_costという3つの列のみを含むようにし、元のdf_product DataFrameからこれらの列のコピーを作成します。
df_tmp['new_price'] = np.round(df_tmp['unit_cost'] / 0.7)
これは、単価を0.7(マークアップ係数1.43)で割って各商品の新しい価格を計算し、roundメソッドを使って最も近い整数に丸め、結果をnew_priceという新しいカラムに保存しています。結果のDataFrameは、df_tmpとして保存されます。
df_tmp['new_profit_rate'] = (df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price'].
前のステップで計算した新しい価格を用いて、各商品の単位あたりの利益率を計算し、その結果をnew_profit_rateという新しいカラムに保存しています。結果のDataFrameはdf_tmpとして保存されます。
df_tmp.head(10)
headメソッドを使用して、df_tmp DataFrameの最初の10行を表示しています。
要約すると、このコードは、3つの列を持つ新しいDataFrameを作成し、マークアップ係数1.43と最も近い整数への丸め込みに基づいて各製品の新しい価格を計算し、新しい価格を使用して各製品の単位あたりの利益率を計算し、コピーしたDataFrameに新しい2列を追加し、結果のDataFrameの最初の10行を表示するものです。
P-067: 商品データ(df_product)の各商品について、利益率が30%となる新たな単価を求めよ。今回は、1円未満を切り上げること。そして結果を10件表示させ、利益率がおよそ30%付近であることを確認せよ。ただし、単価(unit_price)と原価(unit_cost)には欠損が生じていることに注意せよ。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_tmp['new_price'] = np.ceil(df_tmp['unit_cost'] / 0.7)
df_tmp['new_profit_rate'] = \
(df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price']
df_tmp.head(10)
product_cd | unit_price | unit_cost | new_price | new_profit_rate | |
---|---|---|---|---|---|
0 | P040101001 | 198.0 | 149.0 | 213.0 | 0.300469 |
1 | P040101002 | 218.0 | 164.0 | 235.0 | 0.302128 |
2 | P040101003 | 230.0 | 173.0 | 248.0 | 0.302419 |
3 | P040101004 | 248.0 | 186.0 | 266.0 | 0.300752 |
4 | P040101005 | 268.0 | 201.0 | 288.0 | 0.302083 |
5 | P040101006 | 298.0 | 224.0 | 320.0 | 0.300000 |
6 | P040101007 | 338.0 | 254.0 | 363.0 | 0.300275 |
7 | P040101008 | 420.0 | 315.0 | 451.0 | 0.301552 |
8 | P040101009 | 498.0 | 374.0 | 535.0 | 0.300935 |
9 | P040101010 | 580.0 | 435.0 | 622.0 | 0.300643 |
解説:
このコードは、df_productという名前のpandas DataFrameオブジェクトを操作しています。
以下、コードを一行ずつ分解してみます。
df_tmp = df_product[['product_cd', 'unit_price', 'unit_cost']].copy(): この行は、df_productのサブセットで、"product_cd", "unit_price", "unit_cost "の列のみを含むdf_tmpという新しいDataFrameオブジェクトを作成します。.copy()メソッドは、元のdf_productを参照する代わりに、DataFrameの新しいコピーを作成するために使用されます。
df_tmp['new_price'] = np.ceil(df_tmp['unit_cost'] / 0.7): この行は、df_tmpに「new_price」という新しい列を計算します。これは、「unit_cost」列を0.7で割り、その結果の上限をnumpyのceil()関数で取った結果です。これにより、利益率30%に基づく新しい価格が計算されます。
df_tmp['new_profit_rate'] = \ (df_tmp['new_price'] - df_tmp['unit_cost']) / df_tmp['new_price']: この行は、df_tmpに「new_profit_rate」という新しい列を作り、「new_price」列から「unit_cost」列を引き、「new_price」列で割った結果である。これにより、新価格に対する利益率が計算される。
df_tmp.head(10): df_tmp DataFrameの最初の10行を出力する。この行は、前の行の計算が正しく行われたかどうかをチェックするために使用されます。
P-068: 商品データ(df_product)の各商品について、消費税率10%の税込み金額を求めよ。1円未満の端数は切り捨てとし、結果を10件表示せよ。ただし、単価(unit_price)には欠損が生じていることに注意せよ。
df_tmp = df_tmp = df_product[['product_cd', 'unit_price']].copy()
df_tmp['tax_price'] = np.floor(df_tmp['unit_price'] * 1.1)
df_tmp.head(10)
product_cd | unit_price | tax_price | |
---|---|---|---|
0 | P040101001 | 198.0 | 217.0 |
1 | P040101002 | 218.0 | 239.0 |
2 | P040101003 | 230.0 | 253.0 |
3 | P040101004 | 248.0 | 272.0 |
4 | P040101005 | 268.0 | 294.0 |
5 | P040101006 | 298.0 | 327.0 |
6 | P040101007 | 338.0 | 371.0 |
7 | P040101008 | 420.0 | 462.0 |
8 | P040101009 | 498.0 | 547.0 |
9 | P040101010 | 580.0 | 638.0 |
解説:
このコードでは、df_productという名前のpandas DataFrameオブジェクトも操作しています。
以下、コードを一行ずつ分解してみます。
df_tmp = df_product[['product_cd', 'unit_price']].copy(): この行は、df_productのサブセットで、"product_cd "と "unit_price "の列のみを含むdf_tmpという新しいDataFrameオブジェクトを作成しています。.copy()メソッドは、元のdf_productを参照する代わりに、DataFrameの新しいコピーを作成するために使用されています。
df_tmp['tax_price'] = np.floor(df_tmp['unit_price'] * 1.1): この行は、df_tmpに「tax_price」という新しい列を計算します。この列は、「unit_price」列に1.1を掛け、その結果の床をnumpyのfloor()関数で取った結果です。これにより、10%の税金が加算された新しい価格が計算されます。
df_tmp.head(10): この行は、df_tmpのDataFrameの最初の10行をプリントアウトします。これは、前の行の計算が正しく行われたかどうかをチェックするために使用します。
P-069: レシート明細データ(df_receipt)と商品データ(df_product)を結合し、顧客毎に全商品の売上金額合計と、カテゴリ大区分コード(category_major_cd)が”07″(瓶詰缶詰)の売上金額合計を計算の上、両者の比率を求めよ。抽出対象はカテゴリ大区分コード”07″(瓶詰缶詰)の売上実績がある顧客のみとし、結果を10件表示せよ。
# コード例1
df_tmp_1 = df_receipt.groupby('customer_id').agg({'amount':'sum'}). \
reset_index().rename(columns={'amount':'sum_all'})
df_tmp_2 = pd.merge(df_receipt, df_product.query('category_major_cd == "07"'),
how='inner', on='product_cd').groupby('customer_id').\
agg({'amount':'sum'}).reset_index().\
rename(columns={'amount':'sum_07'})
df_tmp_3 = pd.merge(df_tmp_1, df_tmp_2, how='inner', on='customer_id')
df_tmp_3['sales_rate'] = df_tmp_3['sum_07'] / df_tmp_3['sum_all']
df_tmp_3.head(10)
customer_id | sum_all | sum_07 | sales_rate | |
---|---|---|---|---|
0 | CS001113000004 | 1298 | 1298 | 1.000000 |
1 | CS001114000005 | 626 | 486 | 0.776358 |
2 | CS001115000010 | 3044 | 2694 | 0.885020 |
3 | CS001205000004 | 1988 | 346 | 0.174044 |
4 | CS001205000006 | 3337 | 2004 | 0.600539 |
5 | CS001212000027 | 448 | 200 | 0.446429 |
6 | CS001212000031 | 296 | 296 | 1.000000 |
7 | CS001212000046 | 228 | 108 | 0.473684 |
8 | CS001212000070 | 456 | 308 | 0.675439 |
9 | CS001213000018 | 243 | 145 | 0.596708 |
解説:
このコードでは、pandasのDataFrameオブジェクトであるdf_receiptとdf_productも操作しています。
以下、コードを一行ずつ分解してみます。
df_tmp_1 = df_receipt.groupby('customer_id').agg({'amount':'sum'}).reset_index().rename(columns={'amount':'sum_all'}): この行は、df_receipt DataFrameを "customer_id "でグループ化し、グループごとに "amount "列の合計を計算し、その結果のDataFrameのインデックスをリセットしています。その後、.rename()メソッドを用いて、結果のDataFrameの名前を "sum_all "に変更します。
df_tmp_2 = pd.merge(df_receipt, df_product.query('category_major_cd == "07"'), how='inner', on='product_cd').groupby('customer_id').agg({'amount': sum'}).reset_index().rename(columns={'amount': 'sum_07' }): この行は、まずdf_productをフィルタリングして、category_major_cd列が "07 "に等しい行のみを含むようにします。次に、このフィルタリングされたDataFrameを、内側結合を使用して、"product_cd "カラムのdf_receiptと結合させます。次に、結果のDataFrameは「customer_id」でグループ化され、グループごとに「amount」列の合計が計算され、インデックスがリセットされる。出来上がったDataFrameは、.rename()メソッドを使用して "sum_07 "にリネームされます。
df_tmp_3 = pd.merge(df_tmp_1, df_tmp_2, how='inner', on='customer_id'): この行は、内側結合を使用して、df_tmp_1とdf_tmp_2を「customer_id」カラムでマージします。これにより、各顧客の「sum_all」カラムと「sum_07」カラムを含む新しいDataFrameが作成されます。
df_tmp_3['sales_rate'] = df_tmp_3['sum_07'] / df_tmp_3['sum_all']: この行は、df_tmp_3の新しい列「sales_rate」を計算するもので、「sum_07」列を「sum_all」列で割った結果です。これは、顧客の総購入額のうち、「07」カテゴリーからの購入の割合を計算するものである。
df_tmp_3.head(10)。df_tmp_3 DataFrameの最初の10行を出力します。この行は、前の行の計算が正しく行われたかどうかをチェックするために使用されます。
# コード例2(参考、unstackと横方向のsumを使った例)
df_temp = df_receipt.merge(df_product, how='left', on='product_cd'). \
groupby(['customer_id', 'category_major_cd'])['amount'].sum().unstack()
df_temp = df_temp[df_temp['07'] > 0]
df_temp['sum_all'] = df_temp.sum(axis=1)
df_temp['sales_rate'] = df_temp['07'] / df_temp['sum_all']
# 以降はデータフレームの整形と表示のための処理
df_temp.columns.name = ''
df_temp = df_temp.reset_index()
df_temp.head(10)
customer_id | 04 | 05 | 06 | 07 | 08 | 09 | sum_all | sales_rate | |
---|---|---|---|---|---|---|---|---|---|
0 | CS001113000004 | NaN | NaN | NaN | 1298.0 | NaN | NaN | 1298.0 | 1.000000 |
1 | CS001114000005 | NaN | 40.0 | NaN | 486.0 | 100.0 | NaN | 626.0 | 0.776358 |
2 | CS001115000010 | NaN | NaN | NaN | 2694.0 | NaN | 350.0 | 3044.0 | 0.885020 |
3 | CS001205000004 | 100.0 | 128.0 | 286.0 | 346.0 | 368.0 | 760.0 | 1988.0 | 0.174044 |
4 | CS001205000006 | 635.0 | 60.0 | 198.0 | 2004.0 | 80.0 | 360.0 | 3337.0 | 0.600539 |
5 | CS001212000027 | 248.0 | NaN | NaN | 200.0 | NaN | NaN | 448.0 | 0.446429 |
6 | CS001212000031 | NaN | NaN | NaN | 296.0 | NaN | NaN | 296.0 | 1.000000 |
7 | CS001212000046 | NaN | NaN | NaN | 108.0 | NaN | 120.0 | 228.0 | 0.473684 |
8 | CS001212000070 | NaN | NaN | 148.0 | 308.0 | NaN | NaN | 456.0 | 0.675439 |
9 | CS001213000018 | NaN | NaN | NaN | 145.0 | 98.0 | NaN | 243.0 | 0.596708 |
解説:
このコードでは、pandasのDataFrameオブジェクトであるdf_receiptとdf_productも操作しています。
以下、コードを一行ずつ分解してみます。
df_temp = df_receipt.merge(df_product, how='left', on='product_cd').groupby(['customer_id', 'category_major_cd']) ['amount'].sum().unstack(): この行は、まず左結合を使って、df_receiptとdf_productを "product_cd "カラムで結合します。次に、結果のDataFrameを "customer_id "と "category_major_cd "でグループ化し、各グループの "amount "列の合計を計算し、結果のシリーズをアンスタックして、インデックスとして "customer_id"、列名として "category_major_cd"、値として "amount "列を持つDataFrameを作成しました。
df_temp = df_temp[df_temp['07'] > 0]: この行は、df_temp DataFrameをフィルタリングして、「07」列の値が0より大きい行のみを含むようにします。これにより、「07」カテゴリから何も購入していない顧客が取り除かれます。
df_temp['sum_all'] = df_temp.sum(axis=1): この行は、df_tempに「sum_all」という新しい列を計算し、各行のすべての列の合計を算出します。これは、各顧客がすべてのカテゴリーにわたって費やした金額の合計を計算するものです。
df_temp['sales_rate'] = df_temp['07'] / df_temp['sum_all']: この行は、df_tempに「sales_rate」という新しいカラムを計算します。これは、顧客の総購入額のうち、「07」カテゴリーからの購入の割合を計算するものです。
df_temp.columns.name = '': この行は、df_temp DataFrameから列名を削除しています。
df_temp = df_temp.reset_index(): この行は、df_tempのインデックスをリセットして、0から始まる整数の範囲を持つ「index」という新しい列を作成します。
df_temp.head(10): この行は、df_tempデータフレームの最初の10行をプリントアウトします。この行は、前の行の計算が正しく行われたかどうかをチェックするために使用されます。
P-070: レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、顧客データ(df_customer)の会員申込日(application_date)からの経過日数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates()
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']],
how='inner', on='customer_id')
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'])
df_tmp['elapsed_days'] = df_tmp['sales_ymd'] - df_tmp['application_date']
df_tmp['elapsed_days'] = df_tmp['elapsed_days'].dt.days
df_tmp.head(10)
customer_id | sales_ymd | application_date | elapsed_days | |
---|---|---|---|---|
0 | CS006214000001 | 2018-11-03 | 2015-02-01 | 1371 |
1 | CS006214000001 | 2017-05-09 | 2015-02-01 | 828 |
2 | CS006214000001 | 2017-06-08 | 2015-02-01 | 858 |
3 | CS006214000001 | 2018-10-28 | 2015-02-01 | 1365 |
4 | CS006214000001 | 2019-09-08 | 2015-02-01 | 1680 |
5 | CS006214000001 | 2018-01-31 | 2015-02-01 | 1095 |
6 | CS006214000001 | 2017-07-05 | 2015-02-01 | 885 |
7 | CS006214000001 | 2018-11-10 | 2015-02-01 | 1378 |
8 | CS006214000001 | 2019-04-10 | 2015-02-01 | 1529 |
9 | CS006214000001 | 2019-06-01 | 2015-02-01 | 1581 |
解説:
このコードは、df_receiptというDataFrameとdf_customerという別のDataFrameに対してデータ操作を行っています。顧客の申込日と購入日の間の日数を計算しています。
以下はコードのステップバイステップです。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates(): df_receipt DataFrame から "customer_id" と "sales_ymd" 列のみを選択して、df_tmp という新しい DataFrame を作り、重複する行は削除しています。この結果、顧客IDと売上日の組み合わせが一意になるDataFrameが出来上がります。
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']], how='inner', on='customer_id'): df_tmpとdf_customer DataFrameの内側結合を行い、df_customer DataFrameから「customer_id」列と「application_date」列のみ選択します。結果として得られるDataFrameには、df_tmpとdf_customerの両方に存在する顧客IDを持つ行のみが含まれることになります。
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str')): df_tmp の "sales_ymd" 列を pd.to_datetime() 関数を使用して datetime 形式に変換する。この関数は、カラムを文字列形式から、計算に使用できるdatetime形式に変換する。
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date']): df_tmp の "application_date" 列を pd.to_datetime() 関数を使用して datetime 形式に変換する。この関数は、カラムを文字列形式から計算に使用できるdatetime形式に変換する。
df_tmp['elapsed_days'] = df_tmp['sales_ymd'] - df_tmp['application_date']: "sales_ymd" と "application_date" 列の差を計算し、結果を df_tmp の新しい列 "elapsed_days" に代入。この計算の結果、2つの日付の差を日、時間、分、秒で表したtimedeltaオブジェクトが生成されます。
df_tmp['elapsed_days'] = df_tmp['elapsed_days'].dt.days: "elapsed_days" 列から日数だけを取り出し、その結果を同じ列に代入して戻す。elapsed_days」カラムには現在timedeltaオブジェクトが含まれているが、日数のみを保持したいため、このステップが必要である。
df_tmp.head(10): df_tmpの最初の10行を表示し、計算が正しく行われたことを確認する。
全体として、このコードは、顧客の行動を分析し、顧客がメンバーシップやアカウントを申し込んでから購入するまでにかかる時間を判断するのに便利です。
P-071: レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、顧客データ(df_customer)の会員申込日(application_date)からの経過月数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1ヶ月未満は切り捨てること。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates()
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']],
how='inner', on='customer_id')
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'])
df_tmp['elapsed_months'] = df_tmp[['sales_ymd', 'application_date']]. \
apply(lambda x: relativedelta(x[0], x[1]).years * 12 + \
relativedelta(x[0], x[1]).months, axis=1)
df_tmp.head(10)
customer_id | sales_ymd | application_date | elapsed_months | |
---|---|---|---|---|
0 | CS006214000001 | 2018-11-03 | 2015-02-01 | 45 |
1 | CS006214000001 | 2017-05-09 | 2015-02-01 | 27 |
2 | CS006214000001 | 2017-06-08 | 2015-02-01 | 28 |
3 | CS006214000001 | 2018-10-28 | 2015-02-01 | 44 |
4 | CS006214000001 | 2019-09-08 | 2015-02-01 | 55 |
5 | CS006214000001 | 2018-01-31 | 2015-02-01 | 35 |
6 | CS006214000001 | 2017-07-05 | 2015-02-01 | 29 |
7 | CS006214000001 | 2018-11-10 | 2015-02-01 | 45 |
8 | CS006214000001 | 2019-04-10 | 2015-02-01 | 50 |
9 | CS006214000001 | 2019-06-01 | 2015-02-01 | 52 |
解説:
このコードは、前回説明したコードと似ていますが、顧客の申込日から購入日までの日数を計算するのではなく、経過月数を計算するものです。
以下、コードをステップバイステップで説明します。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates(): df_receipt DataFrameからcustomer_idとsales_ymdの列のみを選択し、重複する行を削除して、新しいdf_tmpというDataFrameを作成します。この結果、顧客IDと売上日の組み合わせが一意になるDataFrameが出来上がります。
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']], how='inner', on='customer_id'): df_tmpとdf_customer DataFrameの内側結合を行い、df_customer DataFrameから「customer_id」列と「application_date」列のみ選択します。結果として得られるDataFrameには、df_tmpとdf_customerの両方に存在する顧客IDを持つ行のみが含まれることになります。
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str')): df_tmp の "sales_ymd" 列を pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date']): df_tmp の "application_date" カラムを pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['elapsed_months'] = df_tmp[['sales_ymd', 'application_date']]. \ Γ apply(lambda x: relativedelta(x[0], x[1]).years * 12 + Γ relativedelta(x[0], x[1]).months, axis=1): "sales_ymd" と "application_date" 列の差を計算して、結果を df_tmp の "elapsed_months" という新しい列へ代入しています。この計算では、dateutilライブラリのrelativedelta関数を使用して、2つのdatetime値の差を年や月の単位で計算する。apply()メソッドでは、"sales_ymd "と "application_date "列の各行にこの関数を適用し、経過月数の合計を返す。
df_tmp.head(10): df_tmpの最初の10行を表示し、計算が正しく行われたことを確認します。
全体として、このコードは、顧客の行動を分析し、顧客がメンバーシップやアカウントを申し込んでから購入するまでの期間を経過月数で判断するのに便利である。
P-072: レシート明細データ(df_receipt)の売上日(df_customer)に対し、顧客データ(df_customer)の会員申込日(application_date)からの経過年数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1年未満は切り捨てること。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates()
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']],
how='inner', on='customer_id')
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'])
df_tmp['elapsed_years'] = df_tmp[['sales_ymd', 'application_date']]. \
apply(lambda x: relativedelta(x[0], x[1]).years, axis=1)
df_tmp.head(10)
customer_id | sales_ymd | application_date | elapsed_years | |
---|---|---|---|---|
0 | CS006214000001 | 2018-11-03 | 2015-02-01 | 3 |
1 | CS006214000001 | 2017-05-09 | 2015-02-01 | 2 |
2 | CS006214000001 | 2017-06-08 | 2015-02-01 | 2 |
3 | CS006214000001 | 2018-10-28 | 2015-02-01 | 3 |
4 | CS006214000001 | 2019-09-08 | 2015-02-01 | 4 |
5 | CS006214000001 | 2018-01-31 | 2015-02-01 | 2 |
6 | CS006214000001 | 2017-07-05 | 2015-02-01 | 2 |
7 | CS006214000001 | 2018-11-10 | 2015-02-01 | 3 |
8 | CS006214000001 | 2019-04-10 | 2015-02-01 | 4 |
9 | CS006214000001 | 2019-06-01 | 2015-02-01 | 4 |
解説:
このコードは、前回説明した例と似ていますが、顧客の申込日から購入日までの経過日数や経過月数を計算するのではなく、経過年数を計算するものです。
以下、コードを順を追って説明します。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates(): df_receipt DataFrameからcustomer_idとsales_ymdという列のみを選択し、重複する行をすべて削除してdf_tmpという新しいデータフレームを作成します。この結果、顧客IDと売上日の組み合わせが一意になるDataFrameが出来上がります。
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']], how='inner', on='customer_id'): df_tmpとdf_customer DataFrameの内側結合を行い、df_customer DataFrameから「customer_id」列と「application_date」列のみ選択します。結果として得られるDataFrameには、df_tmpとdf_customerの両方に存在する顧客IDを持つ行のみが含まれることになります。
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str')): df_tmp の "sales_ymd" 列を pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date']): df_tmp の "application_date" カラムを pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['elapsed_years'] = df_tmp[['sales_ymd', 'application_date']]. \ Γ apply(lambda x: relativedelta(x[0], x[1]).years, axis=1): "sales_ymd" と "application_date" 列の差を計算し、その結果を df_tmp の新しい列 "elapsed_years" に代入します。この計算では、dateutilライブラリのrelativedelta関数を使用して、2つのdatetime値の差を年単位で計算する。apply()メソッドは、"sales_ymd "と "application_date "列の各行にこの関数を適用し、総経過年数を返す。
df_tmp.head(10): df_tmpの最初の10行を表示し、計算が正しく行われたことを確認します。
全体として、このコードは、顧客の行動を分析し、顧客がメンバーシップやアカウントを申し込んでから購入するまでの期間を経過年数で判断するのに便利である。
P-073: レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、顧客データ(df_customer)の会員申込日(application_date)からのエポック秒による経過時間を計算し、顧客ID(customer_id)、売上日、会員申込日とともに10件表示せよ(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。なお、時間情報は保有していないため各日付は0時0分0秒を表すものとする。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates()
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']],
how='inner', on='customer_id')
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'])
df_tmp['elapsed_epoch'] = df_tmp['sales_ymd'].view(np.int64) - \
df_tmp['application_date'].view(np.int64)
df_tmp['elapsed_epoch'] = df_tmp['elapsed_epoch'] / 10**9
df_tmp.head(10)
customer_id | sales_ymd | application_date | elapsed_epoch | |
---|---|---|---|---|
0 | CS006214000001 | 2018-11-03 | 2015-02-01 | 118454400.0 |
1 | CS006214000001 | 2017-05-09 | 2015-02-01 | 71539200.0 |
2 | CS006214000001 | 2017-06-08 | 2015-02-01 | 74131200.0 |
3 | CS006214000001 | 2018-10-28 | 2015-02-01 | 117936000.0 |
4 | CS006214000001 | 2019-09-08 | 2015-02-01 | 145152000.0 |
5 | CS006214000001 | 2018-01-31 | 2015-02-01 | 94608000.0 |
6 | CS006214000001 | 2017-07-05 | 2015-02-01 | 76464000.0 |
7 | CS006214000001 | 2018-11-10 | 2015-02-01 | 119059200.0 |
8 | CS006214000001 | 2019-04-10 | 2015-02-01 | 132105600.0 |
9 | CS006214000001 | 2019-06-01 | 2015-02-01 | 136598400.0 |
解説:
このコードは、datetimeの値をUnixタイムスタンプに変換することで、顧客の申込日と購入日の間の経過時間(秒)を計算するものです。
以下は、コードのステップバイステップです。
df_tmp = df_receipt[['customer_id', 'sales_ymd']].drop_duplicates(): df_receipt DataFrameから "customer_id" と "sales_ymd" 列のみを選択して、df_tmpという新しい DataFrameを作成し、重複する行は削除しています。この結果、顧客IDと売上日の組み合わせが一意になるDataFrameが出来上がります。
df_tmp = pd.merge(df_tmp, df_customer[['customer_id', 'application_date']], how='inner', on='customer_id'): df_tmpとdf_customer DataFrameの内側結合を行い、df_customer DataFrameから「customer_id」列と「application_date」列のみ選択します。結果として得られるDataFrameには、df_tmpとdf_customerの両方に存在する顧客IDを持つ行のみが含まれることになります。
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str')): df_tmp の "sales_ymd" 列を pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date']): df_tmp の "application_date" カラムを pd.to_datetime() 関数を使用して datetime 形式に変換する。
df_tmp['elapsed_epoch'] = df_tmp['sales_ymd'].view(np.int64) - \ df_tmp['application_date'].view(np.int64): "sales_ymd" と "application_date" 列の差を計算して、その結果を df_tmp の "elapsed_epoch" 列に割り当て ます。この計算では、view()メソッドでdatetimeの値をint64形式(Unixタイムスタンプ)に変換し、2つの値を要素ごとに減算してナノ秒単位の経過時間を求めています。
df_tmp['elapsed_epoch'] = df_tmp['elapsed_epoch'] / 10**9: "elapsed_epoch" 列を10^9で割って、ナノ秒から秒に経過時間を変換する。
df_tmp.head(10): df_tmpの最初の10行を表示し、計算が正しく行われたことを確認します。
全体として、このコードは、顧客の行動を分析し、顧客がメンバーシップやアカウントを申し込んでから購入するまでの時間を、経過秒数で判断するのに便利である。
P-074: レシート明細データ(df_receipt)の売上日(sales_ymd)に対し、当該週の月曜日からの経過日数を計算し、売上日、直前の月曜日付とともに10件表示せよ(sales_ymdは数値でデータを保持している点に注意)。
df_tmp = df_receipt[['sales_ymd']].copy()
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['elapsed_days'] = df_tmp['sales_ymd'].apply(lambda x:x.weekday())
df_tmp['monday'] = \
df_tmp['sales_ymd'].apply(lambda x: x - relativedelta(days=x.weekday()))
df_tmp.head(10)
sales_ymd | elapsed_days | monday | |
---|---|---|---|
0 | 2018-11-03 | 5 | 2018-10-29 |
1 | 2018-11-18 | 6 | 2018-11-12 |
2 | 2017-07-12 | 2 | 2017-07-10 |
3 | 2019-02-05 | 1 | 2019-02-04 |
4 | 2018-08-21 | 1 | 2018-08-20 |
5 | 2019-06-05 | 2 | 2019-06-03 |
6 | 2018-12-05 | 2 | 2018-12-03 |
7 | 2019-09-22 | 6 | 2019-09-16 |
8 | 2017-05-04 | 3 | 2017-05-01 |
9 | 2019-10-10 | 3 | 2019-10-07 |
解説:
このコードは、以下のステップを実行しています。
既存のDataFrame df_receiptからsales_ymd列を選択し、その列のコピーを作成することで、df_tmpという新しいDataFrameを作成しています。
次に、df_tmp の sales_ymd カラムは、pd.to_datetime 関数を使用して pandas の datetime 形式に変換されます。これにより、曜日などの情報を抽出できるようになるなど、より柔軟な方法で日付を扱うことができるようになります。
df_tmpには、applyメソッドを使ってelapsed_daysという新しいカラムが作成されています。このメソッドは、sales_ymd列の各行にラムダ関数を適用し、この場合は曜日を整数値で返します(月曜日が0、日曜日が6です)。
df_tmpには、applyメソッドと別のラムダ関数を使用して、mondayという新しい列が作成されます。この関数は、sales_ymd列の各行を受け取り、週の初めからの経過日数(x.weekday()から得られる)を引き、その結果の日付を直近の月曜日に切り捨てたものを返します。これにより、df_tmp内のすべての日付が、月曜日を始点とする週単位に効果的にグループ化されます。
最後に、headメソッドがdf_tmp上で呼び出され、結果のDataFrameの最初の10行が表示されます。
全体として、このコードは、販売日の列を処理して、分析または集計の目的で有用な情報を抽出しています。具体的には、DataFrameに曜日と対応する週の開始日を表す新しい列を作成し、週や曜日ごとに売上データをグループ化したり集計したりするために使用することができます。
P-075: 顧客データ(df_customer)からランダムに1%のデータを抽出し、先頭から10件表示せよ。
df_customer.sample(frac=0.01).head(10)
customer_id | customer_name | gender_cd | gender | birth_day | age | postal_cd | address | application_store_cd | application_date | status_cd | |
---|---|---|---|---|---|---|---|---|---|---|---|
15707 | CS003702000032 | 松山 充則 | 0 | 男性 | 1943-12-24 | 75 | 214-0001 | 神奈川県川崎市多摩区菅********** | S13003 | 20160422 | 0-00000000-0 |
13320 | CS014712000023 | 大山 夏空 | 1 | 女性 | 1940-11-02 | 78 | 264-0024 | 千葉県千葉市若葉区高品町********** | S12014 | 20150630 | 0-00000000-0 |
2995 | CS028402000059 | 山口 寛治 | 0 | 男性 | 1970-06-07 | 48 | 245-0009 | 神奈川県横浜市泉区新橋町********** | S14028 | 20150508 | 0-00000000-0 |
5927 | CS007313000191 | 梅沢 麻緒 | 1 | 女性 | 1984-12-29 | 34 | 285-0858 | 千葉県佐倉市ユーカリが丘********** | S12007 | 20180913 | 0-00000000-0 |
20923 | CS017713000004 | 川瀬 未華子 | 1 | 女性 | 1947-12-12 | 71 | 166-0003 | 東京都杉並区高円寺南********** | S13017 | 20141218 | 0-00000000-0 |
13579 | CS004412000576 | 重松 真帆 | 1 | 女性 | 1970-05-29 | 48 | 167-0051 | 東京都杉並区荻窪********** | S13004 | 20170501 | 0-00000000-0 |
5845 | CS025512000044 | 上村 菜々美 | 1 | 女性 | 1963-03-08 | 56 | 242-0014 | 神奈川県大和市上和田********** | S14025 | 20150222 | 3-20080930-4 |
21563 | CS001412000408 | 古沢 まさみ | 1 | 女性 | 1976-08-13 | 42 | 210-0852 | 神奈川県川崎市川崎区鋼管通********** | S13001 | 20160102 | 0-00000000-0 |
8983 | CS004313000089 | 志水 陽子 | 1 | 女性 | 1983-07-06 | 35 | 176-0014 | 東京都練馬区豊玉南********** | S13004 | 20151010 | 0-00000000-0 |
703 | CS018512000192 | 寺西 郁恵 | 1 | 女性 | 1965-09-29 | 53 | 204-0002 | 東京都清瀬市旭が丘********** | S13018 | 20180528 | 0-00000000-0 |
解説:
このコードは、df_customerというpandasのDataFrameから行のサブセットをサンプリングし、そのサブセットの最初の10行を表示するものです。
以下は、コードの各パートが行っていることです。
df_customerは、顧客データを含むpandasのDataFrameで、おそらく多くの行と列を持つ。
sampleメソッドは、df_customerに対して、frac=0.01という引数で呼び出されます。このメソッドは、DataFrameの行の一部をランダムに選択します。ここで、fracは、サンプリングする行の割合を表します。この場合、frac=0.01は、df_customerの行の1%が無作為に選択されることを意味します。
headメソッドは、df_customerの結果のサブセットに対して、引数10で呼び出されます。このメソッドは、サブセットの最初の10行を返し、それが表示されます。
つまり、このコードは全体として、df_customerから行の1%のランダムなサンプルを選択し、そのサンプルの最初の10行を表示しています。これは、データセット全体を操作することなく、データの小さなサブセットに対してデータを探索したり、迅速な分析を実行したりするのに便利です。
P-076: 顧客データ(df_customer)から性別コード(gender_cd)の割合に基づきランダムに10%のデータを層化抽出し、性別コードごとに件数を集計せよ。
# sklearn.model_selection.train_test_splitを使用した例
_, df_tmp = train_test_split(df_customer, test_size=0.1,
stratify=df_customer['gender_cd'])
df_tmp.groupby('gender_cd').agg({'customer_id' : 'count'})
customer_id | |
---|---|
gender_cd | |
0 | 298 |
1 | 1793 |
9 | 107 |
解説:
このコードでは、以下のステップを実行します。
train_test_splitはscikit-learnライブラリの関数で、データセットをランダムに2つのサブセット(「トレーニング」サブセットと「テスト」サブセット)に分割するものです。このコードでは、df_customerというpandasのDataFrameに適用し、引数test_size=0.1でデータの10%をテスト用に確保することを指定し、引数stratify=df_customer['gender_cd']でgender_cd列の値で階層化することを指定しました。この関数は2つのDataFrameを返しますが、最初のDataFrameは無視されます(_が代入されます、これはPythonの慣習で、使用される予定のない変数を表す)。
train_test_splitが返す2番目のDataFrameは、df_tmpという新しい変数に代入されます。
df_tmp に対して、groupby メソッドが gender_cd という引数で呼ばれます。このメソッドは、df_tmpの行をgender_cd列の値でグループ化する。
aggメソッドは、groupby操作の結果に対して、{'customer_id' : 'count'}という引数で呼び出される。このメソッドは、各グループのcustomer_idカラムに関数(ここではcount)を適用し、各グループの結果のカウントを持つDataFrameを返します。
出来上がったDataFrameは、表示されます。
つまり、このコードは、全体として、pandasのDataFrameを訓練用とテスト用にランダムに分割し、特定の列(gender_cd)の値によって層別化しています。そして、テスト用サブセットから新しいDataFrameを作成し、gender_cd列でグループ化し、各グループのcustomer_id列のユニークな値のカウントを計算することができます。これは、テスト用サブセットのデータにおける性別による顧客の分布を把握するのに便利です。
P-077: レシート明細データ(df_receipt)の売上金額を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。なお、外れ値は売上金額合計を対数化したうえで平均と標準偏差を計算し、その平均から3σを超えて離れたものとする(自然対数と常用対数のどちらでも可)。結果は10件表示せよ。
df_sales_amount = df_receipt.groupby('customer_id'). \
agg({'amount':'sum'}).reset_index()
df_sales_amount['log_sum_amount'] = np.log(df_sales_amount['amount'] + 0.5)
df_sales_amount['log_sum_amount_ss'] = preprocessing.scale(df_sales_amount['log_sum_amount'])
df_sales_amount.query('abs(log_sum_amount_ss) > 3').head(10)
customer_id | amount | log_sum_amount | log_sum_amount_ss | |
---|---|---|---|---|
8306 | ZZ000000000000 | 12395003 | 16.332804 | 7.967822 |
解説:
このコードは、df_receiptという名前のpandas DataFrameオブジェクトに対して、データの前処理とフィルタリングを実行しています。
以下、コードのステップバイステップの説明です。
df_sales_amount = df_receipt.groupby('customer_id').agg({'amount':'sum'}).reset_index(): この行は、df_receiptの行をcustomer_id列の値でグループ化し、各グループのamount列の合計を算出しています。結果として得られるDataFrame df_sales_amountは、一意のcustomer_id値ごとに1行、customer_idとamountの2列を持ちます。reset_index()メソッドを使用して、グループ化後のインデックスを整数値にリセットしています。
df_sales_amount['log_sum_amount'] = np.log(df_sales_amount['amount'] + 0.5): この行は、df_sales_amountにlog_sum_amountという新しいカラムを作成し、各顧客の金額の合計の自然対数、および0の対数を取るのを避けるために0.5を加えた値を格納します。
df_sales_amount['log_sum_amount_ss'] = preprocessing.scale(df_sales_amount['log_sum_amount']): この行は、df_sales_amountにlog_sum_amount_ssという新しい列を作り、scikit-learnライブラリのpreprocessing.scale関数を使ってlog_sum_amountをスケールした値を格納しています。この関数は、入力配列の平均を引き、標準偏差で割ることで、平均がゼロで分散が単位の新しい配列になる。
df_sales_amount.query('abs(log_sum_amount_ss) > 3').head(10): この行は、log_sum_amount_ssの絶対値が3より大きい行だけを残すようにdf_sales_amountをフィルタリングし、結果のDataFrameの最初の10行を選択する。このフィルターでは、売上総額の平均値からの標準偏差が3以上離れている顧客を選択し、異常値である可能性を示しています。queryメソッドを使用して、文字列式(この場合は「abs(log_sum_amount_ss) > 3」)に基づいて行をフィルタリングしています。
P-078: レシート明細データ(df_receipt)の売上金額(amount)を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。なお、ここでは外れ値を第1四分位と第3四分位の差であるIQRを用いて、「第1四分位数-1.5×IQR」を下回るもの、または「第3四分位数+1.5×IQR」を超えるものとする。結果は10件表示せよ。
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")',
engine='python'). \
groupby('customer_id'). \
agg({'amount':'sum'}).reset_index()
pct25 = np.percentile(df_sales_amount['amount'], q=25)
pct75 = np.percentile(df_sales_amount['amount'], q=75)
iqr = pct75 - pct25
amount_low = pct25 - (iqr * 1.5)
amount_hight = pct75 + (iqr * 1.5)
df_sales_amount.query('amount < @amount_low or @amount_hight < amount').head(10)
customer_id | amount | |
---|---|---|
98 | CS001414000048 | 8584 |
332 | CS001605000009 | 18925 |
549 | CS002415000594 | 9568 |
1180 | CS004414000181 | 9584 |
1558 | CS005415000137 | 8734 |
1733 | CS006414000001 | 9156 |
1736 | CS006414000029 | 9179 |
1752 | CS006415000105 | 10042 |
1755 | CS006415000147 | 12723 |
1757 | CS006415000157 | 10648 |
解説:
このコードは、df_receiptという名前のpandas DataFrameオブジェクトに対して、データの前処理とフィルタリングを実行しています。
以下、コードのステップバイステップの説明です。
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').agg({'amount':'sum'}).reset_index(): この行は、まずdf_receiptをフィルタリングして、customer_id列が文字「Z」で始まる行を除外しています。queryメソッドは、文字列式に基づいて行をフィルタリングするために使用されます。engine='python'引数は、クエリにPythonエンジンを使用することを指定するために使用され、これによりstr.startswithメソッドを使用することができます。得られたDataFrameは、customer_idでグループ化され、グループごとに金額列の合計が算出されます。結果として得られるDataFrame df_sales_amountは、一意のcustomer_id値ごとに1行、customer_idとamountの2つのカラムを持つ。reset_index()メソッドを使用して、グループ化後のインデックスを整数値にリセットしています。
pct25 = np.percentile(df_sales_amount['amount'], q=25): この行は、numpy.percentile関数を使用してdf_sales_amountのamount列の25番目のパーセンタイルを計算し、その結果を変数pct25に代入しています。
pct75 = np.percentile(df_sales_amount['amount'], q=75): この行は、numpy.percentile関数を使用してdf_sales_amountのamount列の75パーセンタイルを計算し、その結果を変数pct75に代入しています。
iqr = pct75 - pct25: この行は、75パーセンタイル(pct75)から25パーセンタイル(pct25)を引くことによって、df_sales_amountの金額列の四分位範囲(IQR)を計算し、その結果を変数iqrに代入しています。
amount_low = pct25 - (iqr * 1.5): この行は、amount_low = pct25 - (iqr * 1.5)という式を使って、売上金額の「通常」範囲の下限を計算する。これは一般的に箱型プロットの下側の「ひげ」として知られています。
amount_high = pct75 + (iqr * 1.5)。この行は、amount_high = pct75 + (iqr * 1.5)という式を使って、売上金額の「通常」範囲の上限を計算します。これは一般に箱ひげ図における上限の「ひげ」として知られている。
df_sales_amount.query('amount < @amount_low or @amount_high < amount').head(10): この行は、amount列がamount_lowより小さいかamount_highより大きい行だけを残すようにdf_sales_amountをフィルタリングし、結果のDataFrameの最初の10行を選択する。このフィルタは、売上金額の合計が売上金額の「正常な」範囲外にある顧客を選択するもので、ボックスプロットの下ひげと上ひげの間の範囲と定義されています。Pythonの変数amount_lowとamount_highを参照するために@記号を使用した文字列式に基づいて行をフィルタリングするために、queryメソッドが使用されています。
P-079: 商品データ(df_product)の各項目に対し、欠損数を確認せよ。
df_product.isnull().sum()
product_cd 0 category_major_cd 0 category_medium_cd 0 category_small_cd 0 unit_price 7 unit_cost 7 dtype: int64
解説:
このコードは、pandasのDataFrame df_productの各列の欠損(null)値の数を計算するために使用されます。以下、ステップバイステップで説明します。
df_product: これは、使用されている DataFrame の名前です。
isnull(): このメソッドは、df_product と同じ形状の DataFrame を返し、各要素は df_product の対応する要素が null かどうかを示す boolean 値です。
sum():isnull()が返すboolean DataFrameに適用され、各列のboolean値の和を返すメソッド。boolean 値は整数として扱われるので、これは事実上 df_product の各列の null 値の数を数えることになります。
したがって、df_product.isnull().sum()は、インデックスがdf_productの列名、値が各列のNULL値の数であるSeriesオブジェクトを返す。これは、どの列に欠損値があり、各列に何個の欠損値があるかを特定するために使用することができます。
P-080: 商品データ(df_product)のいずれかの項目に欠損が発生しているレコードを全て削除した新たな商品データを作成せよ。なお、削除前後の件数を表示させ、079で確認した件数だけ減少していることも確認すること。
df_product_1 = df_product.copy()
df_product_1.dropna(inplace=True)
print('削除前:', len(df_product))
print('削除後:', len(df_product_1))
削除前: 10030 削除後: 10023
解説:
このコードは、pandasのDataFrame df_productからNULL(欠損)値を含むすべての行を削除し、NULLでない行だけを含む新しいDataFrame df_product_1を作成するために使用されます。以下、ステップバイステップで説明します。
df_product: これは、使用する元のDataFrameの名前です。
df_product.copy(): このメソッドは、元のDataFrameのコピーを作成し、元のDataFrameが変更されないようにします。
df_product_1: 作成される新しいDataFrameの名前です。
dropna()。このメソッドは、df_product_1 から null 値を含むすべての行を削除するために使用される。inplace=True 引数は、新しい DataFrame を返すのではなく、その場で df_product_1 を変更するために使用されます。
print('Before deletion:', len(df_product)): この行は、ヌル値を削除する前の元のDataFrame df_productの行数を表示します。
print('After deletion:', len(df_product_1)): この行は、ヌル値が削除された後の新しいDataFrame df_product_1の行数を表示しています。
したがって、このコードでは、まず元のDataFrame df_productのコピーを作成し、コピーしたDataFrameからNULL値を含むすべての行を削除して新しいDataFrame df_product_1を作成し、最後にNULL値の削除前と削除後の元のDataFrameと新しいDataFrame内の行数を表示する。このコードは、ヌル値のために削除された行数を特定し、ヌル値を含まないクリーンなDataFrameを作成するために使用することができます。
Comment