如何以不同的顺序有效地读取 Python 中的大文本文件行:a)每次随机行,b)从中间开始......?

How to efficiently read lines of large text file in Python in different orders: a) random line each time, b) starting in middle...?

我有一个大文本文件(500 万到 1000 万行)。每行有 10 到 5,000 个字母数字项,它们之间用 space 或逗号分隔。 每行以换行符结束。行数在 运行 时间之前已知,并且文本文件在 运行 时间期间不会更改。

每次代码为运行,将传递50-200个搜索列表,每个包含2-10个项目(词)。对于这些搜索列表中的每一个,我想 在文本文件中找到 x 行,其中 至少包含该列表中的一项。行数x范围为5-10行,设置在运行时。匹配不区分大小写,并且必须精确匹配单词边界(例如,"foo" 匹配“,foo”但不匹配“foobar”)。

每个搜索列表都有三种搜索顺序策略之一:

这是我为 "normal search" 写的 [此测试代码检查所有行到文件末尾,在 x 行之后不停止;在最终版本中,在找到 x 个搜索列表匹配项后,它将停止并转到下一个搜索列表]:

import re
textFile = "bigTextFile.txt"
searchItems = ["foo","bar","anotherfoo","anotherbar","etc"]
searchStrategy = "normal" #could be "normal", "random", "bucket"; this code is just for "normal"
results = []
with open(textFile) as inFile:
    for line in inFile:
        regex = re.compile('(' + '|'.join('\b'+item+'\b' for item in searchItems) +')', re.IGNORECASE)
        match = regex.search(line)  
        if match is not None:
            analysisResult = analyze_the_match(line)
            results.append(analysisResult)

此代码适用于 "normal search"。在我尝试过的方法中,它似乎是最快的,但我是 Python 的新手,我想一定有更好的方法 (speed/efficiency) 来做到这一点。

[更新评论以更好地解释不同搜索策略的原因]这些项目高度偏斜。研究数据,我发现大约一半的搜索将在 10,000 行之前完成,40% 可能需要几百万行才能找到足够的匹配项,而 10% 的搜索将无法在整个文本文件中找到足够的匹配项。每行中的项目与其所在的行无关,但行的范围相似(即第 100,000-500,000 行相关,1,500,000-1,750,000 行相关,等等)。对于给定的搜索列表,代码可能 运行 多次,并且对于每个 运行,优先级可能是关注不同范围的行。如果整个文本文件只有 x 行包含特定搜索列表中的项目,则结果将始终是那些 x 行。但是对于许多搜索列表,有 2x10x100,000x 行匹配,并且在不同的时间,我想选择不同的台词。在某些时候,一个特定的范围可能是优先考虑的,在其他时候随机抽样是最好的,有时从头开始找到第一个 x 行就可以了,因此 "normal"、"random" 和 "bucket" 搜索策略。

我非常感谢任何关于 "random" 和 "bucket" 的想法,因为我不确定如何最有效地处理它们。我玩弄了 linecache, itertools islice, readlines (per @Martin Thoma in this answerreadlines 出奇的快),也修改了上面的代码,但是我在 "random" 和 "bucket" 上的尝试都是笨拙的,低效的,并且我知道我还不知道什么是最好的 :)。

有什么建议吗?谢谢。

对于随机搜索和桶搜索,您可以对文件进行线性扫描并构建候选结果列表,如果列表已满并且出现更好的候选结果,则替换列表中的候选结果。

对于随机选择,您只需计算候选人出现在列表中的几率,然后使用随机数来确定候选人是否被列入列表。

对于桶选择,如果候选的桶排名优于现有项目的排名,则候选人将替换现有列表成员。

随机选择:

import random
candidates = []
n = 0
with open(textFile) as inFile:
    for line in inFile:
        if valid_candidate(line): # use regex here
            n += 1
            if n <= x:
                candidates.append(line)
            else:
                i = random.randrange(n)
                if i < x:
                    candidates[i] = line
results = []
for line in candidates:
    analysisResult = analyze_the_match(line)
    results.append(analysisResult)

桶选择:

import bisect
candidates = []
n = 0
with open(textFile) as inFile:
    for line in inFile:
        n += 1
        if valid_candidate(line): # use regex here
            rank = get_rank(n) # use n to determine the bucket and assign a rank, lower numbers are higher rank
            bisect.insort(candidates, (rank, n, line))
results = []
for rank, n, line in candidates[:x]:
    analysisResult = analyze_the_match(line)
    results.append(analysisResult)