while循环中累积的内存使用量

qkh*_*hly 6 python memory pandas

我的代码包含这个while循环:

while A.shape[0] > 0:
    idx = A.score.values.argmax()
    one_center = A.coordinate.iloc[idx]
    # peak_centers and peak_scores are python lists
    peak_centers.append(one_center)
    peak_scores.append(A.score.iloc[idx])
    # exclude the coordinates around the selected peak
    A = A.loc[(A.coordinate <= one_center - exclusion) | (A.coordinate >= one_center + exclusion)]
Run Code Online (Sandbox Code Playgroud)

A是一只DataFrame看起来像这样的熊猫:

   score  coordinate
0  0.158           1
1  0.167           2
2  0.175           3
3  0.183           4
4  0.190           5
Run Code Online (Sandbox Code Playgroud)

我试图找到最大分数(一个峰值)A,然后在先前找到的峰值周围排除一些坐标(在这种情况下为几百个),然后找到下一个峰值,依此类推.

A这是一只非常大的熊猫DataFrame.在运行此while循环之前,ipython会话使用了20%的机器内存.我认为运行这个while循环只会导致内存消耗下降,因为我从中排除了一些数据DataFrame.但是,我观察到的是内存使用量不断增加,并且在某些时候机器内存耗尽.

我在这里错过了什么吗?我是否需要在某处明确释放内存?

这是一个可以使用随机数据复制行为的简短脚本:

import numpy as np
import pandas as pd

A = pd.DataFrame({'score':np.random.random(132346018), 'coordinate':np.arange(1, 132346019)})
peak_centers = []
peak_scores = []
exclusion = 147
while A.shape[0] > 0:
    idx = A.score.values.argmax()
    one_center = A.coordinate.iloc[idx]
    # peak_centers and peak_scores are python lists
    peak_centers.append(one_center)
    peak_scores.append(A.score.iloc[idx])
    # exclude the coordinates around the selected peak
    A = A.loc[(A.coordinate <= one_center - exclusion) | (A.coordinate >= one_center + exclusion)]

# terminated the loop after memory consumption gets to 90% of machine memory
# but peak_centers and peak_scores are still short lists
print len(peak_centers)
# output is 16
Run Code Online (Sandbox Code Playgroud)

Mic*_*zlo 3

你的DataFrame规模太大了,无法处理。当您执行此行时,内存负载会加倍:

A = A.loc[(A.coordinate <= one_center - exclusion) | (A.coordinate >= one_center + exclusion)]
Run Code Online (Sandbox Code Playgroud)

这是因为您要为 分配一个新值,因此在过滤旧值时A会为新值分配内存。DataFrame新数据点的大小几乎与旧数据点相同,因为您选择了几乎所有数据点。这会消耗足够的内存来存储 的两个副本A,并且这还不考虑实现loc过程中用于簿记的额外内存。

显然loc会导致 pandas 为额外的数据副本分配足够的内存。我不知道这是为什么。我认为这是某种性能优化。这意味着在内存使用高峰时,您最终消耗的内存大小是DataFrame. 一旦loc完成并且未分配的内存被释放(您可以通过调用强制释放gc.collect()),内存负载就会下降到DataFrame. 在下一次调用 时loc,所有内容都会加倍,并且负载会恢复到四倍。再次收集垃圾,你的收入又回到了原来的两倍。只要您愿意,这种情况就会持续下去。

要验证发生了什么,请运行代码的修改版本:

import numpy as np
import pandas as pd
import gc

A = pd.DataFrame({'score':np.random.random(32346018), 'coordinate':np.arange(1, 32346019)})
peak_centers = []
peak_scores = []
exclusion = 147
count = 0
while A.shape[0] > 0:
    gc.collect()  # Force garbage collection.
    count += 1    # Increment the iteration count.
    print('iteration %d, shape %s' % (count, A.shape))
    raw_input()   # Wait for the user to press Enter.
    idx = A.score.values.argmax()
    one_center = A.coordinate.iloc[idx]
    # peak_centers and peak_scores are python lists
    peak_centers.append(one_center)
    peak_scores.append(A.score.iloc[idx])
    print(len(peak_centers), len(peak_scores))
    # exclude the coordinates around the selected peak
    A = A.loc[(A.coordinate <= one_center - exclusion) | (A.coordinate >= one_center + exclusion)]
Run Code Online (Sandbox Code Playgroud)

top在迭代之间按 Enter 键,并使用或类似工具密切关注内存使用情况。

在第一次迭代开始时,您将看到内存使用百分比x。在第二次迭代中,loc第一次调用后,内存使用量翻倍至2x4x随后,您将看到它在每次调用期间上升到,然后在垃圾回收之后loc下降到。2x