🕐

【後編】Pythonで日付や id を含むデータの集計方法についてまとめてみた

2021/01/30に公開

時系列の特徴量作成についてまとめた前編に引き続き、後編(本ブログ)では日付を含むデータの範囲を絞ったり、IDごとに集計する場合のテクニックをまとめたいと思います。

なお、前編と同じく、データはkaggleのM5の下記の2つを使用しています。

  • sales_train_evaluation.csv
  • calendar.csv

加工して下記のようなデータ(文中では dtable )を使用します。

詳細なコードはこちら

期間で集計

期間で集計する場合は、dt.datatime(年、月、日)を使って条件指定してやりましょう。

from_years, from_months, from_days = 2011, 2, 15
till_years, till_months, till_days = 2011, 2, 20

# 特定期間
specific_period = dtable[(dtable["date"] >= dt.datetime(from_years, from_months, from_days)) \
& (dtable["date"] <= dt.datetime(till_years, till_months, till_days))]

https://qiita.com/mSpring/items/6ec1ab28dcb261db2c73

特定期間の売上TOP 30%以内のitem_idを抽出

この場合は売上ですが、目的変数が例えば上位30%以上のものだけを取り出したい場合、いろんなやり方があると思いますが、今回は降順に並べ替えてcumsumを使って累積和を取り、累積和÷総和が30%以下の項目を抽出して上位30%の項目を取り出します。

https://note.nkmk.me/python-numpy-cumsum-cumprod/

item_id_group = pd.DataFrame(specific_period.groupby("item_id").sum()).sort_values('sales_quantity', ascending=False)

# 降順で累積和
item_id_group["cum_ratio"] = item_id_group["sales_quantity"].cumsum()/item_id_group["sales_quantity"].sum()

top30_item_id_list = list(item_id_group[item_id_group["cum_ratio"]<=0.3].index)

# 売上TOP30%のitem_idを含む項目のみ表示
dtable[dtable["item_id"].isin(top30_item_id_list)]

加えてgroupbyを使えば、上位30%のみを集計することも可能です。

# 上位30%の合計をcat_idごとに集計
print("上位30%")
display(dtable[dtable["item_id"].isin(top30_item_id_list)].groupby("cat_id").sum())

# 上位30%以外の合計をcat_idごとに集計
print("上位30%以外")
display(dtable[~dtable["item_id"].isin(top30_item_id_list)].groupby("cat_id").sum())

店舗別に上位30%の item_id を抽出

今度は、店舗別に上位30%の item_id を抽出してみましょう。

さきほどと同じように累積和を使いますが、今度は店舗別に累積和を出して降順に並び替える必要があるので、少し工夫が必要です。

groupby().transform() を使うことで、groupbyで集計した数値を入力することができます。

https://qiita.com/greenteabiscuit/items/132e0f9b1479926e07e0

分母(numerator)に累積和、分子(denominator)に総和をつかって店舗ごとの累積和 ÷ 総和を算定してやります。

# store_id、item_idで集計
store_item_id_group_sum = dtable[["store_id", "item_id", "sales_quantity"]].groupby(["store_id", "item_id"]).sum()

# store_idはそのままに、店舗ごとにitem_idがsales_quantityの降順に並ぶようにソート
store_item_id_group_sum_sort = store_item_id_group_sum.sort_values(["store_id", "sales_quantity"], ascending=[True, False])

# sales_quantityの累計を算定
store_item_id_group_sum_sort_cumsum = store_item_id_group_sum_sort.groupby("store_id").transform(np.cumsum)

# 分母と並びを合わせるため、店舗別にitem_idを昇順に並び替える
numerator = store_item_id_group_sum_sort_cumsum.sort_values(["store_id", "item_id"], ascending=[True, True])

分子を出力すると以下のようになります。

# store_id、item_idで集計
store_item_id_group_sum = dtable[["store_id", "item_id", "sales_quantity"]].groupby(["store_id", "item_id"]).sum()

# さらにstore_idごとの合計を算定する → その合計額が各店舗のitem_idに計上される
denominator = store_item_id_group_sum.groupby("store_id").transform(np.sum)

分母を出力すると以下のようになります。

分母(denominator)の店舗IDごとの総和で分子(numerator)の店舗ごとの累積和を割って累積比率を算定すると以下のようになります。

by_store_item_id_rate = numerator / denominator

店舗IDごとの累積比率が30%以下の項目を抽出すれば、各店舗ごとの上位30%のitem_idを抽出することが可能出来ます。

store_idとitem_idの2つのカラムに分かれていると抽出の際に使いづらいのでこの2つを連結させてやります。

# 
by_store_item_id_rate<=0.3

by_store_item_id_rate = by_store_item_id_rate.reset_index()

# store_idとitem_idの2つのカラムに分かれていると、DataFrame型の入れ子で使えない
# そこでstore_idとitem_idを組み合わせた store_item_id を作成する
by_store_item_id_rate["store_item_id"] = by_store_item_id_rate["store_id"].str.cat(by_store_item_id_rate["item_id"], sep="-")

# store_item_id を index とする
by_store_item_id_rate = by_store_item_id_rate[["store_item_id", "sales_quantity"]].set_index("store_item_id")

# 各店舗ごとのTOP30%のitem_idを表示
byshop_top_30 = by_store_item_id_rate[by_store_item_id_rate["sales_quantity"]<0.3]

# 各店舗ごとのTOP30%のitem_idのリストを作成
byshop_top_30_list = list(byshop_top_30.index)

各店舗ごとの上位30%のリストを店舗IDとアイテムIDを連結させた項目の作成することができました。

店舗IDとアイテムIDを連結させて、先ほどのリストを使って抽出しましょう。

# 店舗ごとのTOP30%を表示
dtable["store_item_id"] = dtable["store_id"].str.cat(dtable["item_id"], sep="-")

dtable[dtable["store_item_id"].isin(byshop_top_30_list)]

以上になります、最後までお読みいただきありがとうございました。

Discussion