【问题标题】:Python Large list sorting and storagePython 大列表排序和存储
【发布时间】:2017-11-09 10:27:13
【问题描述】:

我目前正在处理一些非常大的信息列表(50 到 1 亿个条目),其中列表中的每个项目都采用 [float,(string_1,string_2)] 的形式

我以未排序的方式将元素添加到列表中,并且最终希望有一个按浮点值排序的列表。例如,我会有一个如下所示的列表:

[ [0.5,(A,B)], [-0.15,(B,C)], [0.3,(A,C)], [-0.8,(A,D)] ]

然后排序得到

[ [0.5,(A,B)], [0.3,(A,C)], [-0.15,(B,C)], [-0.8,(A,D)] ]

目前我正在使用 heapq 添加项目,然后使用 sorted(heap) 最终给我我需要的列表。我的问题是:有没有更好的方法可以将数百万个项目添加到列表中并对它们进行排序而不会导致我的计算机崩溃?持有这么长的列表然后对其进行排序会导致我的 RAM 出现一些问题。

【问题讨论】:

  • 有这么多条目,您应该真的考虑在收集数据时将其写入文件。
  • 为什么不使用 generators ?与其将数据存储到列表中然后对其进行排序,不如创建一个生成器然后对其进行一次排序。
  • @ChihebNexus:生成器仍将转换为内存中的list 以对其进行排序。
  • @ShadowRanger 是的,但是您将避免将整个列表存储在内存中然后再次对其进行排序。
  • @ChihebNexus:你不会的。如果您只是在前面设置list(没有毫无意义地使用heapq),那么调用.sort() 是完全一样的。如果您调用sorted(mygenerator)sorted(mylist) 相比,它可以节省,但如果您已经拥有mylist,调用mylist.sort() 也可以避免内存开销。

标签: python list sorting heap


【解决方案1】:

sorted() 创建了一个完全不同的列表,因此大量列表所需的 RAM 增加了一倍。请改用列表的 .sort() 方法 - 对列表进行就地排序。

除非有什么您没有告诉我们,否则请完全离开 heapq。把这些条目放在一堆没有我能想到的目的。只需使用列表的.append() 方法添加新条目,然后将.sort(reverse=True) 应用于列表末尾。

如果您的 RAM 仍然不足,那么您根本无法完全在内存中解决此问题,并且需要设计一种合并磁盘文件的方法。

与“太小”的 RAM 一起生活

在最坏的情况下,即使列表本身也无法放入可用内存中。您仍然可以创建已排序的序列,但它需要将已排序的块写入磁盘并稍后合并它们。对于合并部分,heapq 很有用。这是一个例子:

import pickle
import heapq

MAXPERFILE = 100  # the array will never get bigger than this

def getfname(i):
    return "pickled%d.dat" % i

filenum = 0
def dumptofile(a):  # dump the array to file, as pickled data
    global filenum
    fname = getfname(filenum)
    with open(fname, "wb") as f:
        pickle.dump(len(a), f)
        for x in a:
            pickle.dump(x, f)
    filenum += 1

# generate some random data
import random
a = []
for _ in range(1012):  # 10 "full" files with some leftovers
    a.append(random.random())
    if len(a) == MAXPERFILE:
        a.sort(reverse=True)
        dumptofile(a)
        del a[:]
if a:
    a.sort(reverse=True)
    dumptofile(a)

print("number of files written:", filenum)

# now merge the files together; first a function
# to generate the file contents, one at a time
def feedfile(i):
    fname = getfname(i)
    with open(fname, "rb") as f:
        count = pickle.load(f)
        for _ in range(count):
            yield pickle.load(f)

for x in heapq.merge(*(feedfile(i) for i in range(filenum)),
                     reverse=True):
    print(x)

通过减小MAXPERFILE,可以减小最大内存使用量,尽管更大的MAXPERFILE 性能会更好。事实上,如果MAXPERFILE 足够小并且数据总量足够大,则合并代码可能会因操作系统“打开的文件过多”错误而死。

【讨论】:

    猜你喜欢
    • 2012-04-10
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 2016-08-11
    • 2016-05-10
    • 1970-01-01
    • 2018-03-07
    • 1970-01-01
    相关资源
    最近更新 更多