Python 如何释放数据帧使用的内存?

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删除对它的

我有一个非常大的csv文件,我在pandas中打开,如下所示

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)