Python 从一组段范围构建遮罩

Python 从一组段范围构建遮罩,python,performance,numpy,bitmask,Python,Performance,Numpy,Bitmask,我正在处理由长序列组成的数据(整个人类基因组,因此总序列长度约为3e9)。我有22个文件,每个文件包含5e7到2.5e8个字符之间的子序列 就我的问题而言,这些字符要么是0要么是1,因此文件如下所示: 0101111010110011001001000000110011110001111000010100001010110000010 给定0或1的索引是其“位置”(基于0) 我有一组不重叠的范围,表示与序列文件中的位置相对应的位置,例如 [(1700,2000),(9000,15000),(16

我正在处理由长序列组成的数据(整个人类基因组,因此总序列长度约为3e9)。我有22个文件,每个文件包含5e7到2.5e8个字符之间的子序列

就我的问题而言,这些字符要么是
0
要么是
1
,因此文件如下所示:

0101111010110011001001000000110011110001111000010100001010110000010

给定
0
1
的索引是其“位置”(基于0)

我有一组不重叠的范围,表示与序列文件中的位置相对应的位置,例如

[(1700,2000),(9000,15000),(16000,18000)]

对于范围列表中的每个位置,我希望将序列中的任何
1
转换为
0
(转换不包括范围上限,如python切片,请参见示例)

例如:

sequence = 1111011101
ranges   = [(0, 3), (7,10)]
result   = 0001011000  
# the first 3 and last 3 positions are converted to 0 if they are not 
# 0 already, otherwise they are left alone

我正在寻找一种有效的方法来更新给定一组范围的序列,可能是多次。我可能会一次又一次地这样做,所以我担心速度。内存不是问题,因此从与序列长度相同的范围创建掩码就可以了,只要创建掩码的速度很快

序列是如何表示的?当然不是这里的整数(前导零呢?)。是一串数字字符吗?还是一个整数数组

假设它是一个整数数组,那么简单的for循环没有问题

In [50]: sequence = np.array([1, 1, 1, 1, 0, 1, 1, 1, 0, 1])

In [51]: ranges   = [(0, 3), (7,10)]

In [52]: for r in ranges:
   ....:     sequence[r[0]:r[1]] = 0
   ....:     

In [53]: sequence
Out[53]: array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0])
仅将整个切片设置为零的矢量化和广播操作几乎总是比检查条目是否首先为零的任何操作都要快

如果范围的数量非常大,Python循环可能会很慢,在这种情况下,您可以将其简单地移动到Cython,或者考虑用Cython类型的内存视图同时访问共享内存阵列,特别是如果您可以保证范围永远不会重叠的话。

如果从Python字符串开始,可以考虑预先计算一个数组格式,例如使用<代码> NUMPY.CHAR。默认情况下,这些数组是不可变的,就像Python字符串一样,但是您可以将

write
标志设置为
True
,以便对它们进行变异。如果由于数据序列的大小而导致空间问题,则可以进一步将数据预计算为自定义1位整数类型的数组,但不要进行此优化,除非某些基准测试表明确实需要这样做

假设您可以在NumPy中转换为标准的length-1字符串类型,这也适用于:

In [69]: s2 = np.char.array("1111011101", itemsize=1)    

In [70]: s2.setflags(write=True)

In [71]: for r in ranges:
    s2[r[0]:r[1]] = '0'
   ....:     

In [72]: s2
Out[72]: 
chararray(['0', '0', '0', '1', '0', '1', '1', '0', '0', '0'], 
      dtype='|S1')

In [73]: s2.tostring()
Out[73]: '0001011000'

使用numpy字节数组将整个内容加载到内存中,使用
numpy.zero
创建字节掩码,并在pad的边缘添加
one
,然后在numpy中添加它。这有什么问题吗?这似乎是一个很好的算法在图形卡上-考虑看PyCUDA。这可能会很好地工作,或者您可能会因为数据传输而减慢速度。作为一种可能的解决方案,您可以将数据压缩8倍,方法是实际将每组8个
1
s和
0
s存储在一个字节中。这几乎肯定会大大加快您的算法速度。一个有趣的现象是,只要您的范围在任何时候都转换为零或全部转换为一,如果范围重叠,则完全可以—数据只需写入两次。@lolopop对此没有问题,我有类似的想法。我只是想知道是否有某种神秘的高功率方式来处理绕过for循环的范围;)@IanPudney是的,没错,这很方便,因为有时范围实际上会重叠,尽管通常我会通过合并或根据某些条件选择来预处理它们。实际上我刚刚检查了,范围集包含389222对值,这并不太糟糕。在不同的情况下,其上限可能为1米。序列表示有点随意。实际上,序列由DNA碱基组成,即
ACGT
,但我使用1和0对掩码文件进行不同的操作,因为我屏蔽的条件总是真或假。我添加了一点,以防最终以位字符串格式处理此数据。