b10*_*ard 82 python memory pandas
我有一个非常大的csv文件,我在熊猫中打开如下....
import pandas
df = pandas.read_csv('large_txt_file.txt')
Run Code Online (Sandbox Code Playgroud)
一旦我这样做,我的内存使用量增加2GB,这是预期的,因为该文件包含数百万行.当我需要释放这个内存时,我的问题出现了.我跑了....
del df
Run Code Online (Sandbox Code Playgroud)
但是,我的内存使用率没有下降.这是释放熊猫数据帧所使用的内存的错误方法吗?如果是,那么正确的方法是什么?
Wil*_*hes 85
减少Python中的内存使用很困难,因为Python实际上并没有将内存释放回操作系统.如果删除对象,则内存可用于新的Python对象,但不能free()返回系统(请参阅此问题).
如果您坚持使用数字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
Run Code Online (Sandbox Code Playgroud)
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}
Run Code Online (Sandbox Code Playgroud)
您可以通过键入%reset Out以清除历史记录来解决此问题.或者,您可以调整ipython保留的历史记录ipython --cache-size=5(默认值为1000).
尽可能避免使用对象dtypes.
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
Run Code Online (Sandbox Code Playgroud)
带有对象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
Run Code Online (Sandbox Code Playgroud)
您可能希望避免使用字符串列,或者找到将字符串数据表示为数字的方法.
如果您的数据框包含许多重复值(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
Run Code Online (Sandbox Code Playgroud)
您可以查看内存使用情况(docs):
>>> 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
Run Code Online (Sandbox Code Playgroud)
从pandas 0.17.1开始,您还df.info(memory_usage='deep')可以查看内存使用情况,包括对象.
Ami*_*ory 29
正如评论中所指出的,有一些事情要尝试:gc.collect(@ EdChum)可能会清除一些东西,例如.至少根据我的经验,这些东西有时是有效的,而且往往不起作用.
但是,有一件事总是有效的,因为它是在操作系统上完成的,而不是语言级别.
假设您有一个创建中间巨大DataFrame的函数,并返回一个较小的结果(也可能是一个DataFrame):
def huge_intermediate_calc(something):
...
huge_df = pd.DataFrame(...)
...
return some_aggregate
Run Code Online (Sandbox Code Playgroud)
然后,如果你做的事情
import multiprocessing
result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]
Run Code Online (Sandbox Code Playgroud)
然后该函数在不同的进程中执行.该过程完成后,操作系统将重新获得其使用的所有资源.实际上没有什么Python,大熊猫,垃圾收集器可以阻止它.
har*_*rdi 12
这解决了为我释放内存的问题!
del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()
Run Code Online (Sandbox Code Playgroud)
数据框将显式设置为null
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)
Run Code Online (Sandbox Code Playgroud)