高階関数reduceに行き着きました
データフレーム同士を結合させようとしたところ、reduceに行き着きました。
やりたかったことをざっくりいうと、日付ごとに特定の文字列を含む要素数を一覧表示させること。
まずは元となるデータフレームをChatGptに作ってもらいました。
import pandas as pd
import random
from datetime import date, timedelta
from functools import reduce
# 日付をランダムに10日間選択
start_date = date(2023, 9, 1)
end_date = date(2023, 9, 10)
date_list = [start_date + timedelta(days=random.randint(0, 9)) for _ in range(30)] # 30個の日付をランダムに選択
# 曜日を生成
day_of_week_list = [d.strftime('%A') for d in date_list]
# 商品名を生成
fruits = ['りんご', 'バナナ', 'みかん', 'いちご']
product_list = [random.choice(fruits) for _ in range(30)]
# データフレームを作成
data = {'日付': date_list, '曜日': day_of_week_list, '商品名': product_list}
df = pd.DataFrame(data).sort_values('日付')
ランダムに選んでいうので毎回違うデータフレームになりますが、
こんな感じです。
print(df)
#
日付 曜日 商品名
1 2023-09-02 Saturday バナナ
2 2023-09-02 Saturday いちご
11 2023-09-02 Saturday バナナ
0 2023-09-03 Sunday りんご
23 2023-09-03 Sunday りんご
29 2023-09-03 Sunday みかん
22 2023-09-04 Monday みかん
12 2023-09-04 Monday みかん
18 2023-09-04 Monday バナナ
27 2023-09-05 Tuesday バナナ
9 2023-09-05 Tuesday みかん
28 2023-09-05 Tuesday バナナ
15 2023-09-05 Tuesday いちご
6 2023-09-06 Wednesday りんご
10 2023-09-06 Wednesday りんご
20 2023-09-06 Wednesday いちご
5 2023-09-06 Wednesday りんご
21 2023-09-07 Thursday みかん
14 2023-09-07 Thursday いちご
7 2023-09-07 Thursday バナナ
19 2023-09-08 Friday みかん
13 2023-09-08 Friday みかん
8 2023-09-08 Friday バナナ
24 2023-09-08 Friday みかん
25 2023-09-08 Friday バナナ
17 2023-09-09 Saturday りんご
4 2023-09-10 Sunday みかん
3 2023-09-10 Sunday みかん
26 2023-09-10 Sunday いちご
16 2023-09-10 Sunday バナナ
次に商品名に特定の文字列を含む日付ごとの要素数を変数にします。
df_ringo = df[df['商品名'].str.contains('りんご')].groupby(['日付','曜日']).size().reset_index(name='りんご')
df_banana = df[df['商品名'].str.contains('バナナ')].groupby(['日付','曜日']).size().reset_index(name='バナナ')
df_mikan = df[df['商品名'].str.contains('みかん')].groupby(['日付','曜日']).size().reset_index(name='みかん')
df_ichigo = df[df['商品名'].str.contains('いちご')].groupby(['日付','曜日']).size().reset_index(name='いちご')
ちなみにこんな感じです。
print(df_ringo)
#
日付 曜日 りんご
0 2023-09-03 Sunday 2
1 2023-09-06 Wednesday 3
2 2023-09-09 Saturday 1
print(df_banana)
#
日付 曜日 バナナ
0 2023-09-02 Saturday 2
1 2023-09-04 Monday 1
2 2023-09-05 Tuesday 2
3 2023-09-07 Thursday 1
4 2023-09-08 Friday 2
5 2023-09-10 Sunday 1
これらを日付、曜日ごとに結合させれば欲しいデータが得られます。
Pandasのmergeを使えば連結できそうです。
ですが問題がひとつ。
mergeは2つのデータフレームしか結合できないようです。
もちろん、df_ringoとdf_bananaを結合して、結合したものにdf_mikanを結合して…とやっていけばできることはできますが、かなり面倒です。
ここで便利なのがreduce関数。
あれとこれをこうして、できたものとそれをさらにこうするみたいなことが一気にできます。
うまく説明できないので、まずはコードを書きます。
dfs = [df_ringo, df_banana,df_mikan, df_ichigo]
df = reduce(lambda x, y: pd.merge(x,y, on=['日付', '曜日'], how='outer'), dfs).fillna(0)
まずは結合したいものをリストにします。
そしてついにreduceの出番。
reduceについてはこのYouTubeの解説がとてもわかりやすいです。
mergeについてはあまり使ったことがなかったので次回に取り上げてみようと思います。
結合した時に、データがない場合ももちろんあるので、fillna(0)でデータがない部分は0を表示させておきました。
結果、こういうデータフレームができ目的のものを完成させることができました。
print(df.sort_values('日付'))
#
日付 曜日 りんご バナナ みかん いちご
3 2023-09-02 Saturday 0.0 2.0 0.0 1.0
0 2023-09-03 Sunday 2.0 0.0 1.0 0.0
4 2023-09-04 Monday 0.0 1.0 2.0 0.0
5 2023-09-05 Tuesday 0.0 2.0 1.0 1.0
1 2023-09-06 Wednesday 3.0 0.0 0.0 1.0
6 2023-09-07 Thursday 0.0 1.0 1.0 1.0
7 2023-09-08 Friday 0.0 2.0 3.0 0.0
2 2023-09-09 Saturday 1.0 0.0 0.0 0.0
8 2023-09-10 Sunday 0.0 1.0 2.0 1.0