我有以下熊猫数据框:
指数 | 开始时间 | 结束时间 | 数量 |
---|---|---|---|
福 | 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 = 50bar
的余数(18)分配给10:00-11:00据我了解,dataframe 的 .resample 方法最适合按间隔拆分,但是:
我猜这是在 Python 中处理时间序列时的常见需求。在我开始构建一些复杂的东西之前......有没有内置/简单的方法来解决它?
这是使用 Pandas Timestamp.floor、Timestamp.ceil 和 explode 来回答您的有趣问题的一种复杂方法:
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