欢迎光临
我们一直在努力

如何使numpy创建动态数组以节省内存

对于个人站长或技术开发者来说,在处理大量日志数据、传感器数据或爬虫结果时,经常需要在VPS或虚拟机上使用Python和NumPy来构建数据集。NumPy数组的核心优势在于其固定大小和连续内存存储,但这也意味着它们并非天然支持高效的动态增长

尝试使用传统方法(如在循环中反复使用 numpy.appendnumpy.concatenate)来“动态”地构建大型数组,会导致极度的内存浪费和性能下降,因为每次操作都会创建并复制整个数组的新副本。

本文将介绍两种高效构建大型数据集的方法:List累积转换(最常用且高效)和NumPy内存映射(Memory Mapping)(适用于超大型数据集)。

1. 为什么传统的NumPy追加是低效的?

当你在一个循环中追加数据时,NumPy需要为新数组找到一块新的、更大的连续内存区域,并将旧数据和新数据全部复制过去。这不仅消耗大量的CPU时间,还会暂时占用双倍甚至多倍于最终数组的内存。

import numpy as np
import time

# 错误且低效的方法示例
def inefficient_append(num_elements):
    data_array = np.array([], dtype=np.int32)
    start_time = time.time()
    for i in range(num_elements):
        # 每次循环都创建一个新的更大的数组副本
        data_array = np.append(data_array, i)
    end_time = time.time()
    print(f"低效追加耗时: {end_time - start_time:.4f} 秒")

inefficient_append(10000)
# 输出结果会显示较高的耗时,随着元素数量增加,耗时呈几何级增长

2. 最佳实践:使用Python List累积然后一次性转换

实现“动态”增长同时节省内存和时间的最简单且最高效的方法是利用Python的内置列表(List)来累积数据。Python List在内部优化了内存管理,允许高效追加。当所有数据收集完毕后,只需一步将其转换为NumPy数组。

import numpy as np
import time

# 最佳实践:使用Python List累积
def efficient_list_conversion(num_elements):
    data_list = []
    start_time = time.time()

    # 步骤一:在循环中高效地追加到 List
    for i in range(num_elements):
        data_list.append(i)

    # 步骤二:一次性将 List 转换为 NumPy 数组
    final_array = np.array(data_list, dtype=np.int32)

    end_time = time.time()
    print(f"List 累积后转换耗时: {end_time - start_time:.4f} 秒")
    print(f"最终数组形状: {final_array.shape}")

efficient_list_conversion(10000)

3. 内存映射(Memory Mapping)技术:处理超大型数组

如果你的数据集太大,以至于无法完全加载到VPS的RAM中(例如几十GB甚至上百GB的数据),你可以利用NumPy的内存映射(np.memmap)功能。这使得操作系统可以将文件视为内存的一部分,只在需要时将数据块加载到RAM中。这对于在资源受限的公有云虚拟机上处理大数据非常有用。

import numpy as np
import os

FILE_NAME = 'large_data.mmap'
NUM_ELEMENTS = 10**7 # 1000万个元素

# 确保文件不存在,否则会加载旧数据
if os.path.exists(FILE_NAME):
    os.remove(FILE_NAME)

# 1. 创建并初始化内存映射文件
# dtype='float32' 比默认的float64节省一半内存
array_shape = (NUM_ELEMENTS,)

# mode='w+' 表示读/写并创建新文件
mmap_array = np.memmap(FILE_NAME, dtype='float32', mode='w+', shape=array_shape)

# 2. 像使用普通NumPy数组一样写入数据
# 注意:写入操作可能会导致磁盘I/O
print(f"正在写入 {NUM_ELEMENTS} 个元素到磁盘...")
for i in range(NUM_ELEMENTS):
    mmap_array[i] = i * 0.5

# 3. 刷新数据到磁盘并关闭文件句柄
mmap_array.flush()
del mmap_array

# 4. 重新加载并读取数据
# mode='r' 表示只读
reloaded_array = np.memmap(FILE_NAME, dtype='float32', mode='r', shape=array_shape)

print(f"读取前10个元素: {reloaded_array[:10]}")
# 注意:只有在访问时,对应的数据块才会被加载到内存中,极大地节省了RAM空间。
【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 如何使numpy创建动态数组以节省内存
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址