0

我有一个带有两个整数列 START 和 END 的 pandas 数据帧 INT,表示间隔 [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)它来生成一个布尔系列,也许有更好的方法可以做到这一点。此外,我必须为数千个位置和间隔执行此操作,这就是我对速度感兴趣的原因。

4

2 回答 2

0

您可以使用boolean indexing,例如this 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
于 2014-10-29T09:32:05.813 回答
0

虽然这不是纯粹的 pandas,但它非常快。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]
于 2019-11-01T11:16:04.413 回答