我有一个非常大的 csv 文件,我打开在熊猫如下..。
import pandas df = pandas.read_csv('large_txt_file.txt')
一旦我这样做,我的内存使用增加了2 GB,这是预期的,因为这个文件包含数百万行。当我需要释放这段记忆时,问题就来了。我跑了。
del df
但是,我的内存使用并没有下降。这是否是熊猫数据帧释放内存的错误方法?如果是的话,正确的方法是什么?
如果在删除时有任何对 df的引用,则 del df不会被删除。因此,您需要使用 del df删除对它的所有引用以释放内存。
df
因此,应该删除绑定到 df 的所有实例,以触发垃圾回收。
使用 Obgragh检查哪些对象持有对象。
正如评论中提到的,有一些事情可以尝试: 例如,gc.collect(@EdChum)可以清除一些东西。至少从我的经验来看,这些东西有时候管用,有时候不管用。
gc.collect
然而,有一件事总是可行的,因为它是在操作系统而不是语言级别上完成的。
假设您有一个函数,它创建了一个中间巨大的 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 中减少内存使用是困难的,因为 Python 实际上并没有将内存释放回操作系统。如果删除了对象,那么内存对于新的 Python 对象是可用的,但是 free()不能返回到系统(看到这个问题)。
free()
如果您坚持使用数字数组,那么这些数组将被释放,但是装箱的对象将不会被释放。
>>> 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,这样就不会创建副本。
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)。
%reset Out
ipython --cache-size=5
尽可能避免使用对象 dtype。
>>> 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 在数组中支持固定大小的字符串,而熊猫不支持(导致了用户的困惑)。这可能会产生显著的差异:
>>> 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
从0.17.1开始,您还可以执行 df.info(memory_usage='deep')来查看包括对象在内的内存使用情况。
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.Collection ())收集并显式地将所有引用设置为空数据帧后,那里的 python 将不再可用该数据帧。
更多关于垃圾收集器的工作在 https://stackify.com/python-garbage-collection/中有很好的解释
看起来 glibc 有个问题影响了熊猫的内存分配: 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)
以下是我为解决这个问题所做的工作。
我有一个小应用程序,读入大型数据集熊猫数据框架,并作为一个应用程序接口服务器。然后,用户可以通过将查询参数传递到 api 来查询数据框架。当用户读入多个数据集时,显然应用程序面临内存使用限制。
不要将数据集读入单个数据框架变量,而是将它们读入数据框架字典。
df_file_contents[file_name] = pd.read_csv(..)
前端提供了一个 api 来清除字典。这将调用 dictionary 的 clear ()方法。这可以定制为在 sys.getsizeof (df _ file _ content)具有一定大小或可用于删除某些键时调用。
df_file_contents.clear()