Pandas dataframe:重采样时间间隔和按比例划分值?

问题描述 投票:0回答:1

我有以下熊猫数据框:

指数 开始时间 结束时间 数量
2023-03-11 09:45:27 2023-03-11 09:58:39 48
酒吧 2023-03-11 09:59:00 2023-03-11 010:09:00 20

我希望按小时间隔拆分数据,这样的数量是:

间隔 数量
2023-03-11 09:00-10:00 50
2023-03-11 10:00-11:00 18

逻辑是:

  • bar
    1分钟(=10%)在09:00-10:00区间内,所以
    bar
    的10%量加到09:00-10:00区间内,共48+ 20*0.1 = 50
  • bar
    的余数(18)分配给10:00-11:00

据我了解,dataframe 的 .resample 方法最适合按间隔拆分,但是:

  • 似乎resample是针对个别时间的,而不是开始和结束时间
  • 好像没有办法按间隔按比例划分时间

我猜这是在 Python 中处理时间序列时的常见需求。在我开始构建一些复杂的东西之前......有没有内置/简单的方法来解决它?

python pandas group-by resampling
1个回答
0
投票

这是使用 Pandas Timestamp.floorTimestamp.ceilexplode 来回答您的有趣问题的一种复杂方法:

import pandas as pd

df = pd.DataFrame(
    {
        "index": ["foo", "bar"],
        "start_time": ["2023-03-11 09:45:27", "2023-03-11 09:59:00"],
        "end_time": ["2023-03-11 09:58:39", "2023-03-11 10:09:00"],
        "amount": [48, 20],
    }
)
for col in ("start_time", "end_time"):
    df[col] = pd.to_datetime(df[col], infer_datetime_format=True)
# Find intervals
df["interval"] = df.apply(
    lambda x: [[x["start_time"].floor("H"), x["start_time"].ceil("H")]]
    if (x["end_time"].ceil("H").hour - x["start_time"].floor("H").hour) == 1
    else [
        [x["start_time"].floor("H"), x["end_time"].floor("H")],
        [x["start_time"].ceil("H"), x["end_time"].ceil("H")],
    ],
    axis=1,
)

# Divide values
df["amount"] = df.apply(
    lambda x: x["amount"]
    if (x["end_time"].ceil("H").hour - x["start_time"].floor("H").hour) == 1
    else [
        int(
            x["amount"]
            * 60
            * (60 - x["start_time"].minute)
            / (x["end_time"] - x["start_time"]).total_seconds()
        ),
        int(
            x["amount"]
            - x["amount"]
            * 60
            * (60 - x["start_time"].minute)
            / (x["end_time"] - x["start_time"]).total_seconds()
        ),
    ],
    axis=1,
)

# Deal with lists of intervals
tmp = df.loc[df["interval"].apply(len) == 2].explode(["interval", "amount"])
other = df.loc[~df.index.isin(tmp.index), :].pipe(
    lambda df_: df_.assign(interval=df_["interval"].apply(lambda x: x[0]))
)
df = pd.concat([other, tmp])

# Compute final values
df = (
    df.assign(interval=df["interval"].apply(tuple))
    .groupby("interval")
    .agg({"amount": sum})
)

然后:

print(df)
# Output
                                           amount
interval
(2023-03-11 09:00:00, 2023-03-11 10:00:00)     50
(2023-03-11 10:00:00, 2023-03-11 11:00:00)     18
© www.soinside.com 2019 - 2024. All rights reserved.