python - Pandas-在各种日期时间范围内合并分钟数

标签 python pandas python-datetime binning

我正在寻找一种有效的方法来处理 pandas 中的以下数据。

我有一个包含数十万个开始和结束时间戳的数据框:

data_df
                      start_ts                     end_ts
0    2019-06-10 12:00:00+00:00  2019-06-10 22:30:00+00:00
1    2019-06-11 12:00:00+00:00  2019-06-11 13:30:00+00:00
2    2019-06-11 14:00:00+00:00  2019-06-11 19:00:00+00:00
3    2019-06-14 12:00:00+00:00  2019-06-14 18:30:00+00:00
4    2019-06-10 12:00:00+00:00  2019-06-10 21:30:00+00:00
5    2019-06-11 12:00:00+00:00  2019-06-11 18:30:00+00:00
...

我还有一组带标签的时间箱 (tp1-tp10)。每天有 10 个 bin,但是这些 bin 的时间每天都在变化(例如- tp1 可能是一天的 00:00 到 01:30,但之后是 00:00 到另一天的 01:45)。每个要处理的数据集有 7 天,每天有 10 个时间段,因此范围集的大小为 70,如下所示:

labeled_bins_df
                   start_range                  end_range  label
0    2019-06-10 00:00:00+00:00  2019-06-10 04:30:00+00:00    tp1
1    2019-06-10 04:30:00+00:00  2019-06-10 09:45:00+00:00    tp2
2    2019-06-10 09:45:00+00:00  2019-06-10 12:30:00+00:00    tp3
...

我想要的是一个包含原始 data_df 数据的表,但包含从 tp1tp10 的附加列,其中包含每行分钟:

timed_bins
                      start_ts                     end_ts    tp1    tp2    tp3    tp4 ...
0    2019-06-10 12:00:00+00:00  2019-06-10 22:30:00+00:00      0      0     30    120 ...
1    2019-06-11 12:00:00+00:00  2019-06-11 13:30:00+00:00      0     45     45      0 ...

我目前正在天真地执行此操作,遍历我的行,并搜索每个数据行所在的 bin,正如您可以想象的那样,这非常慢。是否可以执行任何 pandas-fu 来在日期时间范围内进行这种分箱?

编辑:一个想法,可能有助于思考一个新的方向。如果我要将我所有的时间戳(在我的数据中和在我的标记箱中)转换为 unix 时间戳(自 1970 年 1 月 1 日以来的秒数),那么这将是基于整数范围而不是日期的装箱/求和问题.然后这将产生每个箱子中的秒数,简单地除以 60,我得到每个箱子中的分钟数。这消除了对日期边界等的所有担忧。

编辑 2:根据要求,这是一组简化的示例数据,使用三个不同的时间段。我专门制作了一个数据样本(第二行)跨越 2 天。此外,还有一个显示预期输出的 result_df

data_samples = [
    {'start_ts': '2019-06-10T12:00:00+0000', 'end_ts': '2019-06-10T22:30:00+0000'},
    {'start_ts': '2019-06-10T22:00:00+0000', 'end_ts': '2019-06-11T05:30:00+0000'},
    {'start_ts': '2019-06-10T10:00:00+0000', 'end_ts': '2019-06-10T14:15:00+0000'},
    {'start_ts': '2019-06-12T08:07:00+0000', 'end_ts': '2019-06-12T18:22:00+0000'},
    {'start_ts': '2019-06-11T14:03:00+0000', 'end_ts': '2019-06-11T15:30:00+0000'},
    {'start_ts': '2019-06-11T02:33:00+0000', 'end_ts': '2019-06-11T10:31:00+0000'}
]

data_set = [{
    'start_ts': datetime.datetime.strptime(x['start_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'end_ts': datetime.datetime.strptime(x['end_ts'], '%Y-%m-%dT%H:%M:%S%z')} for x in data_samples]

data_df = pd.DataFrame(data_set)[['start_ts', 'end_ts']]

time_bin_samples = [
    {'start_ts': '2019-06-10T00:00:00+0000', 'end_ts': '2019-06-10T08:15:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-10T08:15:00+0000', 'end_ts': '2019-06-10T18:00:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-10T18:00:00+0000', 'end_ts': '2019-06-11T00:00:00+0000', 'label': 't3'},

    {'start_ts': '2019-06-11T00:00:00+0000', 'end_ts': '2019-06-11T09:00:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-11T09:00:00+0000', 'end_ts': '2019-06-11T19:15:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-11T19:15:00+0000', 'end_ts': '2019-06-12T00:00:00+0000', 'label': 't3'},

    {'start_ts': '2019-06-12T00:00:00+0000', 'end_ts': '2019-06-12T10:30:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-12T10:30:00+0000', 'end_ts': '2019-06-12T12:00:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-12T12:00:00+0000', 'end_ts': '2019-06-13T00:00:00+0000', 'label': 't3'},
]

time_bin_set = [{
    'start_ts': datetime.datetime.strptime(x['start_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'end_ts': datetime.datetime.strptime(x['end_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'label': x['label']} for x in time_bin_samples
]

time_bin_df = pd.DataFrame(time_bin_set)[['start_ts', 'end_ts', 'label']]

result_set = [
    {'t1': 0, 't2': 360, 't3': 270},
    {'t1': 330, 't2': 0, 't3': 120},
    {'t1': 0, 't2': 255, 't3': 0},
    {'t1': 143, 't2': 90, 't3': 382},
    {'t1': 0, 't2': 87, 't3': 0},
    {'t1': 387, 't2': 91, 't3': 0}
]

result_df = pd.DataFrame(result_set)

最佳答案

我知道迭代数据帧的行效率不高。

在这里,我将尝试使用 merge_asof 来识别 data_df 中每行的第一个和最后一个 bin。

然后我将通过迭代一次数据框值来构建子数据框列表,以便添加与一行对应的所有 bin 并连接该列表。

从那里足以计算每个 bin 的时间间隔并使用 pivot_table 获得预期结果。

代码可以是:

# store the index as a column to make sure to keep it
data_df = data_df.rename_axis('ix').reset_index().sort_values(
    ['end_ts', 'start_ts'])
time_bin_df = time_bin_df.rename_axis('ix').reset_index().sort_values(
    ['end_ts', 'start_ts'])

# identify first and last bin per row
first = pd.merge_asof(data_df, time_bin_df, left_on='start_ts',
                      right_on='end_ts', suffixes=('', '_first'),
                      direction='forward').values
last = pd.merge_asof(data_df, time_bin_df, left_on='end_ts', right_on='start_ts',
                     suffixes=('', '_ bin')).values

# build a list of bin dataframes (one per row in data_df)
data = []
for i, val in enumerate(first):
    elt = time_bin_df[(time_bin_df['ix']>=val[3])
                      &(time_bin_df['ix']<=last[i][3])].copy()
    # compute the begin and end of the intersection of the period and the bin
    elt.loc[elt['start_ts']<val[1], 'start_ts'] = val[1]
    elt.loc[elt['end_ts']>val[2], 'end_ts'] = val[2]
    elt['ix_data'] = val[0]
    data.append(elt)

# concat everything
tmp = pd.concat(data)

# compute durations in minutes
tmp['duration'] = (tmp['end_ts'] - tmp['start_ts']).dt.total_seconds() / 60

# pivot to get the expected result
result_df = tmp.pivot_table('duration', 'ix_data', 'label', 'sum', fill_value=0
                            ).rename_axis(None).rename_axis(None, axis=1)

这可能需要一些时间,因为构建数据帧列表仍然需要一个冗长的操作,但其他操作应该矢量化。

关于python - Pandas-在各种日期时间范围内合并分钟数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56915254/

相关文章:

python - 没有名为 'Cython' 的模块与 tar.gz 的 pip 安装

Python 简单的反向传播没有按预期工作

python - pandas.errors.EmptyDataError,但文件不为空

python - 从 Pandas 中的 DatetimeIndex 列出月份和年份

python - 为什么找不到 Python datetime time delta?

python - 如何在Python中获取日期范围内的一些特定工作日的列表

python - Pandas 适用于属性而不是功能

python - 删除每组中最后一个子组对应的行

python - Tensorflow神经网络损失没有减少

pandas - 使用 sklearn 缩小高维 pandas 的数据帧数据