escapade
@escapade

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

pandasで期間内最小値を求めたい

解決したいこと

下のような時系列データがあるとき、この期間内最小値の列を求めたいです。
期間内最小値は一時間ごとに更新される値となっていて、00分~その時点までの数値の最小値を格納しています。

例)

print(df)

----
time	            数値 期間内最小値
2018-07-01 10:00:00	5	5
2018-07-01 10:15:00	4	4
2018-07-01 10:30:00	9	4
2018-07-01 11:00:00	8	8
2018-07-01 11:15:00	2	2
2018-07-01 11:30:00	1	1
2018-07-01 12:00:00	5	5
2018-07-01 12:15:00	7	5
2018-07-01 12:30:00	7	5
2018-07-01 13:00:00	6	6

df["期間内最小値"] = df["数値"].rolling("1H").min()で求めることも考えましたが、これだと期間内最小値に未来の値が入ってしまうようです。mask()やwhere()で条件ごとに処理することも考えましたが、どう処理したものか分からず…
結構大きいデータなのでfor文で処理するのは時間がかかりそうなのですが、もし方法がなければforで処理することも考えています。

何かいいアイデアかヒントがあればよろしくお願いします。

0

3Answer

もっと手軽な方法があるかとは思いますが、
以下の方法でできました。

ざっくり説明すると、何時の列を作っておいて、その列で集計してしまおう作戦です。

まずは用意されている時刻のを切り捨てて、
何時台の列hourを用意します。

df["hour"] = df.index
df["hour"] = df['hour'].dt.floor("H")

その後、hourgroupbyした後にrolling("1H").min()
想定されている通りの集計になるのではと思うのですがいかがでしょう。

df["期間内最小値"] = df["数値"].groupby(df.hour).rolling("1H").min().values

time	   	数値	hour	期間内最小値
2018-07-01 10:00:00	5	2018-07-01 10:00:00	5.0
2018-07-01 10:15:00	4	2018-07-01 10:00:00	4.0
2018-07-01 10:30:00	9	2018-07-01 10:00:00	4.0
2018-07-01 11:00:00	8	2018-07-01 11:00:00	8.0
2018-07-01 11:15:00	2	2018-07-01 11:00:00	2.0
2018-07-01 11:30:00	1	2018-07-01 11:00:00	1.0
2018-07-01 12:00:00	5	2018-07-01 12:00:00	5.0
2018-07-01 12:15:00	7	2018-07-01 12:00:00	5.0
2018-07-01 12:30:00	7	2018-07-01 12:00:00	5.0
2018-07-01 13:00:00	6	2018-07-01 13:00:00	6.0

※処理のために作成した不要な列の削除、期間内最小値がfloatになっているなど、別途処理は必要になりますのでご容赦ください。

2Like

Comments

  1. @escapade

    Questioner

    なるほど、groupby()で集計する!
    ありがとうございます、使ってみます!

df.resample("1H")で1時間ごと(00分~59分59秒999)のグループにできます。

print(df)
#                      数値
# time                     
# 2022-10-21 10:00:00     5
# 2022-10-21 10:15:00     4
# 2022-10-21 10:30:00     9
# 2022-10-21 11:00:00     8
# 2022-10-21 11:15:00     2
# 2022-10-21 11:30:00     1
# 2022-10-21 12:00:00     5
# 2022-10-21 12:15:00     7
# 2022-10-21 12:30:00     7
# 2022-10-21 13:00:00     6

for label, i in df.resample("1H"):
    print(f"{label = }")
    print(i)
    print("---")
# label = Timestamp('2022-10-21 10:00:00', freq='H')
#                      数値
# time                     
# 2022-10-21 10:00:00     5
# 2022-10-21 10:15:00     4
# 2022-10-21 10:30:00     9
# ---
# label = Timestamp('2022-10-21 11:00:00', freq='H')
#                      数値
# time                     
# 2022-10-21 11:00:00     8
# 2022-10-21 11:15:00     2
# 2022-10-21 11:30:00     1
# ---
# label = Timestamp('2022-10-21 12:00:00', freq='H')
#                      数値
# time                     
# 2022-10-21 12:00:00     5
# 2022-10-21 12:15:00     7
# 2022-10-21 12:30:00     7
# ---
# label = Timestamp('2022-10-21 13:00:00', freq='H')
#                      数値
# time                     
# 2022-10-21 13:00:00     6
# ---

このグループに対して、先頭行から各行までの間での最小値を返す.cummin()関数を適用すれば欲しいものが得られます。

すなわち、

out_df = df.resample("1H").transform("cummin")
print(out_df)
#                      数値
# time                     
# 2022-10-21 10:00:00     5
# 2022-10-21 10:15:00     4
# 2022-10-21 10:30:00     4
# 2022-10-21 11:00:00     8
# 2022-10-21 11:15:00     2
# 2022-10-21 11:30:00     1
# 2022-10-21 12:00:00     5
# 2022-10-21 12:15:00     5
# 2022-10-21 12:30:00     5
# 2022-10-21 13:00:00     6
2Like

Comments

  1. @escapade

    Questioner

    transform("cummin")ですか! その発想はありませんでした(というか、cumminを初めて知りました)。
    resample期間を変えればもっと大きな時間で集計することもできそうですね。ありがとうございます!

pandasは凄いですね!rollingは折れ線グラフを滑らかにする時、便利ですね!
時間指定もできるのは驚きです。

さて、暇人x by 居酒屋のカクンターより!

pd2 = pd.DataFrame({
   "time": df["time"].rolling("1H").first().values,
   "min": df["数値"].resample("time").rolling("1H").last().values
})
print(pd2)
0Like

Your answer might help someone💌