内存使用率很高,任何优化此脚本的方法 - python

时间:2014-09-10 00:37:49

标签: python python-3.x subprocess

我试图将每条记录与所有其他记录进行比较,同时比较我返回两条记录中不同元素的索引。

def combinations(records):
    import itertools
    return(itertools.combinations(records,2))

mytuples = tokenize("C:\\Users\\***\\Desktop\\***.data")
data = combinations(mytuples)
new = [([i] for i, t in enumerate(zip(*pair)) if t[0]!=t[1]) for pair in data]

最初我认为,将文件读取到python需要时间,因此我尝试仅使用读取部分执行我的代码,只花了几秒钟。但是当我添加这个比较部分时,它需要占用大量内存,因为它必须比较每个记录和它中的每个元素。我可以看到我的计算机内存使用量很高并达到了极限(8GB)。我的CPU使用率在50%以内,所以我想我可以假设计算不是那么激烈,因此不需要并行化过程(如果我错了就纠正我)。

有什么办法,我可以优化吗?

在评论后添加

def tokenize(filename):
    import csv
    with open(filename,'r') as f:
        f_csv = csv.reader(f, delimiter='\t')
        headers = next(f_csv)
        tuple_attr = tuple(headers)
        mytuples = tuple(tuple(x) for x in f_csv)
        return(tuple_attr,mytuples)

def combinations(records):
    import itertools
    return(itertools.combinations(records,2))


tuple_attr,mytuples = tokenize("C:\\****\\trial.data")
data = combinations(mytuples)
new = ((tuple_attr[i] for i, t in enumerate(zip(*pair)) if t[0]!=t[1]) for pair in data)
#print(new)
skt = set(frozenset(temp) for temp in new)
print(skt)
newset = set(s for s in skt if not any(p < s for p in skt))
print(newset)

这是我的数据..

Age Workclass   Fnlwgt  Education   Education-num
39   State-gov  77516    Bachelors  13
50   Self-emp-not-inc   83311    Bachelors  13
38   Private    215646   HS-grad    9

这是我的输出。

{frozenset({'Age','Workclass','Fnlwgt'}),frozenset({'Age','Workclass','Education-num','Fnlwgt','Education'})}

{frozenset({'Age','Workclass','Fnlwgt'})}

1 个答案:

答案 0 :(得分:4)

您在这里使用的唯一大量内存是您正在尝试构建的列表。

  

数据为3.5mb,有35000条记录,每条记录有15个元素。

如果mytuples是35000个15元组的列表,则combinations将迭代超过612,517,500对15元组。

“比较部分”,你将那对15元组解压缩到15个2元组的迭代器中,不会耗尽内存。这是最糟糕的几KB,而不是8GB。

但事实上你正在尝试存储几亿个元素列表的列表,这些列表的元素是整数......好吧,在64位CPython 3.4中,每个整数(最多1 <&lt;&lt; 62)是28个字节,一个列表需要每个元素8个字节加上一个64字节的标题,所以你说的是每个值100个字节,所以一旦你达到大约8000万个,那就是8GB。

您的更新版本是存储巨大的生成器列表(为什么?!);生成器至少有64个字节或更多,具体取决于它们具有多少状态,因此它将处于同一个球场。


您可以通过将它们存储在更紧凑的对象中来减少这种情况。 array.array('I')或numpy np.ndarray('I4')每个值只使用4个字节而不是36个,因此在内存不足之前可以达到20亿(比你的更多)。

当然,这只能用于存储整数数组,而不是整数列表数组,或者产生整数列表的生成器数组。如果你真的需要整数列表,你可以在numpy中使用2D数组,但不能在array.array中。如果你真的需要整数列表的生成器,那么它们都不起作用。

但我认为你可以完全消除顶级水平。你需要什么new

  

我需要将结果放在一组中。我之后使用skt = set(对新的temp的frozenset(temp))转换它们

如果您使用new做的唯一事情是迭代它一次,您可以使用迭代器而不是列表。最简单的方法是将列表推导更改为生成器表达式(即,将那些外方括号[…]更改为括号(…))。然后你将不会使用任何内存,除了当前值的内存和一些迭代器状态。

鉴于在最新版本中,你只是存储了一堆生成器,每个生成器只能迭代一次,我无法想象为什么你需要多次迭代它们的集合或访问它们按随机顺序。

但是如果由于某种原因你不能这样做,并且可以重构事物,只需编写适当的生成器表达式并将其传递给array.array构造函数或np.fromiter函数。 (如果你愿意,array.array可以像列表一样附加,所以你可以写一个明确的for语句,但我认为你不需要。)


所以,也许这就是你想要的:

new = (([i] for i, t in enumerate(zip(*pair)) if t[0]!=t[1]) for pair in data)

或许其中一个:

new = (i for pair in data for i, t in enumerate(zip(*pair)) if t[0]!=t[1])

new = (i for pair in data for i, t in enumerate(zip(*pair)) if t[0]!=t[1])
new = array.array('I', new)

new = (i for pair in data for i, t in enumerate(zip(*pair)) if t[0]!=t[1])
new = np.fromiter(new, np.int32)

也许最后一个被重新塑造成2D Nx1阵列而不是一维阵列。