【问题标题】:Iterate and match in large files in python在python中的大文件中迭代和匹配
【发布时间】:2015-03-03 08:59:18
【问题描述】:

我有两个大文本文件:

“字母”超过 600 000 行

“数字”超过 100 000 行

字母看起来像这样:

AAAA
AAAB
AAAC
etc…

数字有两列,一列是“id”,一列是数字列表:

id1 5、201、66、33 id2 356 id3 5103, 2, 452 ETC…

我希望“字母”中的每一行都代表一个数字:

1个AAAA 2 AAAB ETC…

然后检查“数字”中的哪一行包含该数字,然后将该 id 与字母组合配对,仅在这种情况下:

AAAB    id3

这个脚本需要几天才能运行:

combine = {}
for i, x in enumerate(letters):
    for id, number in numbers.items():
        if i+1 in number:
            combine[x['letter']] = id

有更快的方法吗?

【问题讨论】:

    标签: python performance algorithm python-3.x


    【解决方案1】:

    如果您使用线性时间算法而不是二次算法,则 1e5 和 6e5 不是大数:

    #!/usr/bin/env python
    with open('letters') as file:
        letters = file.read().splitlines()
    
    def combine_ids(letters):
        with open('numbers') as file:
            for line in file:
                id, space, numbers_str = line.lstrip().partition(' ')
                try:
                    numbers = list(map(int, numbers_str.split(',')))
                except ValueError:
                    continue # skip invalid lines
                for n in numbers:
                    try:
                        yield letters[n], id
                    except IndexError:
                        pass
    
    result = dict(combine_ids(letters))
    print(result)
    

    如果多个id可能对应同一个字母(如果numbers文件中有重复的数字),那么最新的数字获胜。

    示例

    数字:

    id1 5, 33
    id2 23
    id3 103, 2, 3
    

    字母:

    AAAA
    AAAB
    AAAC
    AAAD
    AAAE
    AAAF
    AAAG
    AAAH
    AAAI
    AAAJ
    AAAK
    ...
    AAAX
    AAAY
    AAAZ
    

    输出

    {'AAAX': 'id2', 'AAAC': 'id3', 'AAAD': 'id3', 'AAAF': 'id1'}
    

    注意:这里的数字2 对应于AAAC(从零开始索引),如果字母应该从1 索引,则使用letters[n-1](假设n>=1)。

    【讨论】:

    • 看起来完全正确!但似乎数字文件中的 'line' 并没有换行,而是写了 '\r'。关于我如何解决这个问题的任何想法?:b'ID1 41820,91535,153900,191776,277339\rID2 8,181055,235156,242726,293493,305121\rID3 11,4,29\rID4 16,17\rID5 19 ,20,2,3,24'
    • @user3575737:如果您的文件使用 '\r' 作为换行符(旧 Mac),则以文本模式打开文件以启用通用换行符模式。在这种情况下,将代码更改为使用普通的 str 文字。我已经更新了答案。
    • (l for l in file) 不会比file.read().splitlines() 更好
    • @Evpok:没有。 letters 在这里应该是一个序列,但 () 会创建一个生成器表达式。
    • 哦,确实,我忘了你需要一个序列。
    【解决方案2】:

    将所有字母存储在列表中letters = ["AAAA", "AAAB", "AAAC", ...]

    现在在读入 numbers 文件后,创建一个类似

    的映射
    0 mapped to id1
    1 mapped to id2
    ....
    
    m[0] = "id1", m[1] = "id2"...
    

    在执行上述步骤时,创建一个零数组,读入 numbers 文件并分配它所属的映射行

    p = [0] * len(letters)
    nums = row[row.find(" ") + 1:].split(", ")
    row_name = row[:row.find(" ") - 1]
    for num in nums:
        p[num] = m[row_name]
    

    现在在letters 列表中查找第 i 个字母及其编号即可

    print p[i]
    

    【讨论】:

      【解决方案3】:

      你可以对每个文件进行一次遍历,O(N):

      1. 将字母文件读入数组。您将得到数组索引 (+ 1?) = 行号。

      2. 读取数字文件。对于每一行:使用数字将id 与数组中的字母组合起来。

      【讨论】:

      • 感谢您的意见。由于字母文件包含的行数远多于数字,因此这是我必须遍历的列表以涵盖每个项目..
      • 什么意思?如果缺少数字,您也无法使用您的方法找到它(因为它不存在)。如果存在,它将用于将字母与 id 结合起来。
      • 现在的工作方式是,我遍历“字母”中的每一行,并在“数字”列表中的数字中查找每个行号(每个 ID 可以是数百个)。如果我找到匹配项,例如,如果 AAAA 在第 1 行,并且在 id3 所在的行上找到第一个,则 AAAA = id3。相反,如果我对数字文件中的每一行进行循环,这将只覆盖 1/6 的字母,因为我有 100k 个数字行和 600k 个字母行。
      • 每一行有几个数字。如果每行平均有 6 个数字,则您有 600k 个数字。
      猜你喜欢
      • 2011-12-16
      • 2020-10-16
      • 1970-01-01
      • 2021-09-15
      • 2018-04-06
      • 2015-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多