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



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

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

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

  • 正常。从第一行开始,在中逐行搜索连续顺序,直到找到x行数或到达最后一行线

  • 随机。从文本文件中随机拾取行。继续,直到它找到x行数或完成对每行的搜索。

  • 铲斗范围。首先搜索行的最高优先级范围,然后搜索行的次高优先级范围,再搜索下一行,等等。例如,搜索范围的优先级可能是首先搜索1000000到1499999行,然后是0到999999行,再是1500000到2199999行等。可以有3到20个桶,每个桶覆盖100000到5000000行的范围。存储桶的数量和行号范围在运行时给出。在每个范围内,连续搜索。搜索,直到找到x行数或到达最后一个bucket的末尾。

以下是我为"正常搜索"所写的内容[此测试代码在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)

此代码用于"正常搜索"。在我尝试过的方法中,它似乎是最快的,但我是Python的新手,我想一定有更好的方法(速度/效率)来做到这一点。

[根据评论进行更新,以更好地解释不同搜索策略的原因]这些项目高度倾斜。根据这些数据,我发现大约一半的搜索将在10000行之前完成,40%的搜索可能需要几百万行才能找到足够的匹配,10%的搜索在整个文本文件中找不到足够的匹配。每行中的项目与其所在行没有关系,但行的范围相似(即100000-50000行相关,150000-1750000行相关,等等)。对于给定的搜索列表,代码可以运行多次,每次运行的优先级可能是关注不同范围的行。如果整个文本文件只有x行包含特定搜索列表中的项目,则结果将始终是x这些行。但对于许多搜索列表,有2x10x10000x行就可以了,因此采用了"正常"、"随机"one_answers"桶"搜索策略。

我真的很感激任何关于"随机"one_answers"桶"的想法,因为我不知道如何最有效地处理它们。我玩了linecacheitertools islicereadlines(根据@Martin Thoma的回答,readlines的速度快得惊人),以及修改上面的代码,但我在"随机"one_answers"桶"上的尝试都很笨拙,效率很低,我知道我不知道什么是最好的:)。

有什么建议吗?谢谢

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

对于随机选择,只需计算候选人在名单中的几率,并使用随机数来确定候选人是否被列入名单。

对于bucket选择,如果某个候选者的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)

最新更新