【问题标题】:How can I efficiently convert (start_time,[time_deltas]) to (start_time, end_time)?如何有效地将 (start_time,[time_deltas]) 转换为 (start_time, end_time)?
【发布时间】:2020-05-09 23:13:35
【问题描述】:

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

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

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

【问题讨论】:

  • 你有一个比“pythonic”代码对应的问题更具体/更少以意见为中心的问题吗?请参阅Meta Stack Exchange 上的Are Pythonic questions opinion-based?,认为问题是有效的只要其中仍然存在删除“pythonic”标准的内容。我已经猜测剩余的内容会是什么,并试图为此进行编辑;我们将不胜感激您对该编辑的评论。
  • 您似乎正在给append 打很多电话。从我所见(以及others suggest)来看,concat 几乎等同于append,但可以让您建立一个数据帧列表,然后在一次调用中将它们连接在一起。跨度>
  • @CharlesDuffy 感谢您的编辑,这正是我想问的。谢谢你。 Pythonic 是一种糟糕的表达方式

标签: python python-3.x pandas


【解决方案1】:

这样怎么样:

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)

【讨论】:

  • 这是一种非常有趣的查看方式,虽然我自己还没有测试过 - 根据您的基准测试,这看起来要快得多。谢谢!
【解决方案2】:

如果这对任何人都有帮助: 我发现我的数据集的每个 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)

【讨论】:

    猜你喜欢
    • 2021-06-07
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    • 2020-11-17
    相关资源
    最近更新 更多