【问题标题】:PANDAS: fast check whether integers fall into a set of intervalsPANDAS:快速检查整数是否落入一组区间
【发布时间】:2014-10-29 08:33:17
【问题描述】:

我有一个带有两个整数列 START 和 END 的 pandas 数据帧 INT,表示间隔 [START, END]。我需要检查整数 POS 是否落在这些间隔之一中,即是否存在 START

我有一个我认为是有效的解决方案,按顺序检查 POS 值并跟踪最近的间隔,这样我就可以开始希望接近我想要的间隔(如果它存在的话),我只需要在区间表中前进,检查是否有:

max_inter = max(intervals.END - intervals.START)
last_index = 0
def find(POS):
  global last_index, max_inter, intervals
  i = last_index
  while i > 0 and intervals.START.iloc[i] > POS - max_inter:
    i -= 1
  found = False
  while i < len(intervals) - 1 and intervals.START.iloc[i]) <= POS:
    if intervals.START.iloc[i] <= POS and POS <= intervals.END.iloc[i]:
      found = True
      break
    i += 1
  last_index = i
  return found

但是这比我想要的慢,因为它是在纯 python 中,有没有一种有效的方法可以在 pandas 或 numpy 中做到这一点?

我已经尝试过类似的东西

any((intervals.START <= POS) & (POS <= intervals.END))

但这比我的解决方案慢得多。有什么建议吗?我是否缺少库函数?

谢谢

编辑:也许我应该提到我需要检查一个(排序的)POS 值系列,我目前正在使用positions.map(find) 来生成一个布尔系列,也许有更好的方法可以做到这一点。我还必须为数千个位置和间隔执行此操作,这就是我对速度感兴趣的原因。

【问题讨论】:

  • intervals[(intervals['START'] &lt;= POS) &amp; (POS &lt;= intervals['END'])] 不起作用吗?
  • @EdChum 它可以工作,但速度要慢得多,即使它使用库函数,因为它每次都需要检查整个间隔表。我需要更高效的东西。
  • 另一个想法是像您已经订购 START 和 END 并在 START 和 END 上执行 np.searchsorted 传递 POS ,然后比较索引位置是否在此范围内,如果您了解我的话意思

标签: python pandas numpy intervals interval-tree


【解决方案1】:

你可以使用boolean indexing,就像这个SO answer的答案一样

就个人而言,我会使用eval,这对于大型数组非常有效,如下所示:

import pandas as pd

df = pd.DataFrame([[4,9],[2,5],[3,6],[1,4]], columns=['start','end'])
df
   start  end
0      4    9
1      2    5
2      3    6
3      1    4

pos = 3

df[df.eval('(start <= {}) & ({} <= end)'.format(pos,pos))]
   start  end
1      2    5
2      3    6
3      1    4

【讨论】:

  • 我在我的一小部分数据上尝试了您的建议,这需要 13 分钟,而我的解决方案需要 26 秒。我猜 eval 是 非常有效,但是每次检查整个 df 会浪费时间,而我一次只检查几个间隔。
  • @Giovo,您对建议的效率满意吗?
  • @Giovo,希望您能找到更好的解决方案,一定有知识渊博的人想出一个兼具效率和性能的解决方案;)
  • @Giovo,您是否考虑过使用 cython 来提高性能?
【解决方案2】:

虽然这不是纯粹的 pandas,但它的速度非常快。 NCLS 用于在

安装:

# pip install ncls
# or
# conda install -c bioconda ncls

设置:

import numpy as np
np.random.seed(0)
import pandas as pd

size = int(1e6)

dtype = np.int32
start = np.random.randint(int(1e7), size=size, dtype=dtype)
end = start + np.random.randint(int(1e3), size=size, dtype=dtype)

start2 = np.random.randint(int(1e7), size=size, dtype=dtype)
end2 = start2 + 1

intervals = pd.DataFrame({"Start": start, "End": end})
#           Start      End
# 0       8325804  8326332
# 1       1484405  1485343
# 2       2215104  2215531
# 3       5157699  5157834
# 4       8222403  8222497
# ...         ...      ...
# 999995  2981746  2982673
# 999996  1453668  1454251
# 999997  3325111  3325135
# 999998  4311711  4312465
# 999999  8089671  8090277
# 
# [1000000 rows x 2 columns]
points = pd.DataFrame({"Start": start2, "End": end2})
#           Start      End
# 0       1714420  1714421
# 1        980607   980608
# 2       5566444  5566445
# 3       2788107  2788108
# 4       6145575  6145576
# ...         ...      ...
# 999995  1824809  1824810
# 999996  6135851  6135852
# 999997  5190341  5190342
# 999998  7403307  7403308
# 999999  9732498  9732499
# 
# [1000000 rows x 2 columns]

执行:

from ncls import NCLS
n = NCLS(intervals.Start.values, intervals.End.values, intervals.index.values)
# Wall time: 421 ms

p_ix, i_ix = n.all_overlaps_both(points.Start.values, points.End.values, points.index.values)
# Wall time: 4.4s

len(i_ix) / 1e6
# 49.895545

i = intervals.reindex(i_ix).reset_index(drop=True)
p = points.reindex(p_ix).reset_index(drop=True)
p.columns = ["PStart", "PEnd"]
result = pd.concat([i, p], axis=1)
print(result)

#             Start      End   PStart     PEnd
# 0         1713535  1714442  1714420  1714421
# 1         1713560  1714479  1714420  1714421
# 2         1713670  1714590  1714420  1714421
# 3         1713677  1714666  1714420  1714421
# 4         1713694  1714627  1714420  1714421
# ...           ...      ...      ...      ...
# 49895540  9732449  9732910  9732498  9732499
# 49895541  9732491  9733159  9732498  9732499
# 49895542  9732492  9732621  9732498  9732499
# 49895543  9732496  9732653  9732498  9732499
# 49895544  9732496  9732512  9732498  9732499
# 
# [49895545 rows x 4 columns]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    相关资源
    最近更新 更多