【问题标题】:Check if row's date range overlap any previous rows date range in Python / Pandas Dataframe检查行的日期范围是否与 Python / Pandas Dataframe 中的任何先前行的日期范围重叠
【发布时间】:2021-08-03 12:30:26
【问题描述】:

我在 pandas 数据框中有一些数据,其中包含一个排名列、一个开始日期和一个结束日期。数据按排名列从最低到最高排序(因此开始/结束日期是无序的)。我希望删除日期范围与任何以前的行重叠的每一行

通过一个玩具示例:

原始数据

Rank    Start_Date  End_Date
1   1/1/2021    2/1/2021
2   1/15/2021   2/15/2021
3   12/7/2020   1/7/2021
4   5/1/2020    6/1/2020
5   7/10/2020   8/10/2020
6   4/20/2020   5/20/2020

想要的结果

Rank    Start_Date  End_Date
1   1/1/2021    2/1/2021
4   5/1/2020    6/1/2020
5   7/10/2020   8/10/2020

解释:第 2 行被删除,因为它的开始与第 1 行重叠,第 3 行被删除,因为它的结束与第 1 行重叠。第 4 行被保留,因为它不与任何先前保留的行(即第 1 行)重叠。同样,保留第 5 行,因为它不与第 1 行或第 4 行重叠。第 6 行被删除,因为它与第 4 行重叠。

尝试:

  1. 我可以使用 np.where 来检查前一行与当前行并创建一个“重叠”列,然后过滤此列。但这不满足我的要求(即在上面的玩具示例中,第 3 行将被包括在内,因为它不与第 2 行重叠,但由于它与第 1 行重叠,因此应排除在外)。

    df['overlap'] = np.where((df['start']> df['start'].shift(1)) &
                                  (df['start'] < df['end'].shift(1)),1 ,0)
    df['overlap'] = np.where((df['end'] < df['end'].shift(1)) &
                                  (df['end'] > df['start'].shift(1)), 1, df['overlap'])

  1. 我已经尝试了基于这个问题Removing 'overlapping' dates from pandas dataframe 的答案的实现,使用结束日期的回溯期,但是我的开始日期和结束日期之间的天数不是恒定的,而且它似乎不会产生还是正确答案
    target = df.iloc[0]
    day_diff = abs(target['End_Date'] - df['End_Date'])
    day_diff = day_diff.reset_index().sort_values(['End_Date', 'index'])
    day_diff.columns = ['old_index', 'End_Date']
    non_overlap = day_diff.groupby(day_diff['End_Date'].dt.days // window).first().old_index.values
    results = df.iloc[non_overlap]

谢谢,任何帮助都将非常非常感激!

【问题讨论】:

  • 日期格式是什么?月/日/年?
  • 是 - 美国格式。

标签: python pandas


【解决方案1】:

如果 (a) End2>Start1 和 (b) Start2numpy.triu 仅计算与前几行的比较:

a = np.triu(df['End_Date'].values>df['Start_Date'].values[:,None])
b = np.triu(df['Start_Date'].values<df['End_Date'].values[:,None])

好的行是那些在 a&amp;b 的对角线上只有 True 的行

df[(a&b).sum(0)==1]

输出:

Rank Start_Date   End_Date
   1 2021-01-01 2021-02-01
   4 2020-05-01 2020-06-01
   5 2020-07-10 2020-08-10

注意。由于需要计算行的组合,这种方法在数组变大时会占用大量内存,但应该很快

【讨论】:

  • 我忘了提到我首先使用pandas.to_datetime将日期列转换为日期时间类型
【解决方案2】:

另一个有助于内存使用的选项是 IntervalIndex 和 for 循环的组合:

转换日期:

df.Start_Date = df.Start_Date.transform(pd.to_datetime, format='%m/%d/%Y')
df.End_Date = df.End_Date.transform(pd.to_datetime, format='%m/%d/%Y')

创建间隔索引:

intervals = pd.IntervalIndex.from_arrays(df.Start_Date, 
                                         df.End_Date, 
                                         closed='both')

运行一个 for 循环(这样可以避免广播,虽然速度很快,但可能会占用大量内存,具体取决于数组大小):

index = np.arange(intervals.size)
keep = [] # indices of `df` to be retained    

# get rid of the indices where the intervals overlap
for interval in intervals:
    keep.append(index[0])
    checks = intervals[index].overlaps(intervals[index[0]])
    if checks.any():
        index = index[~checks]
    else:
        break
    if index.size == 0:
        break
df.loc[keep]
 
   Rank Start_Date   End_Date
0     1 2021-01-01 2021-02-01
3     4 2020-05-01 2020-06-01
4     5 2020-07-10 2020-08-10

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多