【解説】データサイエンス100本ノック【問70〜74 回答】

【解説】データサイエンス100本ノック【問70〜74 回答】



目次

データサイエンス100本ノックの始め方は、以下の記事を参考にしていただければと思います。


>>データサイエンス100本ノックの始め方を確認する


第70問目: 経過日数の計算

P-070: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、顧客データフレーム(df_customer)の会員申込日(application_date)からの経過日数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに表示せよ。結果は10件表示させれば良い(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。

本問は時間の計算について問われています。

時間の計算は、データを日付型に変換する必要があります。

日付型への変換は、pandas.to_datetime関数を使用すれば良いです。

変換を行った日付型同士では、引き算などの計算が行なえます。

変換元のデータ型毎に、日付型に変換する方法を以下の記事で紹介しているので、こちらも参照してください。

>> 【解説】日付データの変換方法を学ぶ | データサイエンス100本ノック【問45〜問48 回答】

本問を解きながら、使い方を学んでいきましょう。

まずはdf_receiptdf_customerの構造を確認します。

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_receiptの売上日とdf_customerの会員申込日を内部結合を行うことで抽出します。

1
2
df_tmp = pd.merge(df_receipt[['customer_id', 'sales_ymd']], df_customer[['customer_id', 'application_date']], how='inner', on='customer_id')
df_tmp.head(20)
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	customer_id	sales_ymd	application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
3 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
5 CS006214000001 20181028 20150201
6 CS006214000001 20170509 20150201
7 CS006214000001 20190908 20150201
8 CS006214000001 20180131 20150201
9 CS006214000001 20170705 20150201
10 CS006214000001 20181110 20150201
11 CS006214000001 20170705 20150201
12 CS006214000001 20190410 20150201
13 CS006214000001 20180131 20150201
14 CS006214000001 20181103 20150201
15 CS006214000001 20190601 20150201
16 CS006214000001 20190908 20150201
17 CS006214000001 20190507 20150201
18 CS006214000001 20181110 20150201
19 CS006214000001 20190507 20150201

出力した結果を見ると、重複している行が確認されますので、重複した行を削除します。

重複した行の削除は以下のコードで削除可能です。

1
2
df_tmp = df_tmp.drop_duplicates()
df_tmp.head(20)
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

customer_id sales_ymd application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
7 CS006214000001 20190908 20150201
8 CS006214000001 20180131 20150201
9 CS006214000001 20170705 20150201
10 CS006214000001 20181110 20150201
12 CS006214000001 20190410 20150201
15 CS006214000001 20190601 20150201
17 CS006214000001 20190507 20150201
22 CS008415000097 20181118 20150322
23 CS008415000097 20170328 20150322
24 CS008415000097 20180325 20150322
28 CS008415000097 20190417 20150322
30 CS028414000014 20170712 20150711
31 CS028414000014 20180408 20150711
32 CS028414000014 20180527 20150711
33 CS028414000014 20180811 20150711
34 CS028414000014 20191023 20150711

次にsales_ymdapplication_dateを日付型に変換します。

日付型への変更方法は以下の記事にまとめているので参照してください。

>> 【解説】日付データの変換方法を学ぶ | データサイエンス100本ノック【問45〜問48 回答】

1
2
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'], format='%Y%m%d')
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 2018-11-03 20150201
1 CS006214000001 2017-05-09 20150201
2 CS006214000001 2017-06-08 20150201
4 CS006214000001 2018-10-28 20150201
7 CS006214000001 2019-09-08 20150201
... ... ... ...
65672 CS004411000027 2017-06-01 20150517
65674 CS040513000029 2018-06-07 20150915
65676 CS004613000146 2017-12-02 20160813
65678 CS002314000037 2018-04-21 20151202
65680 CS040311000022 2019-04-16 20150419
32411 rows × 3 columns

同様にapplication_dateを日付型に変換します。

1
2
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'], format='%Y%m%d')
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 2018-11-03 2015-02-01
1 CS006214000001 2017-05-09 2015-02-01
2 CS006214000001 2017-06-08 2015-02-01
4 CS006214000001 2018-10-28 2015-02-01
7 CS006214000001 2019-09-08 2015-02-01
... ... ... ...
65672 CS004411000027 2017-06-01 2015-05-17
65674 CS040513000029 2018-06-07 2015-09-15
65676 CS004613000146 2017-12-02 2016-08-13
65678 CS002314000037 2018-04-21 2015-12-02
65680 CS040311000022 2019-04-16 2015-04-19
32411 rows × 3 columns

sales_ymdapplication_dateの両者を日付型に変換できたので、あとは両データの差分をとり経過日数を計算し、新たなカラムであるelapsed_dateに格納します。

1
2
df_tmp['elapsed_date'] = df_tmp['sales_ymd'] - df_tmp['application_date']
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	sales_ymd	application_date	elapsed_date
0 CS006214000001 2018-11-03 2015-02-01 1371 days
1 CS006214000001 2017-05-09 2015-02-01 828 days
2 CS006214000001 2017-06-08 2015-02-01 858 days
4 CS006214000001 2018-10-28 2015-02-01 1365 days
7 CS006214000001 2019-09-08 2015-02-01 1680 days
8 CS006214000001 2018-01-31 2015-02-01 1095 days
9 CS006214000001 2017-07-05 2015-02-01 885 days
10 CS006214000001 2018-11-10 2015-02-01 1378 days
12 CS006214000001 2019-04-10 2015-02-01 1529 days
15 CS006214000001 2019-06-01 2015-02-01 1581 days

これで完成です。

第71問目: 経過月数の算出

P-071: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、顧客データフレーム(df_customer)の会員申込日(application_date)からの経過月数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに表示せよ。結果は10件表示させれば良い(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1ヶ月未満は切り捨てること。

前の問題と異なり「経過月数」の計算を求められています。

月単位の演算を行う場合は、dateutil.relativedeltaモジュールを使用します。

以下に簡単な使い方の例を示します。

1
2
3
4
5
from datetime import datetime
from dateutil.relativedelta import relativedelta

result = datetime(2020, 12, 1) + relativedelta(months=3)
result
出力
1
datetime.datetime(2021, 3, 1, 0, 0)

また、以下のようにrelativedelta(datetime1, datetime2)とすると、datetime1からdatetime2への経過時間を取得することが出来ます。

1
2
3
4
5
6
7
from datetime import datetime
from dateutil.relativedelta import relativedelta

datetime1 = datetime(2020, 12,1 )
datetime2 = datetime(2019, 2, 1)

relativedelta(datetime1, datetime2)
出力
1
relativedelta(years=+1, months=+10)

上記の出力では、1年と10ヶ月が経過していることが示されています。

「1年と10ヶ月」ではなく「22ヶ月」という形式で出力させる場合は、以下のようにすればOKです。

1
2
months = relativedelta(datetime1, datetime2).years * 12 + relativedelta(datetime1, datetime2).months
print("経過月数: {}".format(months))
出力
1
経過月数: 22

これらのノウハウを用いて本問を解いていきましょう。

前問と同様に、df_receiptとdf_customerの必要なカラムのみで内部結合させていきます。

1
2
df_tmp = pd.merge(df_receipt[['customer_id', 'sales_ymd']], df_customer[['customer_id', 'application_date']], how='inner', on='customer_id')
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	sales_ymd	application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
3 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
5 CS006214000001 20181028 20150201
6 CS006214000001 20170509 20150201
7 CS006214000001 20190908 20150201
8 CS006214000001 20180131 20150201
9 CS006214000001 20170705 20150201

重複している行が存在しているようなので、drop_duplicates()で重複している行を削除します。

1
df_tmp = df_tmp.drop_duplicates()

sales_ymdapplication_dateそれぞれを、日付型に変換します。

変換方法は、以下を参照してください。

>> 【解説】日付データの変換方法を学ぶ | データサイエンス100本ノック【問45〜問48 回答】

1
2
3
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'], format='%Y%m%d')
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'], format='%Y%m%d')
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	sales_ymd	application_date
0 CS006214000001 2018-11-03 2015-02-01
1 CS006214000001 2017-05-09 2015-02-01
2 CS006214000001 2017-06-08 2015-02-01
4 CS006214000001 2018-10-28 2015-02-01
7 CS006214000001 2019-09-08 2015-02-01
8 CS006214000001 2018-01-31 2015-02-01
9 CS006214000001 2017-07-05 2015-02-01
10 CS006214000001 2018-11-10 2015-02-01
12 CS006214000001 2019-04-10 2015-02-01
15 CS006214000001 2019-06-01 2015-02-01

冒頭に紹介した例の用を参考に、経過月数を求めていきます。

ここでは復習も兼ねてapplyとlambda式を使って算出します。

>> 【lambda式とapplyを復習】【データサイエンス】Pythonで最頻値を表示する方法 | データサイエンス100本ノック【問29 回答】

1
2
df_tmp['elapsed_date'] = df_tmp[['sales_ymd', 'application_date']].apply(lambda x: relativedelta(x[0], x[1]).years * 12 + relativedelta(x[0], x[1]).months)
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
14

customer_id sales_ymd application_date elapsed_date
0 CS006214000001 2018-11-03 2015-02-01 NaN
1 CS006214000001 2017-05-09 2015-02-01 NaN
2 CS006214000001 2017-06-08 2015-02-01 NaN
4 CS006214000001 2018-10-28 2015-02-01 NaN
7 CS006214000001 2019-09-08 2015-02-01 NaN
... ... ... ... ...
65672 CS004411000027 2017-06-01 2015-05-17 NaN
65674 CS040513000029 2018-06-07 2015-09-15 NaN
65676 CS004613000146 2017-12-02 2016-08-13 NaN
65678 CS002314000037 2018-04-21 2015-12-02 NaN
65680 CS040311000022 2019-04-16 2015-04-19 NaN
32411 rows × 4 columns

elapsed_dateがすべてNaNになってしまいました。

原因を探っていきます。

relativedeltaで経過年数を算出する部分だけ抜き取ってコードを実行してみます。

1
df_tmp[['sales_ymd', 'application_date']].apply(lambda x: relativedelta(x[0], x[1]).years)
出力
1
2
3
sales_ymd           1
application_date 0
dtype: int64

本来であれば、6万ほどあるsales_ymdapplication_dateの経過年数を出してくれるはずなのですが、一つしか出力されませんでした。

これは、applyメソッドがデフォルトで縦方向のデータ間のrelativedeltaの計算をしているためです。言い換えるとapplyメソッドはデフォルトでaxis=0を指定しており、sales_ymd列間の経過年数の計算をしてしまっているのです。

上記のコードの場合x[0]がsales_ymdの0番目である2018-11-03というデータを抽出し、x[1]がsales_ymdの1番目である2017-05-09というデータを取得して経過年数を算出してしまうのです。

以下のようにaxis=1を指定すれば、各レコードにおけるsales_ymdapplication_date間の経過年数を算出することが出来ます。

1
df_tmp[['sales_ymd', 'application_date']].apply(lambda x: relativedelta(x[0], x[1]).years, axis=1)
出力
1
2
3
4
5
6
7
8
9
10
11
12
0        3
1 2
2 2
4 3
7 4
..
65672 2
65674 2
65676 1
65678 2
65680 3
Length: 32411, dtype: int64

これを踏まえ、コードを修正します。

修正は簡単でaxis=1を指定するだけです。

1
2
df_tmp['elapsed_date'] = 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)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	sales_ymd	application_date	elapsed_date
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
4 CS006214000001 2018-10-28 2015-02-01 44
7 CS006214000001 2019-09-08 2015-02-01 55
8 CS006214000001 2018-01-31 2015-02-01 35
9 CS006214000001 2017-07-05 2015-02-01 29
10 CS006214000001 2018-11-10 2015-02-01 45
12 CS006214000001 2019-04-10 2015-02-01 50
15 CS006214000001 2019-06-01 2015-02-01 52

第72問目: 経過年数の算出

P-072: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、顧客データフレーム(df_customer)の会員申込日(application_date)からの経過年数を計算し、顧客ID(customer_id)、売上日、会員申込日とともに表示せよ。結果は10件表示させれば良い。(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。1年未満は切り捨てること。

経過年数を求める場合経過月数を求める場合と同様に、dateutil.relativedeltaモジュールを使用します。

早速問題を解きながら使い方を学んでいきましょう。

まずはdf_receiptの構造を確認します。

1
df_receipt.head(5)
出力
1
2
3
4
5
6
	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
7

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

まずは、df_receiptdf_customerという2つのデータフレームから、今回の問題を解く上で必要となるカラムだけを抽出したいと思います。

今回の問題を解く上で、必要となるカラムは、customer_id, sales_ymd, application_dateですね。

df_receiptdf_customerの間で共通的なカラムとしてcustomer_idが存在しますので、mergeを用いて内部結合で新たなデータフレームdf_tmpを作成します。

1
2
df_tmp = pd.merge(df_receipt[['customer_id', 'sales_ymd']], df_customer[['customer_id', 'application_date']], how='inner', on='customer_id')
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
14

customer_id sales_ymd application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
3 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
... ... ... ...
65677 CS004613000146 20171202 20160813
65678 CS002314000037 20180421 20151202
65679 CS002314000037 20180421 20151202
65680 CS040311000022 20190416 20150419
65681 CS040311000022 20190416 20150419
65682 rows × 3 columns

重複した行を削除します。

1
2
df_tmp = df_tmp.drop_duplicates()
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
7 CS006214000001 20190908 20150201
... ... ... ...
65672 CS004411000027 20170601 20150517
65674 CS040513000029 20180607 20150915
65676 CS004613000146 20171202 20160813
65678 CS002314000037 20180421 20151202
65680 CS040311000022 20190416 20150419
32411 rows × 3 columns

経過年数を算出するためには、それぞれのデータを日付型に変換する必要があります。

sales_ymdapplication_datepandas.to_datetimeを用いて日付型に変換します。

1
2
3
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'], format='%Y%m%d')
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'], format='%Y%m%d')
df_tmp
出力
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
<ipython-input-24-755911942cfb>:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'], format='%Y%m%d')
<ipython-input-24-755911942cfb>:2: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'], format='%Y%m%d')
customer_id sales_ymd application_date
0 CS006214000001 2018-11-03 2015-02-01
1 CS006214000001 2017-05-09 2015-02-01
2 CS006214000001 2017-06-08 2015-02-01
4 CS006214000001 2018-10-28 2015-02-01
7 CS006214000001 2019-09-08 2015-02-01
... ... ... ...
65672 CS004411000027 2017-06-01 2015-05-17
65674 CS040513000029 2018-06-07 2015-09-15
65676 CS004613000146 2017-12-02 2016-08-13
65678 CS002314000037 2018-04-21 2015-12-02
65680 CS040311000022 2019-04-16 2015-04-19
32411 rows × 3 columns

この時、Warningが出力されます。

これはデータサイエンス100本ノックの52問目〜53問目 を解いていく際に出力されたWarningと同様のものです。

今回は本コードを実行した直前のコードを、df_tmp = df_tmp.drop_duplicates().copy()とすればWarningは出力されなくなります。

1
2
3
4
df_tmp = df_tmp.drop_duplicates().copy()
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'], format='%Y%m%d')
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'], format='%Y%m%d')
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
14

customer_id sales_ymd application_date
0 CS006214000001 2018-11-03 2015-02-01
1 CS006214000001 2017-05-09 2015-02-01
2 CS006214000001 2017-06-08 2015-02-01
4 CS006214000001 2018-10-28 2015-02-01
7 CS006214000001 2019-09-08 2015-02-01
... ... ... ...
65672 CS004411000027 2017-06-01 2015-05-17
65674 CS040513000029 2018-06-07 2015-09-15
65676 CS004613000146 2017-12-02 2016-08-13
65678 CS002314000037 2018-04-21 2015-12-02
65680 CS040311000022 2019-04-16 2015-04-19
32411 rows × 3 columns

このようにWarningが出力されなくなりました。

続いて、elapsed_dateという新しいカラムに、経過年数の結果を格納します。

経過年数の計算は、lambda式でateutil.relativedeltaモジュールを活用して算出します。

1
2
df_tmp['elapsed_date'] = df_tmp[['sales_ymd', 'application_date']].apply(lambda x: relativedelta(x[0], x[1]).years, axis=1)
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
12

customer_id sales_ymd application_date elapsed_date
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
4 CS006214000001 2018-10-28 2015-02-01 3
7 CS006214000001 2019-09-08 2015-02-01 4
8 CS006214000001 2018-01-31 2015-02-01 2
9 CS006214000001 2017-07-05 2015-02-01 2
10 CS006214000001 2018-11-10 2015-02-01 3
12 CS006214000001 2019-04-10 2015-02-01 4
15 CS006214000001 2019-06-01 2015-02-01 4

これで完成です。

第73問目: 経過時間の算出

P-073: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、顧客データフレーム(df_customer)の会員申込日(application_date)からのエポック秒による経過時間を計算し、顧客ID(customer_id)、売上日、会員申込日とともに表示せよ。結果は10件表示させれば良い(なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意)。なお、時間情報は保有していないため各日付は0時0分0秒を表すものとする。

日付型(datetime)をエポック秒にする場合、timestampメソッドを使用します。

1
2
3
4
5
from datetime import datetime

datetime_test = datetime(2021, 8, 10)

datetime_test.timestamp()
出力
1
1628553600.0

本問では、"2018-11-03"11"というデータ形式で格納されています。

Seriesでの方法としてデータの型変換をastypeを使用し、変換後の方としてint64型に変換するとナノ秒単位の値が得られます。

ナノ秒を秒に変換するには$10^{9}$で割ればOKです。

1
2
3
4
5
6
import pandas as pd
import numpy as np

data = pd.Series(["2021-8-10"])

pd.to_datetime(data).astype(np.int64)/ 10**9
出力
1
2
0    1.628554e+09
dtype: float64

ここまでの前提知識も踏まえて本問を解いていきたいと思います。

まずはdf_receiptとdf_customerにおいて必要なカラムのみを抽出して内部結合させます。

1
2
df_tmp = pd.merge(df_receipt[['customer_id', 'sales_ymd']], df_customer[['customer_id', 'application_date']], how='inner', on='customer_id')
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
3 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
... ... ... ...
65677 CS004613000146 20171202 20160813
65678 CS002314000037 20180421 20151202
65679 CS002314000037 20180421 20151202
65680 CS040311000022 20190416 20150419
65681 CS040311000022 20190416 20150419
65682 rows × 3 columns

重複した行を削除します。ここでcopy()を使用しないと、後々warningが出ます。

1
2
df_tmp = df_tmp.drop_duplicates().copy()
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 20181103 20150201
1 CS006214000001 20170509 20150201
2 CS006214000001 20170608 20150201
4 CS006214000001 20181028 20150201
7 CS006214000001 20190908 20150201
... ... ... ...
65672 CS004411000027 20170601 20150517
65674 CS040513000029 20180607 20150915
65676 CS004613000146 20171202 20160813
65678 CS002314000037 20180421 20151202
65680 CS040311000022 20190416 20150419
32411 rows × 3 columns

それぞれ日付型に変換をします。

1
2
3
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp['application_date'] = pd.to_datetime(df_tmp['application_date'].astype('str'))
df_tmp
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	sales_ymd	application_date
0 CS006214000001 2018-11-03 2015-02-01
1 CS006214000001 2017-05-09 2015-02-01
2 CS006214000001 2017-06-08 2015-02-01
4 CS006214000001 2018-10-28 2015-02-01
7 CS006214000001 2019-09-08 2015-02-01
... ... ... ...
65672 CS004411000027 2017-06-01 2015-05-17
65674 CS040513000029 2018-06-07 2015-09-15
65676 CS004613000146 2017-12-02 2016-08-13
65678 CS002314000037 2018-04-21 2015-12-02
65680 CS040311000022 2019-04-16 2015-04-19
32411 rows × 3 columns

エポック秒での経過時間を求めます。

1
2
df_tmp['elapsed_date'] = (df_tmp['sales_ymd'].astype(np.int64)/10**9) - (df_tmp['application_date'].astype(np.int64)/10**9)
df_tmp.head(10)
1
2
3
4
5
6
7
8
9
10
11
12

customer_id sales_ymd application_date elapsed_date
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
4 CS006214000001 2018-10-28 2015-02-01 117936000.0
7 CS006214000001 2019-09-08 2015-02-01 145152000.0
8 CS006214000001 2018-01-31 2015-02-01 94608000.0
9 CS006214000001 2017-07-05 2015-02-01 76464000.0
10 CS006214000001 2018-11-10 2015-02-01 119059200.0
12 CS006214000001 2019-04-10 2015-02-01 132105600.0
15 CS006214000001 2019-06-01 2015-02-01 136598400.0

これで完成です。

第74問目: 月曜日からの経過日数の計算

P-074: レシート明細データフレーム(df_receipt)の売上日(sales_ymd)に対し、当該週の月曜日からの経過日数を計算し、売上日、当該週の月曜日付とともに表示せよ。結果は10件表示させれば良い(なお、sales_ymdは数値でデータを保持している点に注意)。

日付型(datetime)のweekdayメソッドは曜日を0-6の数字として得ることができます。

また、このweekdayメソッドを使用することで、月曜日の日付を取得することができます。

以下にサンプルコードとともに方法を紹介します。

1
2
3
4
5
6
7
8
from datetime import datetime
from dateutil.relativedelta import relativedelta

datetime_test = datetime(2021, 8, 10)
date = datetime_test.weekday()

result = datetime_test - relativedelta(days=date)
result
出力
1
datetime.datetime(2021, 8, 9, 0, 0)

すなわち、2021年8月9日が月曜日であることがわかります。

これらの前提知識をもとに、本問を解いていきましょう。

まずは、必要なカラムのみを抽出します。本問における必要なカラムは、customer_idとsales_ymdとなります。

1
2
df_tmp = df_receipt[['customer_id', 'sales_ymd']]
df_tmp.head(5)
出力
1
2
3
4
5
6
	customer_id	sales_ymd
0 CS006214000001 20181103
1 CS008415000097 20181118
2 CS028414000014 20170712
3 ZZ000000000000 20190205
4 CS025415000050 20180821

重複している行は削除します。

1
df_tmp = df_tmp.drop_duplicates()

sales_ymdカラムのデータを日付型に変換します。

1
2
df_tmp['sales_ymd'] = pd.to_datetime(df_tmp['sales_ymd'].astype('str'))
df_tmp.head(5)
出力
1
2
3
4
5
6
	customer_id	sales_ymd
0 CS006214000001 2018-11-03
1 CS008415000097 2018-11-18
2 CS028414000014 2017-07-12
3 ZZ000000000000 2019-02-05
4 CS025415000050 2018-08-21

mondayカラムに各々のsales_ymdの日付における月曜日の日付を格納します
各日付における月曜日を算出し、経過日数をelapsed_weekdayカラムに格納して出力します。

1
2
df_tmp['monday'] = df_tmp['sales_ymd'].apply(lambda x: x - relativedelta(days=x.weekday()))
df_tmp.head(5)
出力
1
2
3
4
5
6
customer_id	sales_ymd	monday
0 CS006214000001 2018-11-03 2018-10-29
1 CS008415000097 2018-11-18 2018-11-12
2 CS028414000014 2017-07-12 2017-07-10
3 ZZ000000000000 2019-02-05 2019-02-04
4 CS025415000050 2018-08-21 2018-08-20

sales_ymdからmondayを引き算することで経過日数を算出し、elapsed_weekdayというカラムに代入して出力します

1
2
df_tmp['elapsed_weekday'] = df_tmp['sales_ymd'] - df_tmp['monday']
df_tmp.head(10)
出力
1
2
3
4
5
6
7
8
9
10
11
	customer_id	sales_ymd	monday	elapsed_weekday
0 CS006214000001 2018-11-03 2018-10-29 5 days
1 CS008415000097 2018-11-18 2018-11-12 6 days
2 CS028414000014 2017-07-12 2017-07-10 2 days
3 ZZ000000000000 2019-02-05 2019-02-04 1 days
4 CS025415000050 2018-08-21 2018-08-20 1 days
5 CS003515000195 2019-06-05 2019-06-03 2 days
6 CS024514000042 2018-12-05 2018-12-03 2 days
7 CS040415000178 2019-09-22 2019-09-16 6 days
8 ZZ000000000000 2017-05-04 2017-05-01 3 days
9 CS027514000015 2019-10-10 2019-10-07 3 days

これで完成です。

まとめ: 年月日、月曜からの経過時間の算出方法を学びました。

本記事は、「【解説】データサイエンス100本ノック【問70〜74 回答】」というテーマでまとめました。

本記事で紹介した方法を元にデータサイエンティストとしての知見を深めていただければと思います。

なお、データサイエンティストに必要な知識は、Udemyを活用した学習が効率的です。30日間の返金保証、および一流講師へのQ&Aシステムが整ったオンライン学習プラットフォームです。

不定期で90%以上の割引セールも行っているので無料会員登録だけでも実施しておくといいと思います。

世界で34万人が受講した以下の講座をご確認ください。

【世界で34万人が受講】データサイエンティストを目指すあなたへ〜データサイエンス25時間ブートキャンプ〜

コメント