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

将大尺寸数据集高效加载至Python字典的内存优化问题

问题

我尝试将一个约560MB的大型数据集加载到Python字典中,用于生成3D图形,但遇到内存不足问题,进程被直接"Killed"。之后我添加了分块读取并定期将字典写入JSON文件的逻辑,想避免内存耗尽,但处理到约4.00M/558.0M进度时还是被终止了。

我想知道:

  1. 为什么仅560MB的文件,剔除无用列并转成字典后会占用数GB内存?
  2. 有没有更高效的方法,能得到可高效提取坐标集及其对应值的数据对象?

现有代码与示例数据

处理代码

import json
import logging
import os

import pandas as pd
from tqdm import tqdm


def create_grid_dict(file_path, chunk_size=500000):
    """
    :param file_path: Path to a grid file.
    :param chunk_size: Number of lines to process before dumping into json
    :return: Dictionary object containing the gist grid data with as index the voxel number
             and as values the x, y and z coordinates, and the value
    """
    # Read the data from the file
    with open(file_path, 'r') as file:
        # Read the first line
        header = file.readline().strip()
        header2 = file.readline().strip()
        # Log the header
        logging.info(header)
    columns = header2.split(' ')

    # Get the file size
    file_size = os.path.getsize(file_path)

    output_file = 'datasets/cache.json'
    # Check if the output file already exists
    if os.path.exists(output_file):
        with open(output_file, 'r') as f:
            grid_dict = json.load(f)
            return grid_dict
    else:
        # Create an empty dictionary to store the grid data
        grid_dict = {}

    logging.info(f"Reading file size {file_size} in chunks of {chunk_size} lines.")
    # Read the file in chunks
    with tqdm(total=file_size, unit='B', unit_scale=True, desc="Processing") as pbar:
        for chunk in pd.read_csv(file_path, delim_whitespace=True, skiprows=2, names=columns, chunksize=chunk_size):
            # Filter out the columns you need
            chunk = chunk[['voxel', 'xcoord', 'ycoord', 'zcoord', 'val1', 'val2']]

            # Iterate through each row in the chunk
            for index, row in chunk.iterrows():
                voxel = row['voxel']
                # Store the values in the dictionary
                grid_dict[voxel] = {
                    'xcoord': row['xcoord'],
                    'ycoord': row['ycoord'],
                    'zcoord': row['zcoord'],
                    'val': row['val1'] + 2 * row['val2']
                }
            pbar.update(chunk_size)

            # Write the grid dictionary to the output file after processing each chunk
            with open(output_file, 'w') as f:
                json.dump(grid_dict, f)
    return grid_dict

示例数据集

voxel xcoord ycoord zcoord val1 val2
1 0.1 0.2 0.3 10 5
2 0.2 0.3 0.4 8 4
3 0.3 0.4 0.5 12 6
4 0.4 0.5 0.6 15 7
5 0.5 0.6 0.7 9 3
6 0.6 0.7 0.8 11 5
7 0.7 0.8 0.9 13 6
8 0.8 0.9 1.0 14 7
9 0.9 1.0 1.1 16 8
10 1.0 1.1 1.2 18 9

分析与解决方案

一、内存暴涨的原因

  • Python字典的内存开销极高:每个键值对都需要存储哈希表结构、对象引用等额外信息,嵌套小字典时开销会进一步放大。560MB的文本数据转成Python对象后,内存占用通常会膨胀5-20倍。
  • 分块逻辑存在误区:你虽然分块读取数据,但grid_dict始终在内存中累积所有记录,写入JSON只是备份,内存中的字典并没有被清空,随着处理的块数增加,内存占用只会持续上升。
  • Pandas的额外消耗:DataFrame本身就有不小的内存开销,加上iterrows()逐行处理时会将每行转为Series对象,又增加了一层内存消耗。

二、高效解决方案

1. 改用HDF5列式存储(推荐)

HDF5支持增量写入、随机读取,无需加载全量数据到内存,且存储空间远小于JSON:

import pandas as pd
import os

def create_hdf_cache(file_path, output_file='datasets/cache.h5'):
    if os.path.exists(output_file):
        # 返回只读的HDF5数据接口,不会加载全量数据
        return pd.read_hdf(output_file, key='voxels', mode='r')
    
    # 读取表头
    with open(file_path, 'r') as f:
        f.readline()  # 跳过第一行注释表头
        columns = f.readline().strip().split()
    
    # 分块处理并写入HDF5
    chunk_iter = pd.read_csv(
        file_path,
        delim_whitespace=True,
        skiprows=2,
        names=columns,
        chunksize=500000
    )
    
    for i, chunk in enumerate(chunk_iter):
        # 计算目标值并保留所需列
        chunk['val'] = chunk['val1'] + 2 * chunk['val2']
        chunk = chunk[['voxel', 'xcoord', 'ycoord', 'zcoord', 'val']]
        # 第一次写入用'w'模式,后续用追加模式
        mode = 'w' if i == 0 else 'a'
        chunk.to_hdf(output_file, key='voxels', mode=mode, format='table')
    
    return pd.read_hdf(output_file, key='voxels', mode='r')

# 使用示例
voxel_data = create_hdf_cache('your_dataset.txt')
# 按需提取单个voxel数据
target_row = voxel_data[voxel_data['voxel'] == 100]
x, y, z, val = target_row[['xcoord', 'ycoord', 'zcoord', 'val']].values[0]
# 批量提取所有坐标和值数组(按需加载,不会占满内存)
coords = voxel_data[['xcoord', 'ycoord', 'zcoord']].values
vals = voxel_data['val'].values

2. 用SQLite数据库存储

适合需要复杂查询的场景,同样支持增量写入和按需读取:

import sqlite3
import pandas as pd
import os

def create_sqlite_cache(file_path, output_file='datasets/cache.db'):
    if os.path.exists(output_file):
        conn = sqlite3.connect(output_file)
        return pd.read_sql('SELECT * FROM voxels', conn, index_col='voxel')
    
    conn = sqlite3.connect(output_file)
    # 读取表头
    with open(file_path, 'r') as f:
        f.readline()
        columns = f.readline().strip().split()
    
    chunk_iter = pd.read_csv(
        file_path,
        delim_whitespace=True,
        skiprows=2,
        names=columns,
        chunksize=500000
    )
    
    for chunk in chunk_iter:
        chunk['val'] = chunk['val1'] + 2 * chunk['val2']
        chunk = chunk[['voxel', 'xcoord', 'ycoord', 'zcoord', 'val']]
        # 追加写入数据库
        chunk.to_sql('voxels', conn, if_exists='append', index=False)
    
    # 创建索引加速voxel查询
    conn.execute('CREATE INDEX idx_voxel ON voxels(voxel)')
    conn.close()
    
    conn = sqlite3.connect(output_file)
    return pd.read_sql('SELECT * FROM voxels', conn, index_col='voxel')

3. 优化字典存储逻辑(应急方案)

如果必须用字典,需避免内存累积,采用逐行JSON写入(读取时按需加载):

import json
import os
import pandas as pd
from tqdm import tqdm

def create_grid_dict(file_path, chunk_size=500000):
    output_file = 'datasets/cache.json'
    if os.path.exists(output_file):
        return output_file
    
    with open(file_path, 'r') as f:
        f.readline()
        columns = f.readline().strip().split()
    
    file_size = os.path.getsize(file_path)
    # 逐行写入JSON对象,避免内存累积
    with open(output_file, 'w') as out_f, tqdm(total=file_size, unit='B', unit_scale=True, desc="Processing") as pbar:
        for chunk in pd.read_csv(
            file_path,
            delim_whitespace=True,
            skiprows=2,
            names=columns,
            chunksize=chunk_size
        ):
            chunk['val'] = chunk['val1'] + 2 * chunk['val2']
            chunk = chunk[['voxel', 'xcoord', 'ycoord', 'zcoord', 'val']]
            for _, row in chunk.iterrows():
                json.dump({int(row['voxel']): {
                    'xcoord': row['xcoord'],
                    'ycoord': row['ycoord'],
                    'zcoord': row['zcoord'],
                    'val': row['val']
                }}, out_f)
                out_f.write('\n')
            pbar.update(chunk_size)
    
    return output_file

# 按需读取指定voxel数据
def get_voxel_data(cache_file, voxel_id):
    with open(cache_file, 'r') as f:
        for line in f:
            data = json.loads(line)
            if voxel_id in data:
                return data[voxel_id]
    return None

内容的提问来源于stack exchange,提问作者Zyzyx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:24:54