Pandas 数据帧持续时间扩展到分钟数据

Pandas Dataframe Time Duration Expand to Minute Data

我正在接收数据,其中包含一个 'StartTime' 和一个 'Duration' 的活动时间。当我需要在多天的指定时间 运行ge 进行计算时,这很难处理。我想将这些数据分解为更详细的数据,以便将来的计算更容易。请查看示例以获得更好的理解。

我目前拥有的数据:

data = {'StartTime':['2018-12-30 12:45:00+11:00','2018-12-31 16:48:00+11:00','2019-01-01 04:36:00+11:00','2019-01-01 19:27:00+11:00','2019-01-02 05:13:00+11:00'],
        'Duration':[1,1,3,1,2],
        'Site':['1','2','3','4','5']    
}

df = pd.DataFrame(data)
df['StartTime'] = pd.to_datetime(df['StartTime']).dt.tz_localize('utc').dt.tz_convert('Australia/Melbourne')

我想要的:

data_expected = {'Time':['2018-12-30 12:45:00+11:00','2018-12-31 16:48:00+11:00','2019-01-01 04:36:00+11:00','2019-01-01 04:37:00+11:00','2019-01-01 19:27:00+11:00','2019-01-02 05:13:00+11:00','2019-01-02 05:14:00+11:00'],
        'Duration':[1,1,1,1,1,1,1],
        'Site':['1','2','3','3','4','5','5']    
}

df_expected = pd.DataFrame(data_expected)
df_expected['Time'] = pd.to_datetime(df_expected['Time']).dt.tz_localize('utc').dt.tz_convert('Australia/Melbourne')

想看看有没有人有解决这个问题的好办法。实际上,对于持续时间超过 1 分钟的每一分钟,我需要将持续时间 >1 的数据行复制为时间 +1 分钟。有没有办法在不创建全新数据框的情况下做到这一点?

******** 编辑 ********

回应@DavidErickson 的回答。把它放在这里是因为我不能在评论中放图片。我运行陷入了一点麻烦。 df1 是原始数据帧的子集。应用提供的代码后,df2 是 df1。您可以看到添加到索引 635 的时间不正确。

根据Duration列使用df.index.repeat添加相关行数。然后使用 .groupbycumcount 创建一个掩码,在基本时间之上添加适当的分钟数。

输入:

data = {'StartTime':['2018-12-30 12:45:00+11:00','2018-12-31 16:48:00+11:00','2019-01-01 04:36:00+11:00','2019-01-01 19:27:00+11:00','2019-01-02 05:13:00+11:00'],
        'Duration':[1,1,2,1,2],
        'Site':['1','2','3','4','5']    
}
df = pd.DataFrame(data)
df['StartTime'] = pd.to_datetime(df['StartTime'])

代码:

df = df.loc[df.index.repeat(df['Duration'])]
mask = df.groupby('Site').cumcount()
df['StartTime'] = df['StartTime'] + pd.to_timedelta(mask, unit='m')
df = df.append(df).sort_values('StartTime').assign(Duration=1).drop_duplicates()
df

输出:

    StartTime                   Duration    Site
0   2018-12-30 12:45:00+11:00   1           1
1   2018-12-31 16:48:00+11:00   1           2
2   2019-01-01 04:36:00+11:00   1           3
2   2019-01-01 04:37:00+11:00   1           3
2   2019-01-01 04:38:00+11:00   1           3
3   2019-01-01 19:27:00+11:00   1           4
4   2019-01-02 05:13:00+11:00   1           5
4   2019-01-02 05:14:00+11:00   1           5

如果您 运行 遇到内存问题,那么您也可以尝试使用 dask。我已经包含了@jlandercy 的 pandas 答案并更改为 dask 语法,因为我不确定 pandas 操作 index.repeat 是否适用于 dask。这是关于 funcitons/operations 的文档。我会研究代码中的那些 https://docs.dask.org/en/latest/dataframe-api.html#dask.dataframe.read_sql_table:

import dask.dataframe as dd
#read as a dask dataframe from csv or SQL or other
df = dd.read_csv(files) #df = dd.read_sql_table(table, uri, index_col='StartTime')
df['offset'] = df['Duration'].apply(lambda x: list(range(x)))
df = dd.explode('offset')
df['offset'] = df['offset'].apply(lambda x: dd.Timedelta(x, unit='T'))
df['StartTime'] += df['offset']
df["Duration"] = 1

我认为您可能还想解决 Duration > 2 的用例。

对于修改后的给定输入:

data = {'StartTime':['2018-12-30 12:45:00+11:00','2018-12-31 16:48:00+11:00','2019-01-01 04:36:00+11:00','2019-01-01 19:27:00+11:00','2019-01-02 05:13:00+11:00'],
        'Duration':[1,1,3,1,2],
        'Site':['1','2','3','4','5']    
}

df = pd.DataFrame(data)
df['StartTime'] = pd.to_datetime(df['StartTime'])

这段代码应该可以解决问题:

df['offset'] = df['Duration'].apply(lambda x: list(range(x)))
df = df.explode('offset')
df['offset'] = df['offset'].apply(lambda x: pd.Timedelta(x, unit='T'))
df['StartTime'] += df['offset']
df["Duration"] = 1

基本上,它的工作原理如下:

  • 根据持续时间值创建一个整数列表;
  • 复制具有连续整数偏移量的行 (explode);
  • 将整数偏移量转换为timedelta偏移量;
  • 执行 datetime 算术并重置“持续时间”字段。

结果大约是:

                  StartTime  Duration Site   offset
0 2018-12-30 12:45:00+11:00         1    1 00:00:00
1 2018-12-31 16:48:00+11:00         1    2 00:00:00
2 2019-01-01 04:36:00+11:00         1    3 00:00:00
2 2019-01-01 04:37:00+11:00         1    3 00:01:00
2 2019-01-01 04:38:00+11:00         1    3 00:02:00
3 2019-01-01 19:27:00+11:00         1    4 00:00:00
4 2019-01-02 05:13:00+11:00         1    5 00:00:00
4 2019-01-02 05:14:00+11:00         1    5 00:01:00