この記事はデータサイエンスを勉強しながら、データサイエンス協会が提供する__データサイエンス100本ノック(構造化データ加工編)__を解く過程を自分用にまとめたものです。
チャプター | リンク | チャプター | リンク |
---|---|---|---|
P-001~P-016 | part1 | P-052~P-062 | part6 |
P-017~P-022 | part2 | P-063~P-068 | part7 |
P-023~P-031 | part3 | P-069~P-078 | part8 |
P-032~P-039 | part4 | P-079~P-088 | part9 |
P-040~P-051 | part5 | P-089~P-100 | part10 |
- アンダーサンプリング
RandomUnderSampler()
- csvファイルの書き出し
to_csv()
- csv、tsvファイルの読み込み
pd.read_csv()
、pd.read_table()
P-089
P-089: 売上実績のある顧客に対し、予測モデル構築のため学習用データとテスト用データに分割したい。それぞれ8:2の割合でランダムにデータを分割せよ。
df_tmp = df_receipt.groupby('customer_id').amount.sum().reset_index()
# how='inner'とすることで買い物実績のあるcustomer_idだけを残す
df_tmp = pd.merge(df_customer, df_tmp['customer_id'], how='inner', on='customer_id')
df_train, df_test = train_test_split(df_tmp, test_size=0.2)
P-090
P-090: レシート明細データフレーム(df_receipt)は2017年1月1日〜2019年10月31日までのデータを有している。売上金額(amount)を月次で集計し、学習用に12ヶ月、テスト用に6ヶ月のモデル構築用データを3セット作成せよ。
df_tmp = df_receipt[['sales_ymd', 'amount']].copy()
# sales_ymdから年と月のみを取り出した列を新たに作成
df_tmp['sales_ym'] = df_tmp['sales_ymd'].astype('str').str[:6]
df_tmp = df_tmp.groupby('sales_ym').amount.sum().reset_index()
# データセットを3組作成
df_train_1, df_test_1 = df_tmp[:12], df_tmp[12:18]
df_train_2, df_test_2 = df_tmp[1:13], df_tmp[13:19]
df_train_3, df_test_3 = df_tmp[2:14], df_tmp[14:20]
スライスでデータセットを用意したが、解答のように用意する数を増やしたり、サイズを変えたりしたい時のために関数を用意したほうがいいと思う。
def split_data(df, train_size, test_size, slide_window, start_point):
train_start = start_point * slide_window
test_start = train_start + train_size
return df[train_start : test_start], df[test_start : test_start + test_size]
df_train_1, df_test_1 = split_data(df_tmp, train_size=12, test_size=6, slide_window=6, start_point=0)
df_train_2, df_test_2 = split_data(df_tmp, train_size=12, test_size=6, slide_window=6, start_point=1)
df_train_3, df_test_3 = split_data(df_tmp, train_size=12, test_size=6, slide_window=6, start_point=2)
P-091 RandomUnderSampler()
P-091: 顧客データフレーム(df_customer)の各顧客に対し、売上実績のある顧客数と売上実績のない顧客数が1:1となるようにアンダーサンプリングで抽出せよ。
df_tmp = df_receipt.groupby('customer_id').amount.sum().reset_index()
df_tmp = pd.merge(df_customer, df_tmp, how='left', on='customer_id').fillna(0)
# 売り上げ実績がある顧客は'1'、ない顧客は'0'をもつ新しい列を作成
df_tmp['sales_performance'] = df_tmp['amount'].apply(lambda x: 0 if x == 0 else 1)
# imblearn.under_samplingライブラリののRandomUnderSamplerを使用
rus = RandomUnderSampler(sampling_strategy = 'majority')
df_resample, _ = rus.fit_resample(df_tmp, df_tmp.sales_performance)
アンダーサンプリング:頻度が高い値を含むデータを削減する方法
df_tmp
では実績があるデータが8306
件、実績がないデータが13665
個あるのが確認できる。
つまり実績がないデータを削減して1:1になるように調節する。
抽出できているかは以下のコードで確認(出力結果はどちらも8306)
print(len(df_resample.query('sales_performance == 0')))
print(len(df_resample.query('sales_performance == 1')))
P-092
P-092: 顧客データフレーム(df_customer)では、性別に関する情報が非正規化の状態で保持されている。これを第三正規化せよ。
df_gender = df_customer[['gender_cd', 'gender']].drop_duplicates().reset_index(drop=True)
df_customer_1 = df_customer.drop('gender', axis=1)
第三正規化の意味がわからなかったので以下を参考にしました。
参考:関係データベースの正規化
つまり、df_customer
のgender
はgender_cd
と紐づいているため
gender_cd
をキーにしてdf_gender
とdf_customer_1
の二つのデータフレームに分離している。
P-093
P-093: 商品データフレーム(df_product)では各カテゴリのコード値だけを保有し、カテゴリ名は保有していない。カテゴリデータフレーム(df_category)と組み合わせて非正規化し、カテゴリ名を保有した新たな商品データフレームを作成せよ。
df_product_1 = pd.merge(df_product, df_category, how='left', on=['category_major_cd', 'category_medium_cd', 'category_small_cd'])
df_product_1
pd.merge()
による結合のキー列を3つ指定したが、category_small_cd
によって全てのカテゴリ名が決まるので、キー列はcategory_small_cd
だけでいい。
その場合、重複する列以外を指定して以下のように書ける。
df_product_1 = pd.merge(df_product,
df_category[['category_major_name', 'category_medium_name', 'category_small_name', 'category_small_cd']],
how='left', on='category_small_cd')
df_product_1
P-094 to_csv()
P-094: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ有り
・文字コードはUTF-8
df_product_1.to_csv('./data/df_product_1_header_utf8.csv',header=True, index=False, encoding='UTF-8')
to_csv()
:データフレームをcsvファイルとして保存。
header
、index
で列名と行名を保存するか指定(デフォルトでTrue
なのでheader=True
は書かなくても良い)
以降の問題と区別するため、ファイル名にヘッダと文字コードと追記している。
参考:pandasでcsvファイルの書き出し・追記(to_csv)
P-095
P-095: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ有り
・文字コードはCP932
df_product_1.to_csv('./data/df_product_1_header_cp932.csv', index=False, encoding='CP932')
P-096
P-096: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ無し
・文字コードはUTF-8
df_product_1.to_csv('./data/df_product_1_utf8.csv', header=False, index=False, encoding='UTF-8')
P-097 pd.read_csv()
P-097: 先に作成した以下形式のファイルを読み込み、データフレームを作成せよ。また、先頭10件を表示させ、正しくとりまれていることを確認せよ。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ有り
・文字コードはUTF-8
df = pd.read_csv('./data/df_product_1_header_utf8.csv')
df.head(10)
pd.read_csv()
:csvファイルの読み込み
デフォルトでencoding='UTF-8'
であるため省略している。
参考:pandasでcsv/tsvファイル読み込み(read_csv, read_table)
P-098
P-098: 先に作成した以下形式のファイルを読み込み、データフレームを作成せよ。また、先頭10件を表示させ、正しくとりまれていることを確認せよ。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ無し
・文字コードはUTF-8
df = pd.read_csv('./data/df_product_1_utf8.csv', header=None)
df.head(10)
ヘッダがないデータなのでそのまま読み込むと1行目がカラムになってしまう。
header=None
とすることで、0からの整数がカラムに割り当てられる。
P-099
P-099: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はTSV(タブ区切り)
・ヘッダ有り
・文字コードはUTF-8
df_product_1.to_csv('./data/df_product_1_header_utf8.tsv', sep='\t', index=None, encoding='UTF-8')
P-100 pd.read_table()
P-100: 先に作成した以下形式のファイルを読み込み、データフレームを作成せよ。また、先頭10件を表示させ、正しくとりまれていることを確認せよ。
・ファイル形式はTSV(タブ区切り)
・ヘッダ有り
・文字コードはUTF-8
df = pd.read_table('./data/df_product_1_header_utf8.tsv')
df.head(10)
タブ区切りのデータであるため、read_csv()
ではなく、read_table()
を使う。