当ページのリンクには広告が含まれています。
目次
データサイエンス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_receipt
とdf_customer
の構造を確認します。
出力 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_ymd
とapplication_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_ymd
とapplication_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))
出力
これらのノウハウを用いて本問を解いていきましょう。
前問と同様に、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_ymd
とapplication_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_ymd
とapplication_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_ymd
とapplication_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 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 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_receipt
とdf_customer
という2つのデータフレームから、今回の問題を解く上で必要となるカラムだけを抽出したいと思います。
今回の問題を解く上で、必要となるカラムは、customer_id
, sales_ymd
, application_date
ですね。
df_receipt
とdf_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_ymd
とapplication_date
をpandas.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()
出力
本問では、"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 回答】」というテーマでまとめました。
本記事で紹介した方法を元にデータサイエンティストとしての知見を深めていただければと思います。
なお、データサイエンティストに必要な知識は、TechAcademyのデータサイエンスコース での学習がおすすめです。