You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

追加多个2D NumPy数组生成3D数组时如何降低内存占用

问题原因分析
  1. np.append 完全不适合这个场景:它每次调用都会重新申请内存、复制整个已有数组,700万次循环的情况下不仅速度极慢,内存峰值还会是最终数组的数倍,完全不可用。
  2. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 02:39:04