見出し画像

前処理大全:第4章

Q:予約テーブルとホテルテーブルを結合して宿泊人数が1人のビジネスホテルの予約レコードのみを取り出す

# ホテルマスタの読み込み
hotel_tb = pd.read_csv('hotel.csv', encoding = 'UTF-8')

# reserve_tbとhotel_tbをhotel_idが等しいもの同士で内部結合
# people_numが1かつis_businessがTrueのデータのみ抽出
pd.merge(reserve_tb, hotel_tb, on = 'hotel_id', how = 'inner').query('people_num == 1 & is_business')

# 上記のcodeの良い書き方
# 結合前にデータを絞り処理を軽くする
pd.merge(reserve_tb.query('people_num == 1'), hotel_tb.query('is_business'), on = 'hotel_id', how = 'inner')

4-2:条件に応じた結合テーブルの切り替え

Q:同じ小地域のホテルが20件以上ある場合は、同じ小地域(small_area_name)のホテルをレコメンド候補に。20件に満たない場合は同じ大地域のホテル(big_area_name)をレコメンド候補にする。

# ガベージコレクション(必要ないメモリの解放)のためのライブラリ
import gc

# small_area_name毎にホテル数をカウント
small_area_mst = hotel_tb.groupby(['big_area_name', 'small_area_name'], as_index = False).size().reset_index()
small_area_mst.columns = ['index', 'big_area_name', 'small_area_name', 'hotel_cnt']

# 20件以上であれなjoin_area_idをsmall_area_nameとして設定
# 20件未満であればjoin_area_idをbig_area_nameとして設定
small_area_mst['join_area_id'] = np.where(small_area_mst['hotel_cnt'] - 1 >= 20, small_area_mst['small_area_name'], small_area_mst['big_area_name'])

# 必要なくなった列の削除
small_area_mst.drop(['hotel_cnt', 'index', 'big_area_name'], axis = 1, inplace=True)

# レコメンド元になるホテルにsmall_area_nameを結合することで、join_area_idを設定
base_hotel_mst = pd.merge(hotel_tb, small_area_mst, on = 'small_area_name').loc[:, ['hotel_id', 'join_area_id']]

# 下記は必要に応じて、メモリを解放(必須ではないがメモリ量に余裕のない時に利用
del small_area_mst
gc.collect()

# recommend_hotel_mstはレコメンド候補のためのテーブル
recommend_hotel_mst = pd.concat([
    # join_area_idをbig_area_nameとしたレコメンド候補マスタ
    hotel_tb[['small_area_name', 'hotel_id']].rename(columns={'small_area_name': 'join_area_id'}, inplace = False),
    # join_area_idをsmall_area_nameとしたレコメンド候補マスタ
    hotel_tb[['big_area_name', 'hotel_id']].rename(columns={'big_area_name': 'join_area_id'}, inplace = False)
    ])

# hotel_idの列名が結合すると重複するので変更
recommend_hotel_mst.rename(columns={'hotel_id': 'rec_hotel_id'}, inplace = True)

# base_hotel_mstとrecommend_hotel_mstを結合し、レコメンド候補の情報を付与
# query関数によってレコメンド候補から自分を除く
pd.merge(base_hotel_mst, recommend_hotel_mst, on = 'join_area_id').loc[:, ['hotel_id', 'rec_hotel_id']].query('hotel_id != rec_hotel_id')

4-3:過去データの結合

Q:n件前のデータ取得。全ての行に同じ顧客の2回前の予約金額の情報を付与。2回前の予約がない場合は、値無しとする。

# customer_idごとにreserve_datetimeで並び替え
# groupby関数の後にapply関数を適用することによって、group毎に並び替える
# sort_value関数によってデータを並び替え、axisが0の場合は行、1の場合は列を並び替え
result = reserve_tb.groupby('customer_id').apply(lambda group : group.sort_values(by = 'reserve_datetime', axis = 0, inplace = False))

# resultはすでにcustomer_id毎にgroup化されてる
# customer毎に2つ前のtotal_priceをbefore_priceとして保存
# shift関数はperiodsの引数の数だけデータ業を下にずらす
result['before_price'] = result['total_price'].shift(periods=2)
result

Q:過去n件の平均値

自身の行を含めないで1件前から3件前までの3回の平均予約金額の情報を付与。過去の予約が3回未満の場合は、満たない回数内での平均予約金額を計算。予約が1回もない場合は値無しとする。


# customer_idごとにreserve_datetimeでデータの並び替え
result = reserve_tb.groupby('customer_id').apply(lambda x : x.sort_values(by='reserve_datetime', ascending=True)).reset_index(drop=True)

# 新たな列としてprice_sumを追加
result['price_sum'] = pd.Series(
    # customer_idごとにtotal_priceのwindow3件にまとめ、その合計値を計算
    result.groupby('customer_id').rolling(center=False, window=3, min_periods=3).sum()
    
    # groupby化を解除すると同時に、total_price列の取り出し
    .reset_index(drop=True)
    .loc[:, 'total_price']
)
result

過去n日間の合計値

自身の行のデータを含めないで同じ顧客の過去90日間の合計予約金額を求める。

# 必要なライブラリのインポート
import pandas.tseries.offsets as offsets
import operator

# 日時の計算に利用するため、データ型を文字列から日時型に変換
reserve_tb['reserve_datetime'] = pd.to_datetime(reserve_tb['reserve_datetime'], format='%Y-%m-%d %H:%M:%S')

# reserve_datetimeの日付を確認せずに、同じcustomer_idのデータ業同士を全て結合
sum_table = pd.merge(
    reserve_tb[['reserve_id', 'customer_id', 'reserve_datetime']],
    reserve_tb[['customer_id', 'reserve_datetime', 'total_price']]
    .rename(columns={'reserve_datetime': 'reserve_datetime_before'}),
    on='customer_id')

# checkinの日付を比較して90日以内のデータが結合されているデータ業のみ抽出
# operratorのand_関数を利用して複合条件を設定
# reserve_idごとにtotal_priceの合計値を計算
sum_table = sum_table[operator.and_(
    sum_table['reserve_datetime'] > sum_table['reserve_datetime_before'],
    sum_table['reserve_datetime'] + offsets.Day(-90) <= sum_table['reserve_datetime_before']
)].groupby('reserve_id')['total_price'].sum().reset_index()

# 列名を設定
sum_table.columns = ['reserve_id', 'total_price_sum']

# 計算した合計値を結合し、元のテーブルに情報を付与
# 合計値が存在しないコードの合計値の値を、fillnaを利用して0に変更
pd.merge(reserve_tb, sum_table, on='reserve_id', how='left').fillna(0)

4-4:全結合

Q:顧客ごとに2017年1月〜2017年3月の月間利用料金を計算。利用がない月は0とする。



サポートして頂いたお金は開業資金に充てさせて頂きます。 目標は自転車好きが集まる場所を作る事です。 お気持ち程度でいいのでサポートお願い致します!