如何释放 pandas 数据框使用的内存?
- 2025-02-18 09:23:00
- admin 原创
- 35
问题描述:
我有一个非常大的 csv 文件,我在 pandas 中打开它,如下所示......
import pandas
df = pandas.read_csv('large_txt_file.txt')
一旦我这样做,我的内存使用量就会增加 2GB,这是意料之中的,因为这个文件包含数百万行。当我需要释放这块内存时,问题就出现了。我跑了……
del df
但是,我的内存使用量并没有下降。这是释放 pandas 数据框使用的内存的错误方法吗?如果是,正确的方法是什么?
解决方案 1:
减少 Python 中的内存使用量很困难,因为Python 实际上不会将内存释放回操作系统。如果删除对象,则内存可供新的 Python 对象使用,但不会free()
释放回系统(请参阅此问题)。
如果您坚持使用数字 numpy 数组,那么这些数组会被释放,但装箱对象则不会。
>>> import os, psutil, numpy as np # psutil may need to be installed
>>> def usage():
... process = psutil.Process(os.getpid())
... return process.memory_info()[0] / float(2 ** 20)
...
>>> usage() # initial memory usage
27.5
>>> arr = np.arange(10 ** 8) # create a large array without boxing
>>> usage()
790.46875
>>> del arr
>>> usage()
27.52734375 # numpy just free()'d the array
>>> arr = np.arange(10 ** 8, dtype='O') # create lots of objects
>>> usage()
3135.109375
>>> del arr
>>> usage()
2372.16796875 # numpy frees the array, but python keeps the heap big
减少数据帧的数量
Python 将内存保持在高水位,但我们可以减少创建的数据帧总数。修改数据帧时,请选择inplace=True
,这样您就不会创建副本。
另一个常见问题是保留 ipython 中先前创建的数据框的副本:
In [1]: import pandas as pd
In [2]: df = pd.DataFrame({'foo': [1,2,3,4]})
In [3]: df + 1
Out[3]:
foo
0 2
1 3
2 4
3 5
In [4]: df + 2
Out[4]:
foo
0 3
1 4
2 5
3 6
In [5]: Out # Still has all our temporary DataFrame objects!
Out[5]:
{3: foo
0 2
1 3
2 4
3 5, 4: foo
0 3
1 4
2 5
3 6}
您可以通过键入%reset Out
清除历史记录来解决这个问题。或者,您可以调整 ipython 保留的历史记录量ipython --cache-size=5
(默认值为 1000)。
减少数据帧大小
尽可能避免使用对象数据类型。
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
具有对象 dtype 的值是装箱的,这意味着 numpy 数组只包含一个指针,而数据框中的每个值在堆上都有一个完整的 Python 对象。这包括字符串。
虽然 numpy 支持数组中的固定大小字符串,但 pandas 却不支持(这给用户带来了困惑)。这会带来很大的不同:
>>> import numpy as np
>>> arr = np.array(['foo', 'bar', 'baz'])
>>> arr.dtype
dtype('S3')
>>> arr.nbytes
9
>>> import sys; import pandas as pd
>>> s = pd.Series(['foo', 'bar', 'baz'])
dtype('O')
>>> sum(sys.getsizeof(x) for x in s)
120
您可能希望避免使用字符串列,或者找到一种将字符串数据表示为数字的方法。
如果您有一个包含许多重复值(NaN 非常常见)的数据框,那么您可以使用稀疏数据结构来减少内存使用量:
>>> df1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo float64
dtypes: float64(1)
memory usage: 605.5 MB
>>> df1.shape
(39681584, 1)
>>> df1.foo.isnull().sum() * 100. / len(df1)
20.628483479893344 # so 20% of values are NaN
>>> df1.to_sparse().info()
<class 'pandas.sparse.frame.SparseDataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo float64
dtypes: float64(1)
memory usage: 543.0 MB
查看内存使用情况
您可以查看内存使用情况(文档):
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 14 columns):
...
dtypes: datetime64[ns](1), float64(8), int64(1), object(4)
memory usage: 4.4+ GB
从 pandas 0.17.1 开始,您还可以df.info(memory_usage='deep')
查看包括对象在内的内存使用情况。
解决方案 2:
正如评论中所述,有一些事情可以尝试:gc.collect
例如,(@EdChum) 可能会清除一些东西。至少从我的经验来看,这些事情有时有效,但通常无效。
然而,有一件事总是有效的,因为它是在操作系统而不是语言层面完成的。
假设您有一个函数,它创建一个中间巨大的 DataFrame,并返回一个较小的结果(也可能是一个 DataFrame):
def huge_intermediate_calc(something):
...
huge_df = pd.DataFrame(...)
...
return some_aggregate
然后如果你做类似的事情
import multiprocessing
result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]
然后该函数在另一个进程中执行。当该进程完成时,操作系统将重新占用它使用的所有资源。Python、pandas 和垃圾收集器实际上无法阻止这种情况。
解决方案 3:
这帮我解决了释放内存的问题!!!
import gc
import pandas as pd
del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()
数据框将明确设置为空
在上述声明中
首先,数据框的自引用被删除,这意味着在数据框的所有引用都被垃圾收集器(gc.collect())收集后,数据框不再可供 Python 使用,然后将所有引用明确设置为空数据框。
有关垃圾收集器的工作原理的更多信息,请参阅https://stackify.com/python-garbage-collection/
解决方案 4:
del df
`df如果在删除时有任何对 的引用,则不会被删除。因此您需要使用 删除对它的所有引用
del df`以释放内存。
因此,应删除与 df 绑定的所有实例以触发垃圾收集。
使用objgragh检查哪个正在抓住物体。
解决方案 5:
glibc 似乎存在一个问题,影响了 Pandas 中的内存分配:https://github.com/pandas-dev/pandas/issues/2659
关于这个问题的详细介绍的猴子补丁已经为我解决了这个问题:
# monkeypatches.py
# Solving memory leak problem in pandas
# https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
import pandas as pd
from ctypes import cdll, CDLL
try:
cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")
libc.malloc_trim(0)
except (OSError, AttributeError):
libc = None
__old_del = getattr(pd.DataFrame, '__del__', None)
def __new_del(self):
if __old_del:
__old_del(self)
libc.malloc_trim(0)
if libc:
print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
pd.DataFrame.__del__ = __new_del
else:
print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)
解决方案 6:
以下是我为解决这个问题所采取的措施。
我有一个小应用程序,它将大型数据集读入 pandas 数据框并将其作为 api 提供。然后,用户可以通过将查询参数传递给 api 来查询数据框。当用户读入多个数据集时,应用程序显然会面临内存使用限制。
不要将数据集读入单个数据框变量,而是将它们读入数据框字典。
df_file_contents[file_name] = pd.read_csv(..)
已向前端提供 api 来清除字典。这将调用字典的 clear() 方法。可以自定义当 sys.getsizeof(df_file_contents) 达到特定大小时调用该方法,或者用于删除某些键。
df_file_contents.clear()
- 2025年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)