Python 如何有效地将(开始时间,[时间增量])转换为(开始时间,结束时间)?
基本上,我有数据提供开始时间、时隙数量和每个时隙的持续时间。 我想把它转换成一个开始时间和结束时间的数据帧——我已经做到了,但我忍不住认为它效率不高,或者说特别像python。 实际数据具有多个ID,因此需要分组Python 如何有效地将(开始时间,[时间增量])转换为(开始时间,结束时间)?,python,python-3.x,pandas,Python,Python 3.x,Pandas,基本上,我有数据提供开始时间、时隙数量和每个时隙的持续时间。 我想把它转换成一个开始时间和结束时间的数据帧——我已经做到了,但我忍不住认为它效率不高,或者说特别像python。 实际数据具有多个ID,因此需要分组 import pandas as pd slots = pd.DataFrame({"ID": 1, "StartDate": pd.to_datetime("2019-01-01 10:30:00"), "Quantity": 3, "Duration": pd.to_timede
import pandas as pd
slots = pd.DataFrame({"ID": 1, "StartDate": pd.to_datetime("2019-01-01 10:30:00"), "Quantity": 3, "Duration": pd.to_timedelta(30, unit="minutes")}, index=[0])
grp_data = slots.groupby("ID")
bob = []
for rota_id, row in grp_data:
start = row.iloc[0, 1]
delta = row.iloc[0, 3]
for quantity in range(1, int(row.iloc[0, 2] + 1)):
data = {"RotaID": rota_id,
"DateStart": start,
"Duration": delta,
"DateEnd": start+delta}
bob.append(data)
start = start + delta
fred = pd.DataFrame(bob)
这可能会在其他地方得到回答,但我不知道如何正确搜索,因为我不确定我的问题是什么
编辑:我已经更新了代码,使其在函数调用方面更加高效,而且速度更快,但我仍然有兴趣知道是否有一种矢量化的方法来实现这一点。这样如何:
indices_dup = [np.repeat(i, quantity) for i, quantity in enumerate(slots.Quantity.values)]
slots_ext = slots.loc[np.concatenate(indices_dup).ravel(), :]
# Add a counter per ID; used to 'shift' the duration along StartDate
slots_ext['counter'] = slots_ext.groupby('ID').cumcount()
# Calculate DateStart and DateEnd based on counter and Duration
slots_ext['DateStart'] = (slots_ext.counter) * slots_ext.Duration.values + slots_ext.StartDate
slots_ext['DateEnd'] = (slots_ext.counter + 1) * slots_ext.Duration.values + slots_ext.StartDate
slots_ext.loc[:, ['ID', 'DateStart', 'Duration', 'DateEnd']].reset_index(drop=True)
性能使用 收益率:
旧方法:
289 ms±4.59 ms/循环(7次运行的平均值±标准偏差,每个循环1次)
新方法:
8.13 ms±278µs/循环(7次运行的平均值±标准偏差,每个循环100次)
如果这有助于任何人:
我发现我的数据集每个ID有不同的增量,而@RubenB的初始答案不能处理这些。以下是我基于他/她的代码的最终解决方案:
# RubenB's code
indices_dup = [np.repeat(i, quantity) for i, quantity in enumerate(slots.Quantity.values)]
slots_ext = slots.loc[np.concatenate(indices_dup).ravel(), :]
# Calculate the cumulative sum of the delta per rota ID
slots_ext["delta_sum"] = slots_ext.groupby("ID")["Duration"].cumsum()
slots_ext["delta_sum"] = pd.to_timedelta(slots_ext["delta_sum"], unit="minutes")
# Use the cumulative sum to calculate the running end dates and then the start dates
first_value = slots_ext.StartDate[0]
slots_ext["EndDate"] = slots_ext.delta_sum.values + slots_ext.StartDate
slots_ext["StartDate"] = slots_ext.EndDate.shift(1)
slots_ext.loc[0, "StartDate"] = first_value
slots_ext.reset_index(drop=True, inplace=True)
您是否有一个比对应于“pythonic”代码的问题更具体/更少以观点为中心的问题?请看,坚持一个问题是有效的,只要仍然有实质性的“蟒蛇”标准删除。我已经猜测了剩余的物质是什么,并试图为此进行编辑;您对该编辑的审阅将不胜感激。看起来您正在大量调用
append
。据我所见,concat
几乎等同于append
,但它允许您建立一个数据帧列表,然后在一个调用中将它们连接在一起。@charlesduff感谢您的编辑,这正是我想要问的。非常感谢。Pythonic是一个糟糕的表达方式这是一个非常有趣的方式来看待它,虽然我还没有亲自测试它-基于你的基准,这看起来要快得多。非常感谢。
# RubenB's code
indices_dup = [np.repeat(i, quantity) for i, quantity in enumerate(slots.Quantity.values)]
slots_ext = slots.loc[np.concatenate(indices_dup).ravel(), :]
# Calculate the cumulative sum of the delta per rota ID
slots_ext["delta_sum"] = slots_ext.groupby("ID")["Duration"].cumsum()
slots_ext["delta_sum"] = pd.to_timedelta(slots_ext["delta_sum"], unit="minutes")
# Use the cumulative sum to calculate the running end dates and then the start dates
first_value = slots_ext.StartDate[0]
slots_ext["EndDate"] = slots_ext.delta_sum.values + slots_ext.StartDate
slots_ext["StartDate"] = slots_ext.EndDate.shift(1)
slots_ext.loc[0, "StartDate"] = first_value
slots_ext.reset_index(drop=True, inplace=True)