Python 如何在不加载到内存的情况下对大量词典进行排序

How to sort a large list of dictionaries without loading into memory in Python

我有一个包含大约 5000 万行的 CSV 文件,我正在尝试处理数据并写入一个新的 CSV 文件。下面是代码:

import csv
import itertools

def main():
    with open("input.csv", "r") as csvfile:
        rows = csv.DictReader(csvfile)
        sorted_rows = sorted(rows, key=lambda row: row["name"])
        grouping = groupby(sorted_rows, lambda row: row["name"])

        with open("output.csv", "w") as final_csvfile:
            fieldnames = ["name", "number"]
            writer = csv.DictWriter(final_csvfile, fieldnames=fieldnames)

            for group, items in grouping:
                total = sum(int(item["number"]) for item in items)
                writer.writerow(
                    {
                        "name": group,
                        "number": str(total),
                    }
                )


if __name__ == "__main__":
    main()

这适用于不太多的行,但是当我 运行 具有 5000 万行的实际 CSV 时,它变得非常慢并且程序最终被终止。

现在行:sorted_rows = sorted(rows, key=lambda row: row["name"]) 是主要问题,因为它将 5000 万行加载到内存(列表)中,以便对其进行排序。我已经明白 sorted() 做的第一件事就是将给它的任何生成器转换成一个列表,那么我该怎么做呢?有什么指点吗?

@python_user 上述方法的问题在于它会不断追加到字典中,在你知道之前,字典会变得非常大并且可能会在内存方面造成混乱。

@Bharel 在评论里说了一些关于外部排序的东西,我研究了一下,找到了办法。

我发现 UNIX 排序命令可以对非常大的文件执行外部合并排序,所以我写了一个脚本来对非常大的 CSV 文件进行排序,然后将排序后的 CSV 文件传递​​给 python 代码在问题中。这样,不会有太大的内容写入内存。

代码如下:

sort.sh

echo "sorting CSV"
sort input.csv -o input.csv
echo "Done!"

上面的脚本是运行之后,再将排序后的CSV传入程序:

import csv
from itertools import groupby


def main():
    with open("input.csv", "r") as csvfile:
        rows = csv.DictReader(csvfile)
        grouping = groupby(rows, lambda row: row["name"])

        with open("output.csv", "w") as final_csvfile:
            fieldnames = ["name", "number"]
            writer = csv.DictWriter(final_csvfile, fieldnames=fieldnames)

            for group, items in grouping:
                total = sum(int(item["number"]) for item in items)
                writer.writerow(
                    {
                        "name": group,
                        "number": str(total),
                    }
                )


if __name__ == "__main__":
    main()

请注意,问题中使用 sorted() 的行已消失。我认为这是一个更有效的解决方案。