我有一段代码可以接收来自另一个函数的回调,并创建一个列表列表(pd_arr).然后,此列表用于创建数据框.最后,列表列表被删除.使用memory-profiler进行分析时,这是输出102.632812 MiB 0.000000 MiB init()236....

我有一段代码可以接收来自另一个函数的回调,并创建一个列表列表(pd_arr).然后,此列表用于创建数据框.最后,列表列表被删除.
使用memory-profiler进行分析时,这是输出
102.632812 MiB 0.000000 MiB init()
236.765625 MiB 134.132812 MiB add_to_list()
return pd.DataFrame()
394.328125 MiB 157.562500 MiB pd_df = pd.DataFrame(pd_arr, columns=df_columns)
350.121094 MiB -44.207031 MiB pd_df = pd_df.set_index(df_columns[0])
350.292969 MiB 0.171875 MiB pd_df.memory_usage()
350.328125 MiB 0.035156 MiB print sys.getsizeof(pd_arr), sys.getsizeof(pd_arr[0]), sys.getsizeof(pd_df), len(pd_arr)
350.328125 MiB 0.000000 MiB del pd_arr
在检查pd_df(数据帧)的深内存使用量时,它为80.5 MB.因此,我的问题是为什么在del pd_arr行之后内存不会减少.
此外,每个探查器的总数据帧大小(157-44 = 110 MB)似乎大于80 MB.那么,是什么导致差异呢?
另外,还有没有其他内存有效的方式来创建数据帧(循环接收的数据),而这种方式在时间性能上并不太差(例如:对于大小为100MB的数据帧,以10秒的秒增量为佳)?
编辑:解释此行为的简单python脚本
Filename: py_test.py
Line # Mem usage Increment Line Contents
================================================
9 102.0 MiB 0.0 MiB @profile
10 def setup():
11 global arr, size
12 102.0 MiB 0.0 MiB arr = range(1, size)
13 131.2 MiB 29.1 MiB arr = [x+1 for x in arr]
Filename: py_test.py
Line # Mem usage Increment Line Contents
================================================
21 131.2 MiB 0.0 MiB @profile
22 def tearDown():
23 global arr
24 131.2 MiB 0.0 MiB del arr[:]
25 131.2 MiB 0.0 MiB del arr
26 93.7 MiB -37.4 MiB gc.collect()
在介绍数据框时,
Filename: py_test.py
Line # Mem usage Increment Line Contents
================================================
9 102.0 MiB 0.0 MiB @profile
10 def setup():
11 global arr, size
12 102.0 MiB 0.0 MiB arr = range(1, size)
13 132.7 MiB 30.7 MiB arr = [x+1 for x in arr]
Filename: py_test.py
Line # Mem usage Increment Line Contents
================================================
15 132.7 MiB 0.0 MiB @profile
16 def dfCreate():
17 global arr
18 147.1 MiB 14.4 MiB pd_df = pd.DataFrame(arr)
19 147.1 MiB 0.0 MiB return pd_df
Filename: py_test.py
Line # Mem usage Increment Line Contents
================================================
21 147.1 MiB 0.0 MiB @profile
22 def tearDown():
23 global arr
24 #del arr[:]
25 147.1 MiB 0.0 MiB del arr
26 147.1 MiB 0.0 MiB gc.collect()
解决方法:
回答您的第一个问题时,当您尝试使用del pd_arr清除内存时,实际上不会发生这种情况,因为DataFrame存储了一个指向pd_arr的链接,而顶级范围又保留了一个链接.减少refcounter不会收集内存,因为该内存正在使用中.
您可以通过在del pd_arr之前运行sys.getrefcount(pd_arr)来检查我的假设,结果是2.
现在,我相信下面的代码片段与您要执行的操作相同:https://gist.github.com/vladignatyev/ec7a26b7042efd6f710d436afbfb87de/90df8cc6bbb8bd0cb3a1d2670e03aff24f3a5b24
如果您尝试使用此代码段,则会看到如下所示的内存使用情况:
Line # Mem usage Increment Line Contents
================================================
13 63.902 MiB 0.000 MiB @profile
14 def to_profile():
15 324.828 MiB 260.926 MiB pd_arr = make_list()
16 # pd_df = pd.DataFrame.from_records(pd_arr, columns=[x for x in range(0,1000)])
17 479.094 MiB 154.266 MiB pd_df = pd.DataFrame(pd_arr)
18 # pd_df.info(memory_usage='deep')
19 479.094 MiB 0.000 MiB print sys.getsizeof(pd_arr), sys.getsizeof(pd_arr[0])
20 481.055 MiB 1.961 MiB print sys.getsizeof(pd_df), len(pd_arr)
21 481.055 MiB 0.000 MiB print sys.getrefcount(pd_arr)
22 417.090 MiB -63.965 MiB del pd_arr
23 323.090 MiB -94.000 MiB gc.collect()
试试这个例子:
@profile
def test():
a = [x for x in range(0,100000)]
del a
aa = test()
您将得到您所期望的:
Line # Mem usage Increment Line Contents
================================================
6 64.117 MiB 0.000 MiB @profile
7 def test():
8 65.270 MiB 1.152 MiB a = [x for x in range(0,100000)]
9 # print sys.getrefcount(a)
10 64.133 MiB -1.137 MiB del a
11 64.133 MiB 0.000 MiB gc.collect()
另外,如果调用sys.getrefcount(a),则有时会在del a之前清除内存:
Line # Mem usage Increment Line Contents
================================================
6 63.828 MiB 0.000 MiB @profile
7 def test():
8 65.297 MiB 1.469 MiB a = [x for x in range(0,100000)]
9 64.230 MiB -1.066 MiB print sys.getrefcount(a)
10 64.160 MiB -0.070 MiB del a
但是当您使用熊猫时,事情变得疯狂.
如果打开pandas.DataFrame的源代码,您将看到,在使用list初始化DataFrame的情况下,pandas将创建新的NumPy数组并复制其内容.检查一下:https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L329
删除pd_arr不会释放内存,因为pd_arr将在创建DataFrame并退出函数后收集,因为它没有任何其他链接.前后的getrefcount调用证明了这一点.
从普通列表创建新的DataFrame会使您的列表与NumPy Array复制. (查看np.array(data,dtype = dtype,copy = copy)和有关array的相应文档)
复制操作可能会影响执行时间,因为分配新的内存块是一项繁重的操作.
我试图用Numpy数组初始化新的DataFrame.唯一的区别是numpy.Array内存开销出现的位置.比较以下两个片段:
def make_list(): # 1
pd_arr = []
for i in range(0,10000):
pd_arr.append([x for x in range(0,1000)])
return np.array(pd_arr)
和
def make_list(): #2
pd_arr = []
for i in range(0,10000):
pd_arr.append([x for x in range(0,1000)])
return pd_arr
数字1(创建DataFrame不会产生内存使用开销!):
Line # Mem usage Increment Line Contents
================================================
14 63.672 MiB 0.000 MiB @profile
15 def to_profile():
16 385.309 MiB 321.637 MiB pd_arr = make_list()
17 385.309 MiB 0.000 MiB print sys.getrefcount(pd_arr)
18 385.316 MiB 0.008 MiB pd_df = pd.DataFrame(pd_arr)
19 385.316 MiB 0.000 MiB print sys.getsizeof(pd_arr), sys.getsizeof(pd_arr[0])
20 386.934 MiB 1.617 MiB print sys.getsizeof(pd_df), len(pd_arr)
21 386.934 MiB 0.000 MiB print sys.getrefcount(pd_arr)
22 386.934 MiB 0.000 MiB del pd_arr
23 305.934 MiB -81.000 MiB gc.collect()
2号(由于复制阵列而导致100Mb以上的开销)!:
Line # Mem usage Increment Line Contents
================================================
14 63.652 MiB 0.000 MiB @profile
15 def to_profile():
16 325.352 MiB 261.699 MiB pd_arr = make_list()
17 325.352 MiB 0.000 MiB print sys.getrefcount(pd_arr)
18 479.633 MiB 154.281 MiB pd_df = pd.DataFrame(pd_arr)
19 479.633 MiB 0.000 MiB print sys.getsizeof(pd_arr), sys.getsizeof(pd_arr[0])
20 481.602 MiB 1.969 MiB print sys.getsizeof(pd_df), len(pd_arr)
21 481.602 MiB 0.000 MiB print sys.getrefcount(pd_arr)
22 417.621 MiB -63.980 MiB del pd_arr
23 330.621 MiB -87.000 MiB gc.collect()
因此,仅使用Numpy Array(而不是列表)初始化DataFrame.从内存消耗的角度来看,它更好,并且可能更快,因为它不需要额外的内存分配调用.
希望我现在已经回答了您所有的问题.
本文标题为:python-增加熊猫数据帧创建时的内存使用率


基础教程推荐
- Centos7运行python脚本报错 /usr/bin/python3^M: bad interpreter: No such file or directory解决方法 2023-09-04
- Python--day39--进程池原理及效率测试 2023-09-04
- PyTorch搭建CNN实现风速预测 2023-08-11
- Python实现发送警告通知到企业微信方法详解 2023-08-11
- Python全栈开发——进程与线程(2) 2023-09-03
- Python 制作子弹图 2023-08-08
- python基础之并发编程(一) 2023-08-05
- Python Pandas如何获取和修改任意位置的值(at,iat,loc,iloc) 2023-08-04
- Python中的def __init__( )函数 2022-10-20
- Python实现字符串匹配算法代码示例 2023-09-04