如何生成满足时间间隔约束的datetime序列?
问题:为Pandas DataFrame生成符合规则的单调递增datetime列
给定条件
- 两个datetime对象与时间范围:
import datetime start_dt = datetime.datetime(2023, 7, 26, 6, 0, 0) end_dt = datetime.datetime(2023, 7, 26, 15, 0, 0) range_of_minutes = range(15, 201) # 15到200分钟之间
- Pandas DataFrame:
import pandas as pd df = pd.DataFrame( {'item': ['XXX01', 'XXX02', 'XXX03', 'XXX04', 'XXX05', 'XXX06', 'XXX07', 'XXX08', 'XXX09']} )
需求
为DataFrame添加datetime列,规则如下:
- 第一个item对应
start_dt - 从第二个item开始,每个后续item的datetime必须介于
start_dt和end_dt之间 - 相邻item的时间间隔是
range_of_minutes内的随机值 - datetime必须单调递增
- item数量不固定(可奇可偶)
预期输出示例(仅展示前两列)
item datetime **datetimes are monotonic and range is respected** 0 XXX01 26/07/2023 06:00:00 NaN 1 XXX02 26/07/2023 06:17:34 15,52 2 XXX03 26/07/2023 06:53:55 36,35 3 XXX04 26/07/2023 08:05:15 71,33 4 XXX05 26/07/2023 09:54:10 108,92 5 XXX06 26/07/2023 11:08:20 74,17 6 XXX07 26/07/2023 11:30:20 22 7 XXX08 26/07/2023 14:07:05 156,75 8 XXX09 26/07/2023 14:45:08 38,05
尝试的代码
import random def r_interval(): return random.randint(min(range_of_minutes), max(range_of_minutes)) df.loc[0, "datetime"] = pd.to_datetime(start_dt) df["datetime"] = pd.to_datetime(start_dt) + [r_interval() + dt.shift() for dt in df["datetime"][1:]]
解决方案
你的代码里dt.shift()用法错误,而且没处理时间不能超过end_dt的限制,下面提供两种可行实现:
方法1:循环生成(直观易懂,适合小数据量)
import datetime import pandas as pd import random start_dt = datetime.datetime(2023, 7, 26, 6, 0, 0) end_dt = datetime.datetime(2023, 7, 26, 15, 0, 0) range_of_minutes = range(15, 201) df = pd.DataFrame( {'item': ['XXX01', 'XXX02', 'XXX03', 'XXX04', 'XXX05', 'XXX06', 'XXX07', 'XXX08', 'XXX09']} ) # 初始化datetime列 df['datetime'] = pd.NaT df.loc[0, 'datetime'] = start_dt current_dt = start_dt # 从第二行开始逐个生成时间 for i in range(1, len(df)): # 计算当前到结束还剩多少分钟可用 remaining_minutes = (end_dt - current_dt).total_seconds() / 60 # 计算当前允许的最大间隔:不能超过剩余时间,还要给后面的item留够最小间隔 min_interval = min(range_of_minutes) max_interval = min(max(range_of_minutes), remaining_minutes - min_interval * (len(df) - i - 1)) # 兜底处理:如果剩余时间连最小间隔都不够,强制用最小间隔 if max_interval < min_interval: max_interval = min_interval interval = random.randint(int(min_interval), int(max_interval)) current_dt += datetime.timedelta(minutes=interval) df.loc[i, 'datetime'] = current_dt
方法2:向量化生成(效率更高,适合大数据量)
import datetime import pandas as pd import numpy as np start_dt = datetime.datetime(2023, 7, 26, 6, 0, 0) end_dt = datetime.datetime(2023, 7, 26, 15, 0, 0) range_of_minutes = range(15, 201) df = pd.DataFrame( {'item': ['XXX01', 'XXX02', 'XXX03', 'XXX04', 'XXX05', 'XXX06', 'XXX07', 'XXX08', 'XXX09']} ) total_allowed_minutes = (end_dt - start_dt).total_seconds() / 60 n_intervals = len(df) - 1 # 先生成初始随机间隔 intervals = np.random.randint(min(range_of_minutes), max(range_of_minutes)+1, size=n_intervals) # 调整间隔总和,确保不超过允许的总时间,同时保证每个间隔不小于最小值 total_intervals = intervals.sum() if total_intervals > total_allowed_minutes: scale = (total_allowed_minutes - n_intervals * min(range_of_minutes)) / (total_intervals - n_intervals * min(range_of_minutes)) intervals = (intervals - min(range_of_minutes)) * scale + min(range_of_minutes) intervals = np.round(intervals).astype(int) # 计算累计间隔,生成datetime列 cumulative_intervals = np.cumsum(intervals) df['datetime'] = [start_dt] + [start_dt + datetime.timedelta(minutes=int(ci)) for ci in cumulative_intervals]
说明
两种方法都满足所有要求:
- 第一个item的时间固定为
start_dt - 所有datetime严格单调递增
- 最后一个时间不会超过
end_dt - 相邻间隔始终在15-200分钟范围内
- 适配任意数量的item(奇/偶均可)
内容的提问来源于stack exchange,提问作者user21474411
相关产品推荐
相关产品推荐

