首页 > 解决方案 > 如何有效地将 (start_time,[time_deltas]) 转换为 (start_time, end_time)?

问题描述

本质上,我有提供开始时间、时隙数和每个时隙持续时间的数据。我想将其转换为开始和结束时间的数据框 - 我已经实现了,但我不禁认为它效率不高或特别 Pythonic。真实数据有多个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_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)

这可能会在其他地方得到回答,但我不知道如何正确搜索,因为我不确定我的问题是什么。

编辑:我已经更新了我的代码,使其函数调用效率更高,速度更快,但我仍然想知道是否有矢量化方法。

标签: pythonpython-3.xpandas

解决方案


这种方式怎么样:

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)

性能
查看更大数据帧(复制 1000 次)的性能,使用

slots_large = pd.concat([slots] * 1000, ignore_index=True).drop('ID', axis=1).reset_index().rename(columns={'index': 'ID'})

产量:
旧方法:289 ms ± 4.59 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
新方法:8.13 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


推荐阅读