【问题标题】:Avoiding off-by-one errors when removing columns based on indices in a python list根据 python 列表中的索引删除列时避免一个错误
【发布时间】:2014-09-19 06:07:34
【问题描述】:

我有一个名为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 个项目,但我应该能够毫无问题地将其放入内存,我可以访问集群。

【问题讨论】:

  • 您是否希望TARGFILE 中的列数超过十万?你想在每个循环期间打开并读取文件ADM 还是在循环之前读取一次?

标签: python list indexing slice


【解决方案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(...) 接受一个 interable 并为 iterable 中的每个项目返回一对 (index, item)。例如enumerate('abc') == (0, 'a'), (1, 'b'), (2, 'c')

所以这行说,

将索引中未找到索引的行的每个字符连接在一起

但是,正如 John 指出的那样,Python 索引是零基数,因此我们将 enumerate 的值加 1。

【讨论】:

  • 嘿。他担心错了一个,而你的解决方案错了一个。需要是“如果 i+1 不在 INDICES 中”。不过,否则,我喜欢这种简单性。
  • 嗨约翰和@PeterGibson - 我现在正在实施这个,届时我会接受一个答案 - 我也喜欢这个,虽然我不完全确定它是如何实际运作的。
  • @VincentLaufer - 我知道你已经接受了这个答案,但请做出我在我对你的代码的回答中提到的set() 修改。它会显着提高您的表现。
【解决方案2】:

我喜欢 Peter 的简单回答,尽管它目前是不一样的。我的想法是,您可以通过对 INDICES 进行排序并从后到前执行该过程来摆脱索引移位问题。这导致了 remove_indices1,这确实是低效的。我认为 2 更好,但最简单的是 3,这是 Peter 的答案。

我可能会为一些大数字做一些计时,但我的直觉是,如果 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。

【讨论】:

  • 这第 4 种解决方案是可重复使用的(我实际上有 2 个 TARGFILES,而不是一个)、准确、可逆(可以保留或删除单词 NOT)并且速度极快。非常感谢你的努力。我会尝试从这个答案中尽可能多地学习。
  • 很好的答案 John - 我喜欢基准测试和使用 set 来加快速度。
【解决方案3】:

我最终使用的脚本如下:

#!/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()

【讨论】:

    猜你喜欢
    • 2021-06-03
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多