You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于日期合并元组列表:同名元组保留最长时间跨度项

问题描述

我有一个元组列表,每个元组前两项是pd.Timestamp类型的日期,第三项是名称。需求如下:

  • 识别存在相同名称的多个元组
  • 判断元组的日期区间是否被其他同名元组的日期区间包含
  • 同时满足上述条件时,仅保留该名称下时间跨度最长的元组

示例数据

import pandas as pd

data = [(pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-01-21 00:00:00'), 'John'), 
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-01-21 00:00:00'), 'John'),
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'Jane'),
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John'),
        (pd.Timestamp('2017-01-21 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John'),
       (pd.Timestamp('2017-01-01 00:00:00'), pd.Timestamp('2017-02-10 00:00:00'), 'Jane'),]

期望输出

[(pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John'),
 (pd.Timestamp('2017-01-01 00:00:00'), pd.Timestamp('2017-02-10 00:00:00'), 'Jane')]

当前错误代码及输出

错误代码

names = set([x[2] for x in data])
to_remove = []

for i in range(len(data)):
  for j in range(i+1, len(data)):
    if data[i][2] == data[j][2]:
      if data[i][0] >= data[j][0] and data[i][1] <= data[j][1]:
        to_remove.append(j)
      elif data[j][0] >= data[i][0] and data[j][1] <= data[i][1]:
        to_remove.append(i)

data = [x for i,x in enumerate(data) if i not in set(to_remove)]

错误输出

[(pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-01-21 00:00:00'), 'John'),
 (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'Jane'),
 (pd.Timestamp('2017-01-21 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John')]
修复方案

原代码问题分析

原代码采用两两比较的方式标记需要删除的项,但逻辑存在漏洞:

  • 当存在多个同名称区间时,无法确保所有被包含的项都被标记删除
  • 没有优先筛选出能覆盖所有同名称区间的最长跨度项,导致部分未被完全覆盖的区间残留

方案一:纯Python实现

按名称分组处理,每组内筛选出能覆盖所有区间的最长跨度项:

import pandas as pd

def filter_longest_intervals(data):
    # 按名称分组存储区间
    name_groups = {}
    for interval in data:
        name = interval[2]
        name_groups.setdefault(name, []).append(interval)
    
    result = []
    for name, intervals in name_groups.items():
        # 为每个区间计算跨度,按跨度降序排序
        intervals_with_dur = [(end - start, start, end, name) for start, end, name in intervals]
        intervals_with_dur.sort(reverse=True, key=lambda x: x[0])
        
        # 找到第一个能覆盖当前组所有区间的项
        for dur, start, end, _ in intervals_with_dur:
            all_covered = True
            for s, e, _ in intervals:
                if not (s >= start and e <= end):
                    all_covered = False
                    break
            if all_covered:
                result.append((start, end, name))
                break
    return result

# 测试执行
data = [(pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-01-21 00:00:00'), 'John'), 
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-01-21 00:00:00'), 'John'),
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'Jane'),
        (pd.Timestamp('2017-01-02 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John'),
        (pd.Timestamp('2017-01-21 00:00:00'), pd.Timestamp('2017-02-04 00:00:00'), 'John'),
       (pd.Timestamp('2017-01-01 00:00:00'), pd.Timestamp('2017-02-10 00:00:00'), 'Jane'),]

print(filter_longest_intervals(data))

方案二:Pandas DataFrame实现

利用Pandas的分组和筛选功能,更简洁高效:

import pandas as pd

df = pd.DataFrame(data, columns=['start', 'end', 'name'])
# 计算每个区间的时间跨度
df['duration'] = df['end'] - df['start']

result = []
for name, group in df.groupby('name'):
    # 获取当前组所有区间的最小起始和最大结束(即需要覆盖的范围)
    min_start = group['start'].min()
    max_end = group['end'].max()
    # 按跨度降序排序,找到第一个覆盖目标范围的区间
    target = group.sort_values('duration', ascending=False)[
        (group['start'] <= min_start) & (group['end'] >= max_end)
    ].iloc[0]
    result.append((target['start'], target['end'], name))

print(result)

两种方案都能得到符合预期的输出,核心逻辑都是先按名称分组,再筛选出每组中能覆盖所有同名称区间的最长跨度项。

内容的提问来源于stack exchange,提问作者Blue482

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:45:56