Pandas 数据框 - 删除重叠间隔

Pandas Data Frame - Remove Overlapping Intervals

假设您有一个可以使用以下代码创建的 Pandas 数据框:

test_df = pd.DataFrame(
    {'start_date': ['2021-07-01', '2021-07-02', '2021-07-03',
                    '2021-07-04', '2021-07-05', '2021-07-06'],
     'end_date': ['2021-07-03', '2021-07-04', '2021-07-05',
                  '2021-07-06', '2021-07-07', '2021-07-08'],
     'returns': [1, 1, 0.99, 0.98, 0.99, 0.97]})
test_df = test_df.sort_values('returns', ascending=False)

假设 returns 总是排序的,什么是删除重叠间隔的有效方法?我不想使用循环,因为数据集很大,有没有向量化的方法来实现下面的输出?

预期输出

+------------+------------+---------+
| start_date |  end_date  | returns |
+------------+------------+---------+
| 2021-07-01 | 2021-07-03 |       1 |
| 2021-07-05 | 2021-07-07 |    0.99 |
+------------+------------+---------+

@mozway that works without a loop by using np.triu()(numpy 的上三角)有一个回答。

为您的 DataFrame 稍作更改:

import numpy as np
test_df["start_date"] = pd.to_datetime(test_df["start_date"])
test_df["end_date"] = pd.to_datetime(test_df["end_date"])

a = np.triu(test_df['end_date'].values > test_df['start_date'].values[:, None])
b = np.triu(test_df['start_date'].values < test_df['end_date'].values[:, None])
test_df[(a & b).sum(0) == 1]
#  start_date   end_date  returns
#0 2021-07-01 2021-07-03     1.00
#4 2021-07-05 2021-07-07     0.99

解释:

test_df['end_date'].values > test_df['start_date'].values[:, None]
#array([[ True,  True,  True,  True,  True,  True],
#       [ True,  True,  True,  True,  True,  True],
#       [False,  True,  True,  True,  True,  True],
#       [False, False, False,  True,  True,  True],
#       [False, False,  True,  True,  True,  True],
#       [False, False, False,  True, False,  True]])

此 returns 数组,其中 end_date 值大于 start_date 值。这将查看开始日期和结束日期的每个组合(每个结束日期作为一列,每个开始日期作为一行。如果满足条件,则返回 True

取上三角意味着只有开始日期在结束日期之前或之前的组合(就 test_df 行而言)是 True,其余的 False

另一方面,

b 查找结束日期大于开始日期的所有实例(每个开始日期为一列,每个结束日期为一行)。对于 test_df['start_date'].values < test_df['end_date'].values[:, None] 的第一行,它正在查看第一个结束日期 2021-07-03 的组合以及它是否大于开始日期。

这个的上三角意味着只有开始日期在数据框中的结束日期之前的事件是 True

解构最后一行test_df[(a & b).sum(0) == 1]:

  • (a & b) 是一个数组,其中 True 出现在两个数组都是 True.
  • 的地方
  • (a & b).sum(0) 对该数组的每一列求和,其中 True == 1False == 0.
  • (a & b).sum(0) == 1 是只有一次 ab 一起出现的行 True。我们只需要这些,因为我们需要行,其中:
  1. 开始日期早于之前(或当前)的结束日期

  1. 结束日期晚于之前(或当前)的开始日期

然而

如果这种情况发生不止一次,那么一定有重叠。因为在这种情况下,前导对角线将始终为 True(因为给定数据框行的结束日期必须始终大于开始日期),如果这种情况发生不止一次,则意味着:

  1. 开始日期早于上一个结束日期和当前结束日期

  1. 结束日期大于之前的开始日期和当前开始日期

这意味着这里必须有重叠。

我知道这很令人困惑,但它确实有道理!