PANDAS:快速检查整数是否属于一组间隔

时间:2014-10-29 08:33:17

标签: python numpy pandas

我有一个pandas数据帧INT,有两个整数列,START和END,表示间隔[START,END]。我需要检查整数POS是否属于这些间隔之一,即,如果存在START< = POS< = END的行。我需要为成千上万的POS做到这一点,我有数千个间隔。所有内容都按时间间隔和POS值进行排序。

我认为这是一个有效的解决方案,按顺序检查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)来生成一个布尔系列,也许有更好的方法来做到这一点。此外,我必须为数千个位置和间隔执行此操作,这就是我对速度感兴趣的原因。

2 个答案:

答案 0 :(得分:0)

您可以使用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

答案 1 :(得分:0)

虽然这不是纯熊猫,但速度非常快。 NCLS用于在<5秒内找到约5000万个重叠。

安装:

# 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]