Python 将大量列表转换为数据帧时出现内存问题
我在一段代码的最后一行出现Python 将大量列表转换为数据帧时出现内存问题,python,pandas,memory,Python,Pandas,Memory,我在一段代码的最后一行出现内存不足错误,这段代码在for循环中创建了一个巨大的列表,然后将其转换为数据帧 这是一个最小的可重复的例子。我相信在最后一行中使用了很多额外的内存。如何提高代码的内存效率 import random, pandas, string def function_to_generate_list(): def random_string(): return ''.join(random.choices(string.ascii_uppercase +
内存不足错误,这段代码在for循环中创建了一个巨大的列表,然后将其转换为数据帧
这是一个最小的可重复的例子。我相信在最后一行中使用了很多额外的内存。如何提高代码的内存效率
import random, pandas, string
def function_to_generate_list():
def random_string():
return ''.join(random.choices(string.ascii_uppercase + string.digits, k = 50))
return [random_string(), random_string(), random.random()]
len = 10000*20000
df = []
for i in range(len):
df.append(function_to_generate_list())
df = pandas.DataFrame(df, columns=['column1', 'column2', 'column3'])
考虑使用
执行此操作时,df.append(函数\u to \u generate\u list())
实际上不需要一次存储所有值。当您创建一个dataframe时,它只会在每个值上迭代一次,所以一次只需要一个值。这就是发电机的作用
您可以这样修改最后一行:
df = pandas.DataFrame((function_to_generate_list() for _ in range(length)),
columns=['column1', 'column2', 'column3'])
还要注意,我将len
重命名为length
。因为当您创建变量len
时,您会覆盖一个内置函数len,最好的选择是以数据帧容器使用的默认底层格式(即np.array
)预分配存储对象。通过这种方式,可以通过直接引用这些阵列来创建数据帧,而不是制作它们的转换副本,从而将内存占用减少大约一半
解决方案
基准
基准测试中使用了k=5
和length=1000000
。报告了不同方法的峰值内存使用情况。该基准测试在运行debian 10的Core i5-8250U(4C8T)64位笔记本电脑上执行。通过在tracemaloc.start()
和tracemaloc.get\u tracked\u memory()
之间插入解决方案代码来执行基准测试
- 此解决方案:148.825M谢谢@go2nirvana!我对发电机不熟悉。只需将最后四行更改为您上面所写的内容就足够了,或者我是否应该在
函数\u到生成\u列表
定义中进行更改(例如将返回
替换为收益
)?此答案不会减少内存占用,因为仍需创建中间非数组对象。看看我的答案,谢谢你,黄比尔!在实际情况中,我知道长度的上限,但不知道到底要填充多少。换句话说,在调用函数\u生成\u list()
之前有一个if条件。在这种情况下,我想我仍然可以得到大小为长度
的数组,并且在创建数据帧后删除
未填充的行?或者有更好的方法吗?我不认为会有一个强有力的解决办法,因为总是有机会达到理论上限。您唯一能做的就是为对象找到更多可用空间。例如,用于将输出阵列存储在硬盘上。我明白了,谢谢。我有足够的空间来处理上限被命中的情况,但是我必须删除未填充的行,因为这些行不应该包含在最终的数据帧中。所以我的最后一个问题是关于drop
操作是否有效。如果我通过调用df.drop(df.index[df.column1==0],inplace=True)执行就地drop
操作来删除未填充的行,这不会增加大量内存使用,对吗?我的意思是,数据帧在删除过程中不会被复制到任何地方
?我认为这已经是另一个问题了,因为实现可能必须根据这些附加信息进行调整。您可以发布一个单独的后续问题。
import tracemalloc
import numpy as np
# provided function omitted
tracemalloc.start()
# preallocated output
arr1 = np.zeros(length, dtype=object)
arr2 = np.zeros(length, dtype=object)
arr3 = np.zeros(length, dtype=float)
# assign directly
for i in range(length):
arr1[i], arr2[i], arr3[i] = function_to_generate_list()
# make it a dataframe
df = pd.DataFrame(
{'column1': arr1, 'column2': arr2, 'column3': arr3}
)
print(f"===== Memory Footprint =====")
first, peak = tracemalloc.get_traced_memory()
print(f"Peak memory usage: {peak} ({peak/1048576:.3f}M)")