Python 如何释放数据帧使用的内存?
我有一个非常大的csv文件,我在pandas中打开,如下所示Python 如何释放数据帧使用的内存?,python,pandas,memory,Python,Pandas,Memory,我有一个非常大的csv文件,我在pandas中打开,如下所示 import pandas df = pandas.read_csv('large_txt_file.txt') 一旦我这样做了,我的内存使用就会增加2GB,这是因为该文件包含数百万行。当我需要释放这个内存时,我的问题就来了。我跑 del df 然而,我的内存使用率并没有下降。这是释放数据帧使用的内存的错误方法吗?如果是,正确的方法是什么 del df如果在删除时有任何对df的引用,则不会删除。因此,您需要使用deldf删除对它的
import pandas
df = pandas.read_csv('large_txt_file.txt')
一旦我这样做了,我的内存使用就会增加2GB,这是因为该文件包含数百万行。当我需要释放这个内存时,我的问题就来了。我跑
del df
然而,我的内存使用率并没有下降。这是释放数据帧使用的内存的错误方法吗?如果是,正确的方法是什么
del df
如果在删除时有任何对df
的引用,则不会删除。因此,您需要使用deldf
删除对它的所有引用以释放内存
因此,所有绑定到df的实例都应该被删除以触发垃圾收集
用于检查哪些对象保留在对象上。如注释中所述,有一些方法可以尝试:
gc.collect
(@EdChum)可以清除内容,例如。至少从我的经验来看,这些东西有时有效,但往往无效
然而,有一件事总是有效的,因为它是在操作系统而不是语言级别完成的
假设您有一个函数,该函数创建一个中间巨大的数据帧,并返回一个较小的结果(也可能是一个数据帧):
如果你做了类似的事情
import multiprocessing
result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]
那么。当该过程完成时,操作系统将重新获取它使用的所有资源。垃圾收集器pandas说,Python实在无法阻止这种情况。减少Python中的内存使用是很困难的,因为。如果删除对象,则内存可用于新的Python对象,但不能返回系统() 如果您坚持使用数值numpy数组,则会释放这些数组,但不会释放装箱对象
>>> import os, psutil, numpy as np
>>> def usage():
... process = psutil.Process(os.getpid())
... return process.get_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--cache size=5
(默认值为1000)调整ipython保留的历史记录量
减少数据帧大小
尽可能避免使用对象数据类型
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
带有对象数据类型的值被装箱,这意味着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
>df1.info()
INT64索引:39681584个条目,0到39681583
数据列(共1列):
富64
数据类型:float64(1)
内存使用率:605.5 MB
>>>df1.1形状
(39681584, 1)
>>>df1.foo.isnull().sum()*100./len(df1)
20.628483479893344#因此20%的值为NaN
>>>df1.to_sparse().info()
INT64索引:39681584个条目,0到39681583
数据列(共1列):
富64
数据类型:float64(1)
内存使用率:543.0 MB
查看内存使用情况
您可以查看内存使用情况():
>>df.info()
INT64索引:39681584个条目,0到39681583
数据列(共14列):
...
数据类型:datetime64[ns](1)、float64(8)、int64(1)、object(4)
内存使用率:4.4+GB
从pandas 0.17.1开始,您还可以执行
df.info(memory_usage='deep')
查看包括对象在内的内存使用情况。这为我解决了释放内存的问题
import gc
import pandas as pd
del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()
数据帧将显式设置为null
在上述声明中
首先,数据帧的自引用被删除,这意味着在垃圾收集器(gc.collect())收集了数据帧的所有引用之后,python不再可以使用该数据帧,然后显式地将所有引用设置为空数据帧
有关垃圾收集器工作的更多信息,请参见中的详细说明。glibc似乎存在影响内存分配的问题: 已为我解决了问题:
# 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)
下面是我为解决这个问题所做的工作 我有一个小应用程序,它将大数据集读入pandas dataframe,并将其用作api。然后,用户可以通过向api传递查询参数来查询数据帧。当用户读入多个数据集时,应用程序显然面临内存使用限制 将数据集读入数据帧字典,而不是读入单个数据帧变量
df\u文件内容[文件名]=pd.read\u csv(..)
前端提供了一个api来清除字典。这将调用dictionary的clear()方法。这可以自定义为当sys.getsizeof(df_file_contents)为特定大小时调用,或者可以用于删除特定键
df\u file\u contents.clear()
这是正确的,垃圾收集器可能不会立即释放内存,您也可以导入gc
模块并调用gc.collect()
但它可能不会恢复内存del df
在创建df后不会直接调用,对吗?我认为在您删除df时,有对df的引用。因此,它不会被删除,而是会删除名称。垃圾收集器回收的内存是否实际返回给操作系统取决于实现;垃圾收集器的唯一保证是当前Python进程可以将回收的内存用于其他事情,而不是从操作系统请求甚至更多内存。我没有向df添加任何其他引用。我所做的只是行动
import gc
import pandas as pd
del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()
# 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)