如何使用pandas

时间:2017-07-24 18:03:35

标签: python pandas numpy memory-leaks

我有一个程序,它反复循环遍历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()都无济于事。

编辑2

最小样本在这里:

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。

编辑3

实际上,来自@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

编辑4

当手动添加一行foo_thing = None时,其他脚本也正常工作。问题仍然存在 - 如何有效地找到所有这些案例。 python不应该自动识别不再使用的变量吗?

编辑5

引入类似的功能时:

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()

内存泄漏似乎也已修复。

编辑6

实际上,内存泄漏并不固定。好消息是summary现在不再报告显着增加的内存消耗。不好的是:任务管理器/活动监视器告诉我 - 否则python程序在某些时候崩溃。

3 个答案:

答案 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)