我有一个程序,它反复循环遍历pandas数据框,如下所示:
monts = [some months]
for month in months:
df = original_df[original_df.month == month].copy()
result = some_function(df)
print(result)
但是,每次迭代所需的内存不断增加
types | # objects | total size
================================================ | =========== | ============
<class 'pandas.core.frame.DataFrame | 22 | 6.54 GB
<class 'pandas.core.series.Series | 1198 | 4.72 GB
<class 'numpy.ndarray | 1707 | 648.19 MB
<class 'pandas.core.categorical.Categorical | 238 | 368.90 MB
<class 'pandas.core.indexes.base.Index | 256 | 312.03 MB
================================================ | =========== | ============
<class 'pandas.core.frame.DataFrame | 30 | 9.04 GB
<class 'pandas.core.series.Series | 2262 | 7.29 GB
<class 'numpy.ndarray | 2958 | 834.49 MB
<class 'pandas.core.categorical.Categorical | 356 | 569.39 MB
<class 'pandas.core.indexes.base.Index | 380 | 481.21 MB
你有什么建议如何找到内存泄漏?
注意,在每次迭代时手动调用gc.collect()
都无济于事。
最小样本在这里:
import pandas as pd
from numpy.random import randn
df = pd.DataFrame(randn(10000,3),columns=list('ABC'))
for i in range(10):
print(i)
something = df.copy()
print('#########################')
print('trying to limit memory pressure')
from pympler import muppy, summary
all_objects = muppy.get_objects()
sum1 = summary.summarize(all_objects)
summary.print_(sum1)
print('#########################')
如您所见,这是记录内存消耗的增加。在10次迭代后首先从9 MB开始,它已经使用30 MB。
实际上,来自@Steven的评论可能有一点
for i in range(10):
something = df.copy()
foo_thing = summary.summarize(muppy.get_objects())
summary.print_(foo_thing)
显示问题,而
for i in range(10):
something = df.copy()
summary.print_(summary.summarize(muppy.get_objects()))
工作正常。我怎么能找到导致问题的所有这些变量?我认为这一点尤其重要,因为在我的真实代码中,其中一些是相当大的pandas.Dataframes
。
当手动添加一行foo_thing = None
时,其他脚本也正常工作。问题仍然存在 - 如何有效地找到所有这些案例。
python不应该自动识别不再使用的变量吗?
引入类似的功能时:
def do_some_stuff():
foo_thing = summary.summarize(muppy.get_objects())
summary.print_(foo_thing)
for i in range(10):
something = df.copy()
do_some_stuff()
内存泄漏似乎也已修复。
实际上,内存泄漏并不固定。好消息是summary
现在不再报告显着增加的内存消耗。不好的是:任务管理器/活动监视器告诉我 - 否则python程序在某些时候崩溃。
答案 0 :(得分:1)
问题在于范围界定。在循环中创建新对象时,应该在循环结束时可以访问它。这就是为什么(我假设),垃圾收集器没有使用copy
标记为垃圾收集创建的对象。在函数内部创建新对象时,这些对象仅限于函数作用域,并且不应在函数外部可用。这就是他们被收集的原因。
您提到分配foo_thing = None
可以解决问题。这样做是因为通过将foo_thing
指向另一个对象(None
),不再有引用数据框的变量。我使用了类似的方法,而不是foo_thing = None
,而是del foo_thing
。毕竟,Explicit is better than implicit。
答案 1 :(得分:0)
我使用了最小样本并稍微修改了它,使用来自Pympler的tracker
来查看执行一组循环后的差异,但即使在10.000循环之后,我也看不到任何内存泄漏。 / p>
使用Python 3.6.0,Numpy 1.13.1和Pandas 0.20.3进行测试。
因此,您提供的最小样本不会复制问题,或者问题是版本相关的。
import pandas as pd
from numpy.random import randn
from pympler import tracker
from tqdm import tqdm_notebook
df = pd.DataFrame(randn(10000,3),columns=list('ABC'))
tr_initial = tracker.SummaryTracker()
for i in tqdm_notebook(range(10000)):
something = df.copy()
tr_initial.print_diff()
输出:
types | # objects | total size
====================================================== | =========== | ============
<class 'dict | 78 | 28.73 KB
<class 'list | 36 | 4.59 KB
<class 'traitlets.config.loader.Config | 17 | 4.25 KB
<class 'bytes | 22 | 2.65 KB
<class 'str | 9 | 771 B
<class 'cell | 15 | 720 B
<class 'tuple | 11 | 704 B
function (<lambda>) | 4 | 544 B
<class 'method | 7 | 448 B
<class 'code | 3 | 432 B
<class 'ipykernel.comm.comm.Comm | 7 | 392 B
<class 'ipywidgets.widgets.widget.CallbackDispatcher | 3 | 168 B
<class 'ipywidgets.widgets.widget_layout.Layout | 3 | 168 B
function (store_info) | 1 | 136 B
function (null_wrapper) | 1 | 136 B
答案 2 :(得分:0)
我不会复制,而是遍历groupby。这会解决您的问题吗?
for month, df in original_df.groupby('month'):
result = some_function(df)
print(result)