根据python列表中的索引删除列时避免逐个错误

时间:2014-07-28 05:05:21

标签: python list indexing slice

我有一个名为TARGFILE的目标文件:

10001000020002002001100100200000111
10201001020000120210101100110010011
02010010200000011100012021001012021
00102000012001202100101202100111010

我的想法是将其保留为字符串,并在python中使用切片来删除索引。

删除将基于名为INDICES的整数列表进行,如下所示:

[1, 115654, 115655, 115656, 2, 4, 134765, 134766, 18, 20, 21, 23, 24, 17659, 92573, 30, 32, 88932, 33, 35, 37, 110463, 38, 18282, 46, 18458, 48, 51, 54]

我想删除TARGFILE中与INDICES匹配的每个行的每个位置。例如,INDICES中的第一个数字是1,因此将删除包含1,1,0,0的TARGFILE的第一列。但是,如果没有同时删除所有内容,我会因为一次性错误和更改索引位置而错误地执行此操作。

因此,同时从每行中删除每一列的解决方案可能比使用嵌套循环更快更安全,但我不确定如何对此进行编码。

到目前为止我的代码在这里:

#!/usr/bin/env python
import fileinput
SRC_FILES=open('YCP.txt', 'r')

for line in SRC_FILES:
    EUR_YRI_ADM=line.strip('\n')
    EUR,YRI,ADM=EUR_YRI_ADM.split(' ')
    ADMFO=open(ADM, 'r')
    lines=ADMFO.readlines()
    INDICES=[int(val) for val in lines[0].split()]
    TARGFILE=open(EUR, 'r')

在我看来,使用enumerate的解决方案可能是可行的,但我还没有找到它,而且首先可能不是最理想的......

编辑:回应对内存的担忧:最长的行是~180,000项,但我应该能够毫无问题地将其存入内存,我可以访问集群。

3 个答案:

答案 0 :(得分:2)

我喜欢彼得答案的简单性,即使它目前是一个接一个的。我的想法是你可以通过对INDICES进行排序,从后面到前面完成这个过程来消除索引转移问题。这导致了remove_indices1,这实在是效率低下。我认为2更好,但最简单的是3,这是彼得的答案。

我可能会为一些大数字做一些计时,但我的直觉说如果INDICES非常稀疏,我的remove_indices2将比Peter的remove_indices3快。 (因为您不必遍历每个字符,而只是遍历要删除的索引。)

顺便说一下 - 如果你可以对INDICES进行一次排序,那么你就不需要让本地副本进行排序/反向,但我不知道你是否可以这样做。

rows = [
    '0000000001111111111222222222233333333334444444444555555555566666666667',
    '1234567890123456789012345678901234567890123456789012345678901234567890',
    ]

def remove_nth_character(row,n):
    return row[:n-1] + row[n:]

def remove_indices1(row,indices):
    local_indices = indices[:]
    retval = row
    local_indices.sort()
    local_indices.reverse()
    for i in local_indices:
        retval = remove_nth_character(retval,i)
    return retval

def remove_indices2(row,indices):
    local_indices = indices[:]
    local_indices.sort()
    local_indices.reverse()
    front = row
    chunks = []
    for i in local_indices:
        chunks.insert(0,front[i:])
        front = front[:i-1]
    chunks.insert(0,front)
    return "".join(chunks)

def remove_indices3(row,indices):
    return ''.join(c for i,c in enumerate(row) if i+1 not in indices)

indices = [1,11,4,54,33,20,7]

for row in rows:
    print remove_indices1(row,indices)
print ""
for row in rows:
    print remove_indices2(row,indices)
print ""
for row in rows: 
    print remove_indices3(row,indices)

编辑:添加时间信息,再加上新的赢家!

正如我所怀疑的,当没有要删除的索引时,我的算法(remove_indices2)会获胜。事实证明,基于枚举的更糟糕,因为有更多的索引需要删除。这里是时间码(bigrows行有210000个字符):

bigrows = []
for row in rows:
    bigrows.append(row * 30000)

for indices_len in [10,100,1000,10000,100000]:
    print "indices len: %s" % indices_len
    indices = range(indices_len)
    #for func in [remove_indices1,remove_indices2,remove_indices3,remove_indices4]:
    for func in [remove_indices2,remove_indices4]:
        start = time.time()
        for row in bigrows:
            func(row,indices)
        print "%s: %s" % (func.__name__,(time.time() - start))

以下是结果:

indices len: 10
remove_indices1: 0.0187089443207
remove_indices2: 0.00184297561646
remove_indices3: 1.40601491928
remove_indices4: 0.692481040955
indices len: 100
remove_indices1: 0.0974130630493
remove_indices2: 0.00125503540039
remove_indices3: 7.92742991447
remove_indices4: 0.679095029831
indices len: 1000
remove_indices1: 0.841033935547
remove_indices2: 0.00370812416077
remove_indices3: 73.0718669891
remove_indices4: 0.680690050125

那么,为什么3会这么糟糕呢?好吧,事实证明in运算符在列表上并不高效。它必须遍历所有列表项以进行检查。 remove_indices4只是3但是首先将索引转换为集合,因此内部循环可以执行快速散列查找,而不是遍历列表:

def remove_indices4(row,indices):
    indices_set = set(indices)
    return ''.join(c for i,c in enumerate(row) if i+1 not in indices_set)

而且,正如我原先预计的那样,这比我的高密度算法更好:

indices len: 10
remove_indices2: 0.00230097770691
remove_indices4: 0.686790943146
indices len: 100
remove_indices2: 0.00113391876221
remove_indices4: 0.665997982025
indices len: 1000
remove_indices2: 0.00296902656555
remove_indices4: 0.700706005096
indices len: 10000
remove_indices2: 0.074893951416
remove_indices4: 0.679219007492
indices len: 100000
remove_indices2: 6.65899395943
remove_indices4: 0.701599836349

如果您要删除的索引少于10000个,则2个最快(如果您在函数外部执行索引排序/反转,则更快)。但是,如果你想要一些非常稳定的东西,无论有多少指数,都要使用4。

答案 1 :(得分:1)

我能看到的最简单的方法就是:

>>> for line in TARGFILE:
...     print ''.join(c for i,c in enumerate(line) if (i+1) not in INDICES)
...
100000200020020100200001
100010200001202010110001
010102000000111021001021
000000120012021012100110

(将print替换为输出文件等)

这依赖于能够将每一行加载到内存中,根据您的数据,这可能是合理的,也可能是不合理的。

编辑:解释:

第一行很简单:

>>> for line in TARGFILE:

只需遍历TARGFILE中的每一行。第二行有点复杂:

  • ''.join(...)将一个字符串列表与一个空连接符('')连接在一起。 join通常使用逗号,例如','.join(['a', 'b', 'c']) == 'a,b,c',但在这里我们只想将每个项目加入到下一个项目中。

  • enumerate(...)采用可互换的方式,并为迭代中的每个项目返回(index, item)对。例如enumerate('abc') == (0, 'a'), (1, 'b'), (2, 'c')

所以该线说,

  

将指数未在INDICES

中找到的行的每个字符连接在一起

然而,正如John指出的那样,Python索引是零基础的,所以我们从枚举值中加1。

答案 2 :(得分:0)

我最终使用的脚本如下:

#!/usr/bin/env python

def remove_indices(row,indices):
    indices_set = set(indices)
    return ''.join(c for i,c in enumerate(row) if (i+1) in indices_set)

SRC_FILES=open('YCP2.txt', 'r')

CEUDIR='/USER/ScriptsAndLists/LAMP/LAMPLDv1.1/IN/aps/4bogdan/omni/CEU/PARSED/'
YRIDIR='/USER/ScriptsAndLists/LAMP/LAMPLDv1.1/IN/aps/4bogdan/omni/YRI/PARSED/'
i=0
for line in SRC_FILES:
        i+=1
    EUR_YRI_ADM=line.strip('\n')
        EUR,YRI,ADM=EUR_YRI_ADM.split('\t')
        ADMFO=open(ADM, 'r')


        lines=ADMFO.readlines()
        INDICES=[int(val) for val in lines[0].split()]
        INDEXSORT=sorted(INDICES, key=int)

        EURF=open(EUR, 'r')
        EURFOUT=open(CEUDIR + 'chr' + str(i) + 'anc.hap.txt' , 'a')
        for haplotype in EURF:
                TRIMLINE=remove_indices(haplotype, INDEXSORT)
                EURFOUT.write(TRIMLINE + '\n')
        EURFOUT.close()

        AFRF=open(YRI, 'r')
        AFRFOUT=open(YRIDIR + 'chr' + str(i) + 'anc.hap.txt' , 'a')
        for haplotype2 in AFRF:
                TRIMLINE=remove_indices(haplotype2, INDEXSORT)
                AFRFOUT.write(TRIMLINE + '\n')
        AFRFOUT.close()