【问题标题】:Merge two dataframes based on interval overlap根据间隔重叠合并两个数据帧
【发布时间】:2018-01-09 23:25:03
【问题描述】:

我有两个数据框 A 和 B:

例如:

import pandas as pd
import numpy as np
In [37]:
A = pd.DataFrame({'Start': [10, 11, 20, 62, 198], 'End': [11, 11, 35, 70, 200]})

A[["Start","End"]]
Out[37]:
Start   End
0   10  11
1   11  11
2   20  35
3   62  70
4   198 200
In [38]:
B = pd.DataFrame({'Start': [8, 5, 8, 60], 'End': [10, 90, 13, 75], 'Info': ['some_info0','some_info1','some_info2','some_info3']})

B[["Start","End","Info"]]
Out[38]:
Start   End Info
0   8   10  some_info0
1   5   90  some_info1
2   8   13  some_info2
3   60  75  some_info3

我想根据 A 的间隔(开始-结束)是否与 B 的间隔重叠,将列信息添加到数据帧 A。如果 A 间隔与多个 B 间隔重叠,则信息对应于应添加较短的间隔。

我一直在寻找如何解决这个问题,我发现了一些类似的问题,但他们的大部分答案都是使用iterrows(),在我的情况下,因为我正在处理巨大的数据框是不可行的。

我想要类似的东西:

A.merge(B,on="overlapping_interval", how="left")

然后删除重复项,保持来自较短间隔的信息。

输出应如下所示:

In [39]:
C = pd.DataFrame({'Start': [10, 11, 20, 62, 198], 'End': [11, 11, 35, 70, 200], 'Info': ['some_info0','some_info2','some_info1','some_info3',np.nan]})

C[["Start","End","Info"]]
Out[39]:
Start   End Info
0   10  11  some_info0
1   11  11  some_info2
2   20  35  some_info1
3   62  70  some_info3
4   198 200 NaN

我发现this question 非常有趣,因为它暗示了使用 pandas Interval 对象解决此问题的可能性。但是经过多次尝试,我还没有设法解决它。

有什么想法吗?

【问题讨论】:

  • iterrows 提供了一个生成器,因此非常适合大型 DataFrame。你已经试过了吗??
  • 不是内存问题,而是时间问题。
  • 现在我会编写你自己的算法来处理这个任务。您的第一步可能是对两个数据框进行排序。 --- Btw1:为了使您的问题更好,我建议举一个输出示例---- Btw2:如果您在控制台中打印 Dataframes,那么它们将被概述并且看起来更好,您的问题中的那些被严重概述.
  • @PauRS,您能否详细说明区间边缘(左和右) - 它们是关闭/打开的,请添加您想要的数据集?
  • 请发布所需的输出。

标签: python pandas intervals


【解决方案1】:

我建议做一个函数然后应用到行上:

首先我计算B 中的增量(结束 - 开始)以进行排序

B['delta'] = B.End - B.Start

然后是获取信息的函数:

def get_info(x):
    #Fully included
    c0 = (x.Start >= B.Start) & (x.End <= B.End)
    #start lower, end include
    c1 = (x.Start <= B.Start) & (x.End >= B.Start)
    #start include, end higher
    c2 = (x.Start <= B.End) & (x.End >= B.End)

    #filter with conditions and sort by delta
    _B = B[c0|c1|c2].sort_values('delta',ascending=True)

    return None if len(_B) == 0 else _B.iloc[0].Info #None if no info corresponding

然后你可以把这个函数应用到A:

A['info'] = A.apply(lambda x : get_info(x), axis='columns')


print(A)
   Start  End        info
0     10   11  some_info0
1     11   11  some_info2
2     20   35  some_info1
3     62   70  some_info3
4    198  200        None

注意:

  • 不要使用pd.Interval,而是自己创建条件。 cx 是您的区间定义,更改它们以获得确切的预期行为

【讨论】:

  • 非常感谢!这看起来是一个很好的解决方案。请注意一个错字:而不是 len(_dfb) 应该是 len(_B)
  • 我现在将在我的代码中实现它并检查它是否真的像看起来那样高效。我会告诉你的:)
  • 抱歉打错了,你放样品后我不得不重命名。让我知道它是否适合您的铅。
  • 正如预期的那样,超级高效。它持续了大约 5 分钟(A 有 120,000 行,B 有 400,000 行)。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2019-06-23
  • 2017-10-21
  • 2022-11-02
  • 2019-03-05
  • 2019-12-21
  • 1970-01-01
  • 2016-01-24
  • 1970-01-01
相关资源
最近更新 更多