如何在 Python 中对大文本文件流进行过滤和排序

Posted

技术标签:

【中文标题】如何在 Python 中对大文本文件流进行过滤和排序【英文标题】:How to filter and sort stream of large text file in Python 【发布时间】:2017-02-22 17:16:23 【问题描述】:

我有一个包含三个逗号分隔值的大文本文件 (>1GB),我想将它们分块读入 Pandas DataFrame。下面是 DataFrame 的一个示例:

我想在读入这个文件时过滤它并输出一个“干净”的版本。我遇到的一个问题是一些时间戳是无序的,但问题通常是非常局部的(通常一个刻度在之前或下​​方的几个插槽中是无序的)。有没有办法进行本地化的“滑动窗口”排序?

另外,由于我对 Python 还很陌生,并且正在学习 I/O 方法,因此我不确定用于过滤大型数据文件的最佳类/方法。文本IO基础?

【问题讨论】:

【参考方案1】:

这是一个非常有趣的问题,因为数据大到无法轻易放入内存。

首先,关于 I/O:如果是 CSV,我会使用标准库 csv.reader() 对象,就像这样(我假设是 Python 3):

with open('big.csv', newline='') as f:
    for row in csv.reader(f):
        ...

然后我可能会在collections.deque(maxlen=WINDOW_SIZE) 实例中保留一个滑动窗口,根据您的描述,窗口大小可能设置为 20。将第一行WINDOW_SIZE 读入双端队列,然后进入主读取循环,输出双端队列中最左边的项(行),然后追加当前行。

附加每一行后,如果当前行的时间戳在前一行的时间戳之前(window[-2]),则对双端队列进行排序。您不能直接对双端队列进行排序,但可以执行以下操作:

window = collections.deque(sorted(window), maxlen=WINDOW_SIZE)

Python 的 Timsort 算法可以有效地处理已排序的运行,因此这应该非常快(线性时间)。

如果窗口大小和乱序行数很小(听起来可能如此),我相信整体算法将是 O(N),其中 N 是数据中的行数文件,所以线性时间。

更新:我编写了一些演示代码来生成这样的文件,然后使用上述技术对其进行排序——参见this Gist,在 Python 3.5 上进行了测试。它比 sort 在相同数据上的实用程序快得多,并且在大约 N = 1,000,000 之后也比 Python 的 sorted() 函数快得多。顺便说一句,生成演示 CSV 的函数比排序代码慢得多。 :-) 我对各种 N 的结果计时 process_sliding()(绝对看起来是线性的):

N = 1,000,000:3.5 秒 N = 2,000,000:6.6 秒 N = 10,000,000:32.9 秒

作为参考,这是我的process_sliding() 版本的代码:

def process_sliding(in_filename, out_filename, window_size=20):
    with (open(in_filename, newline='') as fin,
          open(out_filename, 'w', newline='') as fout):
        reader = csv.reader(fin)
        writer = csv.writer(fout)

        first_window = sorted(next(reader) for _ in range(window_size))
        window = collections.deque(first_window, maxlen=window_size)

        for row in reader:
            writer.writerow(window.popleft())
            window.append(row)
            if row[0] < window[-2][0]:
                window = collections.deque(sorted(window), maxlen=window_size)

        for row in window:
            writer.writerow(row)

【讨论】:

我会使用 iteetools.islice 来获取大量数据 @Copperfield 你能说得更具体点吗?如果您的意思是在获取 first_window 块时,我考虑过这一点,但我认为避免导入并使用更基本的内置函数通常更清晰、更简单。 是的,我的意思是first_window。此外,您当前的方式有抛出 StopIteration 异常的风险,也许对于这个用例来说这不是问题,但为什么要留下一个容易避免的问题呢?

以上是关于如何在 Python 中对大文本文件流进行过滤和排序的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Python 对大 XML 文件执行查询?

如何从python中的文本文档中删除所有标点符号和其他符号?

对大文件进行只打印过滤条件到列表元素(优化)

对大文件进行只打印过滤条件到列表元素

在 Python 中使用 h2o4gpu K-Means 对文本文档进行聚类

如何对法律领域的文本文档进行分类