追加多个2D NumPy数组生成3D数组时如何降低内存占用
问题原因分析
np.append完全不适合这个场景:它每次调用都会重新申请内存、复制整个已有数组,700万次循环的情况下不仅速度极慢,内存峰值还会是最终数组的数倍,完全不可用。- 用Python列表存700万个独立的NumPy数组时,每个数组都有几十字节的元数据(shape、dtype、引用计数等)开销,汇总后额外占用几个GB内存,这就是直接调用
np.array(my_list)时内存占用过高的核心原因。
最优解决方案:提前预分配目标数组
核心思路是先统计最终生成的样本总数,直接申请刚好够用的数组空间,全程没有额外冗余内存占用,速度也最快。
import numpy as np import time from tqdm import tqdm def get_X(df_values): # 第一步:轻量循环统计总样本数,不存数据只计数,开销极低 sample_count = 0 counter = 0 start_point = 20 previous_ticker = None df_length = len(df_values) for row in df_values: if 0 <= start_point < df_length: ticker = df_values[start_point][0] if sample_count == 0: previous_ticker = ticker if ticker != previous_ticker: counter += 20 start_point += 20 previous_ticker = ticker sample_count += 1 counter += 1 start_point += 1 else: break # 第二步:预分配刚好大小的数组,按需指定dtype可以大幅降内存 # 比如float32比默认float64省一半内存,整数场景可以用int16/int32进一步压缩 x = np.empty((sample_count, 20, 22), dtype=np.float32) y = np.empty(sample_count, dtype=np.int32) # 第三步:循环直接赋值,不需要任何追加操作 counter = 0 start_point = 20 previous_ticker = None idx = 0 time_1 = time.time() for row in tqdm(df_values): if 0 <= start_point < df_length: ticker = df_values[start_point][0] flag = row[30] if idx == 0: previous_ticker = ticker if ticker != previous_ticker: counter += 20 start_point += 20 previous_ticker = ticker # 直接写入预分配的数组,无额外内存开销 x[idx] = df_values[counter:start_point] y[idx] = flag counter += 1 start_point += 1 idx += 1 else: break print("Time to finish the loop", time.time()-time_1) return x, y x, y = get_X(df.to_numpy())
可选优化方案
不想跑两次循环的增量预分配方案
如果数据集太大跑两次循环嫌麻烦,可以初始化一个初始容量的数组,存满后将容量翻倍,最后截断到实际大小,性能也远高于动态追加:
# 核心逻辑示例 init_capacity = 1000000 # 初始容量100万,按需调整 x = np.empty((init_capacity, 20, 22), dtype=np.float32) y = np.empty(init_capacity, dtype=np.int32) idx = 0 for 循环逻辑: if idx >= len(x): # 容量不够时翻倍 x.resize((len(x)*2, 20, 22)) y.resize((len(y)*2,)) x[idx] = 特征 y[idx] = 标签 idx +=1 # 最后截断到实际样本数 x = x[:idx] y = y[:idx]
内存不足时的磁盘数组方案
如果内存放不下完整的3D数组,可以用NumPy的memmap把数组存在磁盘上,操作和普通NumPy数组完全一致,内存占用仅几MB:
# 替换预分配数组的代码即可 x = np.memmap('x_data.npy', mode='w+', shape=(sample_count,20,22), dtype=np.float32) y = np.memmap('y_data.npy', mode='w+', shape=(sample_count,), dtype=np.int32) # 用完执行flush保存到磁盘 x.flush() y.flush()
内容的提问来源于stack exchange,提问作者samer hassan
相关产品推荐
相关产品推荐

