【问题标题】:How to count overlaps and find overlapping partners for pandas?如何计算重叠并为 pandas 找到重叠的伙伴?
【发布时间】:2019-04-15 10:59:55
【问题描述】:

我很困惑如何使用pandas 最有效地做到这一点。

我有以下熊猫DataFrame,目前包含两列startsends,分别代表区间[1, 10][5, 15][3, 8]

import pandas as pd

dict1 = {'start': [1, 5, 3], 'end': [10, 15, 8]}

df = pd.DataFrame(dict1)
print(df)
   start  end
0      1   10
1      5   15
2      3    8

从 0 开始,我想计算区间如何重叠。这是正确的合并结构(不用太担心闭/开区间):

区间[0, 1]没有区间,[1,3]有1个区间(来自[1, 10]),[3, 5]有两个区间([1, 10][3, 8]这对),区间[5, 8]有三个区间( [1, 10], [3, 8], [5, 15])、[8, 10] 有两个区间([1, 10], [5, 15])等

以表格形式总结结果,预期的结果是:

  start  end  total  interval
0      0   1   0     []
1      1   3   1     [[1, 10]] 
2      3   5   2     [[1, 10], [3, 8]]
3      5   8   3     [[1, 10], [3, 8], [5, 15]]
4      8   10  2     [[1, 10], [5, 15]]
5      10  15  1     [[5, 15]]
6      15  75  0     []

其中intervals 列当前是包含每个间隔列表的列表列表。 (我包括了一个大于 15 的整数,以表明那里什么都没有;75 是任意的)

我应该如何使用 pandas 完成上述任务?这三个步骤似乎是:

(1) 将区间解构为给定任何其他区间并集的部分

(2) 计算重叠区间

(3) 存储间隔以供以后检索

pandas 是否还配备了这项操作?

【问题讨论】:

  • 我没有得到total列的逻辑
  • @U9-Forward 我应该更好地命名该列。表示区间数,或interval列中列表的长度

标签: python pandas


【解决方案1】:

来自pandas 0.24.0可以使用pd.Interval.overlaps

endpoints = df.stack().sort_values().reset_index(drop=True)
intervals = pd.DataFrame({'start':endpoints.shift().fillna(0), 
                          'end':endpoints}).astype(int)
# construct the list of intervals from the endpoints
intervals['intv'] = [pd.Interval(a,b) for a,b in zip(intervals.start, intervals.end)]

# these are the original intervals
orig_invt = pd.arrays.IntervalArray([pd.Interval(a,b) for a,b in zip(df.start, df.end)])

# walk through the intervals and compute the intersections
intervals['total'] = intervals.intv.apply(lambda x: org_intv.overlaps(x).sum())

输出:

+----+--------+------+-----------+-------+
|    | start  | end  |   intv    | total |
+----+--------+------+-----------+-------+
| 0  |     0  |   1  | (0, 1]    |     0 |
| 1  |     1  |   3  | (1, 3]    |     1 |
| 2  |     3  |   5  | (3, 5]    |     2 |
| 3  |     5  |   8  | (5, 8]    |     3 |
| 4  |     8  |  10  | (8, 10]   |     2 |
| 5  |    10  |  15  | (10, 15]  |     1 |
+----+--------+------+-----------+-------+

【讨论】:

    【解决方案2】:

    使用标准 for 循环方法:

    bounds = np.unique(df)
    if 0 not in bounds: bounds = np.insert(bounds, 0, 0)
    
    end = 75
    bounds = np.append(bounds, end)
    
    total = []
    interval = []
    for i in range(len(bounds)-1):
        # Find which intervals fit
        ix = (df['start'] <= bounds[i]) & (df['end'] >= bounds[i+1])
    
        total.append(np.sum(ix))
        interval.append(df[ix].values.tolist())
    
    pd.DataFrame({'start': bounds[:-1], 'end': bounds[1:], 'total': total, 'interval': interval})
    

    【讨论】:

    • 这些方法通常更直观,尽管我认为 for 循环确实会影响性能。谢谢!
    • 我同意。可读性是值得的。根据您的任务的计算量,您可以轻松使用它,而不会注意到计算时间的任何差异。如果没有,其他帖子可能会为您节省一些宝贵的计算时间。
    【解决方案3】:

    我正在使用numpyboardcast

    s1=df1.end.values
    s2=df1.start.values
    s3=df2.end.values
    s4=df2.start.values
    f=pd.DataFrame(((s1[:,None]>=s3)&(s2[:,None]<=s4)).T,index=df2.index)
    df2['total']=f.sum(1)
    df2['interval']=[(df1.values[x]).tolist() for x in f.values]
    df2
    Out[289]: 
       start  end  total                    interval
    0      0    1      0                          []
    1      1    3      1                   [[1, 10]]
    2      3    5      2           [[1, 10], [3, 8]]
    3      5    8      3  [[1, 10], [5, 15], [3, 8]]
    4      8   10      2          [[1, 10], [5, 15]]
    5     10   15      1                   [[5, 15]]
    6     15   75      0                          []
    

    【讨论】:

      猜你喜欢
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多