Python 3:如何以随机顺序迭代遍历大文件中的所有行(超过100万行)?

4

好的,我有多个文本文件,每个文件包含超过500,000或甚至1,000,000行。

目前我做的事情类似于:

import random

def line_function(line):
    # Do something with given line

def random_itteration(filepath):
    with open(filepath) as f:
        lines = f.readlines()
        random.shuffle(lines)
        for line in lines:
            result = line_function(line)

事实上,random.shuffle()Python文档明确指出(由我强调):

请注意,即使对于小的 len(x),x 的排列总数也可以迅速增长到大多数随机数生成器的周期之上。这意味着大多数长序列的排列都无法生成。例如,长度为 2080 的序列是适合于 Mersenne Twister 随机数生成器周期内的最大序列。

所以问题是:

如何以最快、最有效的方式使我的设置按预期工作?

更多信息:

我想将 line_function() 应用于随机行,而不仅仅是按它们的顺序迭代。还请注意,我非常希望每行只处理一次

最后,提前洗牌文本文件或将其分成较小的文件不是一个选项,也不是我要问的问题。


欢迎提供任何见解!先感谢大家。

1
你只需要一个随机排列,而不是所有的排列。如果你只需要一个“随机”的排列,那么引用部分就不是问题了。如果你需要一个真正随机的排列,那就更困难了。 - LangeHaare
1
这个注释适用于从如此大的输入集中随机选择事物的任何方式。 - Barmar
1
文件是静态的还是经常变化?如果是静态的,您可以创建另一个文件来保存文件中每行开头位置的位置。读取该文件,对其进行洗牌,然后您就可以在主文件中寻找这些索引。 - Barmar
3
请忽略 "This implies that most permutations of a long sequence can never be generated." 这句话。对于实际的代码来说,它基本上没有任何实际意义。 - Mark Dickinson
5
我不明白你的问题,为什么把文件加载到一个列表中,然后在列表上使用“random.shuffle”不能满足你的需求?除了你正在执行“shuffled_lines = random.shuffle(lines)”这一行代码会返回“None”之外... - juanpa.arrivillaga
显示剩余7条评论
3个回答

5
正如Mark Dickinson所说,你引用的文档行在实际代码中基本上没有任何实际意义。它对你的代码绝对没有任何相关性。
重要的是洗牌是否与所有可能排列的真正均匀随机分布不可区分,而不是实际上是否产生这样的分布。在某种可区分标准下,random.shuffle与完全随机的洗牌在统计上是不可区分的,这取决于底层Mersenne Twister算法的质量,并且它们可区分的方式与周期无关。
你不需要采取任何特殊措施使你的设置“按预期工作”。random.shuffle已经可以正常工作。

好的,我必须承认,事后看来我的问题有点不成问题。当我不得不洗牌大文件时,我感觉我的程序稍微变慢了一点。我查阅了Python文档,发现了引用的文档行,误解了它,并认为我没有采取正确的方法。你的答案确实澄清了事情,所以谢谢! - Montmons

-1

在Python中,如果你必须这样做,你会发现很难做到“快速高效”,但是开始的地方应该是洗牌算法,比如Fisher-Yates算法。

一旦你实现了它,加载文件,并记录每行从哪个字节偏移量开始。对数组进行洗牌,打开文件,然后迭代数组,从偏移量读取到下一个换行符。

对于像你提出的这么大的数据集,预计lines = f.readlines()将会产生太多的内存压力,需要使用更复杂但更可扩展的解决方案,使用偏移量。

为了更有效率的重新运行,也可以考虑在生成偏移元数据后保存它,这样你就不需要每次都遍历整个文件(或整个文件)。


2
random.shuffle 已经使用了 Fisher-Yates 算法,请参见 https://dev59.com/Dmox5IYBdhLWcg3wLhii - Barmar
哦,原来是这样。谢谢。 - Adam Barnes

-1

我宁愿对整数列表进行洗牌,也不愿处理庞大的行。
(整数是行在行列表中的索引/位置)
就像这样:

import random
from random import randint

def line_function(line):
    # Do something with given line

def random_itteration(filepath):
    with open(filepath) as f:
        lines = f.readlines()
        count = len(lines)
        #random_index_list = random.shuffle(list(xrange(count)))
        random_index_list = random.sample(range(count+1),count)
        for index in random_index_list:
            result = line_function(lines[index])

        #shuffled_lines = random.shuffle(lines)
        #for line in shuffled_lines:
        #    result = line_function(line)

1
他想要以随机顺序处理所有行,而不仅仅是一行随机。 - Barmar
2
@Montmons 这个看起来很有前途,有什么优势吗?更不用说这是不正确的,因为 random_index_list = random.shuffle(list(xrange(count))) 会返回 None - juanpa.arrivillaga
1
@StevenRumbalski 正如我在下面问题的评论中提到的那样,由于这些文件是静态的,你可以创建另一个包含行索引的文件。这是一次性的成本,不需要每次洗牌时都发生。 - Barmar
好的,我测试了一下,确实比较慢。所以我会尝试@Barmar的解决方案,创建额外的文件来保存行索引。稍后我会报告我的发现。我没想到这个问题会引起这么多关注。本来希望可能有一个简单的解决方案(例如来自pip包的另一个洗牌选项之类的),我不知道。但我很高兴收到了一些有见地的答案。 - Montmons
3
朋友,关于random.shuffle你有什么不满意的地方吗?是速度太慢吗? - juanpa.arrivillaga
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接