假设我有一个 pandas 数据框,其中开始时间和结束时间之间有时间间隔,然后是与每个间隔关联的值。
import random
import time
import numpy as np
def random_date(input_dt = None):
if input_dt is None:
start = 921032233
else:
start = dt.datetime.timestamp(pd.to_datetime(input_dt))
d = random.randint(start, int(time.time()))
return dt.datetime.fromtimestamp(d).strftime('%Y-%m-%d %H:%M:%S')
date_ranges = []
for _ in range(200):
date_range = []
for i in range(2):
if i == 0:
date_range.append(random_date())
else:
date_range.append(random_date(date_range[0]))
date_ranges.append(date_range)
date_ranges_df = pd.DataFrame(date_ranges, columns=['start_dt', 'end_dt'])
date_ranges_df['value'] = np.random.random((date_ranges_df.shape[0], 1))
我可以通过两种方式来解决这个问题,我会接受其中一个答案。
获取每个不同重叠区间的总和。这意味着应该有一个与变化的(不重叠且顺序完整的)时间间隔相关的总和。即,如果重叠时间间隔在一段时间内保持不变,则总和将保持不变并具有单个值 - 那么当重叠间隔以任何方式发生变化(删除或添加时间间隔)时,将计算新的总和。这可能涉及一些桌面上的自合并。
另一种(也许更简单)的方法是定义一个标准时间间隔,例如 1 小时,并询问该小时段内所有重叠间隔的总和是多少?
生成的数据帧应具有类似的结构,其中开始时间和结束时间后跟表示该间隔中所有值之和的值列。
每个不同重叠区间的总和: 这比较复杂,因为我们需要检测重叠周期,然后需要对重叠周期的值求和。
定义的小时段内所有重叠间隔的总和: 这需要将我们的数据重新采样为定期的每小时频率(将不规则间隔转换为定期的每小时频率并聚合这些每小时间隔内的“
value
”数据的过程),然后计算总和。
import pandas as pd
# Convert to datetime objects
date_ranges_df['start_dt'] = pd.to_datetime(date_ranges_df['start_dt'])
date_ranges_df['end_dt'] = pd.to_datetime(date_ranges_df['end_dt'])
# Sort by start_dt
date_ranges_df = date_ranges_df.sort_values(by='start_dt')
# Create list of tuples: [(start1, end1, value1), (start2, end2, value2),...]
intervals = list(date_ranges_df.itertuples(index=False, name=None))
# Split intervals into start and end points and sort them
points = sorted([(start, value, 1) for start, _, value in intervals] + [(end, value, -1) for _, end, value in intervals])
result = []
current_value = 0
current_start = points[0][0]
for i, (point, value, change) in enumerate(points):
if i > 0 and point != points[i-1][0]:
result.append((current_start, points, current_value))
current_start = point
current_value += change * value
# Create a dataframe from result
result_df = pd.DataFrame(result, columns=['start_dt', 'end_dt', 'value'])
这可以通过将间隔转换为单独的点来实现,每个点都标记有一个值和一个标志,指示它是起点 (
1
) 还是终点 (-1
)。
然后对点进行排序。当我们迭代这些点时,当我们到达一个不等于前一个点的点(表示一个新的间隔段)时,我们记录前一个段以及到该点的累积值。然后,我们更新当前的起点,并在遇到起点和终点时继续添加或减去值。
生成的 DataFrame
result_df
包含不重叠的段,以及每个段期间活动的间隔值的总和。
# Convert to datetime objects
date_ranges_df['start_dt'] = pd.to_datetime(date_ranges_df['start_dt'])
date_ranges_df['end_dt'] = pd.to_datetime(date_ranges_df['end_dt'])
# Resample to 1-hour intervals
hourly_intervals = pd.date_range(date_ranges_df['start_dt'].min(), date_ranges_df['end_dt'].max(), freq='H')
hourly_df = pd.DataFrame()
for start in hourly_intervals:
end = start + timedelta(hours=1)
# Get intervals that overlap with current hour
mask = ((date_ranges_df['start_dt'] < end) & (date_ranges_df['end_dt'] > start))
overlap = date_ranges_df.loc[mask]
if not overlap.empty:
# Sum values of overlapping intervals
total_value = overlap['value'].sum()
hourly_df = hourly_df.append({'start_dt': start, 'end_dt': end, 'value': total_value}, ignore_index=True)
# Convert column types
hourly_df['start_dt'] = pd.to_datetime(hourly_df['start_dt'])
hourly_df['end_dt'] = pd.to_datetime(hourly_df['end_dt'])
注意:代码假定
start_dt
和 end_dt
列格式正确,并且 value
列包含数值。这应该为这两个代码提供具有相同结构的结果
DataFrame
,包括“start_dt
”、“end_dt
”和“值”列。start_dt
”和“end_dt
”是每个区间的边界,“值”是这些边界内所有重叠区间的总和。
对于第一种方法,它将每个不同重叠间隔的值相加,它看起来像:
start_dt end_dt value
0 2023-07-04 08:06:02+00:00 2023-07-04 14:12:22+00:00 1.2789
1 2023-07-04 17:02:02+00:00 2023-07-04 23:17:54+00:00 0.8672
2 2021-06-30 00:45:11+00:00 2021-06-30 05:32:20+00:00 1.4563
...
对于第二种方法,每小时求和值:
start_dt end_dt value
0 2023-07-04 08:00:00+00:00 2023-07-04 09:00:00+00:00 0.7489
1 2023-07-04 09:00:00+00:00 2023-07-04 10:00:00+00:00 0.5321
2 2023-07-04 10:00:00+00:00 2023-07-04 11:00:00+00:00 0.4563
...
注意:
value
列包含每行在“start_dt
”到“end_dt
”范围内的所有重叠间隔值的总和。
第二种方式: 由于我是新用户,我只能创建答案。因此,这个答案很大程度上建立在@VonC的答案上。
据我测试,目前对他们没有价值的间隔会在过程中丢失,并且重叠间隔不会被累加。为了解决第一个问题,我添加了 else 条件,如果时间间隔内不存在任何值,则该条件会添加零。为了解决第二个问题,我添加了 .sum(),它还将total_value 转换为浮点数。此外,还避免了 .append 的使用。
# Convert to datetime objects
date_ranges_df['start_dt'] = pd.to_datetime(date_ranges_df['start_dt'])
date_ranges_df['end_dt'] = pd.to_datetime(date_ranges_df['end_dt'])
# Resample to 1-hour intervals
hourly_intervals = pd.date_range(date_ranges_df['start_dt'].min(),
date_ranges_df['end_dt'].max(), freq='H')
hourly_df = pd.DataFrame()
for start in hourly_intervals:
end = start + timedelta(hours=1)
# Get intervals that overlap with current hour
mask = ((date_ranges_df['start_dt'] < end) & (date_ranges_df['end_dt'] > start))
overlap = date_ranges_df.loc[mask]
if not overlap.empty:
# Sum values of overlapping intervals
total_value = overlap['value'].sum()
new_entry = pd.Series({'start': start, 'end': end, 'value': total_value})
hourly_df = pd.concat([hourly_df, new_entry.to_frame().T], ignore_index=True)
else:
new_entry = pd.Series({'start': start, 'end': end, 'value': 0})
hourly_df = pd.concat([hourly_df, new_entry.to_frame().T], ignore_index=True)
# Convert column types
hourly_df['start_dt'] = pd.to_datetime(hourly_df['start_dt'])
hourly_df['end_dt'] = pd.to_datetime(hourly_df['end_dt'])