• 如何释放dataframe使用的内存


    如何释放熊猫数据框使用的内存?

    我在熊猫中打开了一个非常大的csv文件,如下所示。

    1. import pandas
    2. df = pandas.read_csv('large_txt_file.txt')

    完成此操作后,内存使用量将增加2GB,这是预期的,因为此文件包含数百万行。我的问题出在我需要释放此内存的时候。我跑了...

    del df

    但是,我的内存使用没有下降。这是释放熊猫数据帧使用的内存的错误方法吗?如果是,正确的方法是什么?

    python  pandas  memory 

    — b10危险
     source


    3

    gcgc.collect()

    — 没错


    del df创建df后不直接调用对吗?我认为在删除df时就有对df的引用。因此,它不会被删除,而是会删除名称。

    — Marlon Abeykoon,2016年


    4

    是否将垃圾回收器回收的内存实际上退还给OS取决于实现。垃圾收集器唯一的保证就是回收的内存可以由当前的Python进程用于其他用途,而不是从操作系统中请求甚至更多的内存。

    — chepner '16


    创建后,我立即致电del df。我没有向df添加任何其他引用。我所做的就是打开ipython并运行这三行代码。如果我在占用大量内存的其他对象上运行相同的代码,例如numpy数组。del nparray运作完美

    — b10hazard '16


    @ b10hazard:df = ''代码末尾的代码怎么样?似乎要清除数据帧使用的RAM。

    — jibounet

    Answers:


    119

    减少Python中的内存使用量非常困难,因为Python实际上并未将内存释放回操作系统。如果删除对象,则内存可用于新的Python对象,但不能free()返回系统(请参阅此问题)。

    如果坚持使用数字numpy数组,则将释放它们,但装箱的对象不会释放。

    1. >>> import os, psutil, numpy as np
    2. >>> def usage():
    3. ... process = psutil.Process(os.getpid())
    4. ... return process.get_memory_info()[0] / float(2 ** 20)
    5. ...
    6. >>> usage() # initial memory usage
    7. 27.5
    8. >>> arr = np.arange(10 ** 8) # create a large array without boxing
    9. >>> usage()
    10. 790.46875
    11. >>> del arr
    12. >>> usage()
    13. 27.52734375 # numpy just free()'d the array
    14. >>> arr = np.arange(10 ** 8, dtype='O') # create lots of objects
    15. >>> usage()
    16. 3135.109375
    17. >>> del arr
    18. >>> usage()
    19. 2372.16796875 # numpy frees the array, but python keeps the heap big

    减少数据框的数量

    Python使内存保持高水位,但是我们可以减少创建的数据帧的总数。修改数据框时,请选择inplace=True,这样就不会创建副本。

    另一个常见的陷阱是在ipython中保留以前创建的数据帧的副本:

    1. In [1]: import pandas as pd
    2. In [2]: df = pd.DataFrame({'foo': [1,2,3,4]})
    3. In [3]: df + 1
    4. Out[3]:
    5. foo
    6. 0 2
    7. 1 3
    8. 2 4
    9. 3 5
    10. In [4]: df + 2
    11. Out[4]:
    12. foo
    13. 0 3
    14. 1 4
    15. 2 5
    16. 3 6
    17. In [5]: Out # Still has all our temporary DataFrame objects!
    18. Out[5]:
    19. {3: foo
    20. 0 2
    21. 1 3
    22. 2 4
    23. 3 5, 4: foo
    24. 0 3
    25. 1 4
    26. 2 5
    27. 3 6}

    您可以通过键入%reset Out清除历史记录来解决此问题。另外,您可以调整ipython保留的历史记录数量ipython --cache-size=5(默认为1000)。

    减少数据框大小

    尽可能避免使用对象dtype。

    1. >>> df.dtypes
    2. foo float64 # 8 bytes per value
    3. bar int64 # 8 bytes per value
    4. baz object # at least 48 bytes per value, often more

    带有对象dtype的值被装箱,这意味着numpy数组仅包含一个指针,并且堆中对于数据框中的每个值都有一个完整的Python对象。这包括字符串。

    尽管numpy支持数组中固定大小的字符串,但pandas不支持(这会引起用户混乱)。这可以产生很大的变化:

    1. >>> import numpy as np
    2. >>> arr = np.array(['foo', 'bar', 'baz'])
    3. >>> arr.dtype
    4. dtype('S3')
    5. >>> arr.nbytes
    6. 9
    7. >>> import sys; import pandas as pd
    8. >>> s = pd.Series(['foo', 'bar', 'baz'])
    9. dtype('O')
    10. >>> sum(sys.getsizeof(x) for x in s)
    11. 120

    您可能要避免使用字符串列,或者找到一种将字符串数据表示为数字的方法。

    如果您的数据框包含许多重复值(NaN非常常见),则可以使用稀疏数据结构来减少内存使用量:

    1. >>> df1.info()
    2. <class 'pandas.core.frame.DataFrame'>
    3. Int64Index: 39681584 entries, 0 to 39681583
    4. Data columns (total 1 columns):
    5. foo float64
    6. dtypes: float64(1)
    7. memory usage: 605.5 MB
    8. >>> df1.shape
    9. (39681584, 1)
    10. >>> df1.foo.isnull().sum() * 100. / len(df1)
    11. 20.628483479893344 # so 20% of values are NaN
    12. >>> df1.to_sparse().info()
    13. <class 'pandas.sparse.frame.SparseDataFrame'>
    14. Int64Index: 39681584 entries, 0 to 39681583
    15. Data columns (total 1 columns):
    16. foo float64
    17. dtypes: float64(1)
    18. memory usage: 543.0 MB

    查看内存使用情况

    您可以查看内存使用情况(docs):

    1. >>> df.info()
    2. <class 'pandas.core.frame.DataFrame'>
    3. Int64Index: 39681584 entries, 0 to 39681583
    4. Data columns (total 14 columns):
    5. ...
    6. dtypes: datetime64[ns](1), float64(8), int64(1), object(4)
    7. memory usage: 4.4+ GB

    从熊猫0.17.1开始,您还df.info(memory_usage='deep')可以查看包括对象在内的内存使用情况。

    — 威尔弗雷德·休斯
     source


    2

    这必须标记为“已接受答案”。它简要但清晰地说明了python即使真正不需要时也如何保持在内存中。保存内存的技巧都是明智且有用的。正如我只想补充用“多处理”还有一个小窍门(如@阿米的答案解释。

    — pedram bashiri


    46

    如评论中所述,有一些尝试的方法:gc.collect(@EdChum)可能清除东西。至少从我的经验来看,这些东西有时会起作用,而通常却不会。

    但是,总有一件事情总是可行的,因为它是在操作系统而不是语言级别上完成的

    假设您有一个函数,该函数创建一个中间庞大的DataFrame,并返回较小的结果(也可能是DataFrame):

    1. def huge_intermediate_calc(something):
    2. ...
    3. huge_df = pd.DataFrame(...)
    4. ...
    5. return some_aggregate

    那如果你做类似的事情

    1. import multiprocessing
    2. result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]

    然后,该函数在不同的过程中执行。该过程完成后,操作系统将收回其使用的所有资源。实际上,Python,熊猫(垃圾收集器)无法阻止这种情况。

    — 阿米·塔沃里(Ami Tavory)
     source


    1

    @ b10hazard即使没有熊猫,我也从未完全理解Python内存在实践中是如何工作的。这种唯一的技巧是我唯一依靠的东西。

    — 阿米·塔沃里


    9

    真的很好。但是,在ipython环境(如jupyter笔记本)中,我发现您需要使用.close()和.join()或.terminate()池来摆脱生成的进程。从Python 3.3开始,最简单的方法是使用上下文管理协议with multiprocessing.Pool(1) as pool: result = pool.map(huge_intermediate_calc, [something])完成后立即关闭池。

    — Zertrin


    2

    这很好,只是不要忘了任务完成后终止并加入池。

    — Andrey Nikishaev


    1

    在阅读了几次有关如何从python对象获取内存的信息后,这似乎是实现此目的的最佳方法。创建一个进程,当该进程被终止时,操作系统将释放内存。

    — muammar


    1

    在创建池时,它可能会帮助某人,尝试使用maxtasksperchild = 1来释放进程并在完成工作后生成一个新进程。

    — giwiro


    22

    这解决了为我释放内存的问题!!!

    1. del [[df_1,df_2]]
    2. gc.collect()
    3. df_1=pd.DataFrame()
    4. df_2=pd.DataFrame()

    数据框将显式设置为null

    — 哈迪
     source


    1

    为什么将数据帧添加到子列表[[df_1,df_2]]中?有什么具体原因吗?请解释。

    — goks'Apr


    5

    您为什么不只使用最后两个语句?我认为您不需要前两个陈述。

    — spacedustpi


    3

    del df如果删除时有任何引用,将不会df删除。因此,您需要删除所有对其的引用,del df以释放内存。

    因此,应删除绑定到df的所有实例以触发垃圾回收。

    使用objgragh来检查哪些对象被保留。

    — 马龙·阿比昆
     source


    链接指向objgraph(mg.pov.lt/objgraph),除非您有objgragh,否则它是您输入的错字

    — SatZ


    1

    似乎glibc有一个问题会影响Pandas中的内存分配:https : //github.com/pandas-dev/pandas/issues/2659

    对这个问题的详细猴补丁解决了我的问题:

    1. # monkeypatches.py
    2. # Solving memory leak problem in pandas
    3. # https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
    4. import pandas as pd
    5. from ctypes import cdll, CDLL
    6. try:
    7. cdll.LoadLibrary("libc.so.6")
    8. libc = CDLL("libc.so.6")
    9. libc.malloc_trim(0)
    10. except (OSError, AttributeError):
    11. libc = None
    12. __old_del = getattr(pd.DataFrame, '__del__', None)
    13. def __new_del(self):
    14. if __old_del:
    15. __old_del(self)
    16. libc.malloc_trim(0)
    17. if libc:
    18. print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
    19. pd.DataFrame.__del__ = __new_del
    20. else:
    21. print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)

    — 标记NS
     source

  • 相关阅读:
    ARM的异常处理
    【Python算法】(一)递归:美术馆艺术大盗问题
    利用Python爬虫获取某乎热榜
    Android Studio(意图Intent)
    22 mysql range 查询
    svo2.0 svo pro 编译运行
    【数据结构】堆及堆排序详解
    GIT代码迁移和仓库镜像
    设计模式—结构型模式之外观模式(门面模式)
    企业虚拟化KVM的三种安装方式(1、完全文本2、模板镜像+配置文件3、gustos图形方式部署安装虚拟机)
  • 原文地址:https://blog.csdn.net/lizz2276/article/details/126013237