LoginSignup
0
0

More than 1 year has passed since last update.

データサイエンス100本ノック(構造化データ加工編)をやってみた part10(P-089~P-100)

Last updated at Posted at 2021-08-19

 この記事はデータサイエンスを勉強しながら、データサイエンス協会が提供するデータサイエンス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の割合でランダムにデータを分割せよ。

p089.py
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セット作成せよ。

p090.py
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となるようにアンダーサンプリングで抽出せよ。

p091.py
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)では、性別に関する情報が非正規化の状態で保持されている。これを第三正規化せよ。

092.py
df_gender = df_customer[['gender_cd', 'gender']].drop_duplicates().reset_index(drop=True)
df_customer_1 = df_customer.drop('gender', axis=1)

 第三正規化の意味がわからなかったので以下を参考にしました。

 参考:関係データベースの正規化

 つまり、df_customergendergender_cdと紐づいているため
 gender_cdをキーにしてdf_genderdf_customer_1の二つのデータフレームに分離している。

P-093

P-093: 商品データフレーム(df_product)では各カテゴリのコード値だけを保有し、カテゴリ名は保有していない。カテゴリデータフレーム(df_category)と組み合わせて非正規化し、カテゴリ名を保有した新たな商品データフレームを作成せよ。

p093.py
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だけでいい。
 その場合、重複する列以外を指定して以下のように書ける。

p093.py
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

p094.py
df_product_1.to_csv('./data/df_product_1_header_utf8.csv',header=True, index=False, encoding='UTF-8')

 to_csv():データフレームをcsvファイルとして保存。
 headerindexで列名と行名を保存するか指定(デフォルトでTrueなのでheader=Trueは書かなくても良い)
 以降の問題と区別するため、ファイル名にヘッダと文字コードと追記している。

参考:pandasでcsvファイルの書き出し・追記(to_csv)

P-095

P-095: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ有り
・文字コードはCP932

p095.py
df_product_1.to_csv('./data/df_product_1_header_cp932.csv', index=False, encoding='CP932')

P-096

P-096: 先に作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。なお、出力先のパスはdata配下とする。
・ファイル形式はCSV(カンマ区切り)
・ヘッダ無し
・文字コードはUTF-8

p096.py
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

p097.py
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

p098.py
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

p099.py
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

p100.py
df = pd.read_table('./data/df_product_1_header_utf8.tsv')
df.head(10)

 タブ区切りのデータであるため、read_csv()ではなく、read_table()を使う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0