You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何增量绘制热力图?3000万×10规模数据内存优化方法咨询

增量绘制大规模热力图的内存优化方案

针对你这种3000万X维度、10个Y维度,且只能以(30000,10)小批次接收数据(无法存储全量数据)的场景,核心思路是先对X轴做空间聚合(因为屏幕分辨率根本无法展示3000万独立点),再增量更新聚合结果并刷新热力图。下面是具体实现方案:

关键前提:X轴的聚合必要性

3000万X点远超过普通屏幕的横向像素(一般最多几千),直接绘制每一个X点完全没有意义——肉眼根本无法区分相邻的点。所以第一步必须对X轴进行分箱(binning),把连续的X样本/数值合并为一个个可展示的“块”,大幅降低数据维度。

方案1:基于聚合统计的增量更新(推荐)

这种方式适合需要展示X轴上Y值的统计分布(比如均值、求和、频数)的场景,内存占用极低,且完全不需要存储全量数据。

步骤拆解:

  1. 确定聚合粒度
    假设我们把每M个X样本合并为一个X块(bin)。比如取M=1000,那么3000万样本会被压缩为30000000 // 1000 = 30000个X块,最终热力图的数据规模是(30000, 10),仅30万元素,内存完全没问题。

  2. 初始化聚合容器
    创建两个数组,分别存储每个X块的Y值累计和,以及每个X块的样本计数:

    import numpy as np
    import matplotlib.pyplot as plt
    
    # 配置参数
    total_x = 30_000_000  # 总X点数
    batch_size = 30_000    # 批次大小
    y_dim = 10             # Y轴维度
    bin_size = 1000        # 每个X块包含的样本数
    num_bins = total_x // bin_size + 1  # 总X块数
    
    # 初始化聚合数组(用float32/int32进一步节省内存)
    heat_sum = np.zeros((num_bins, y_dim), dtype=np.float32)
    bin_counts = np.zeros(num_bins, dtype=np.int32)
    
  3. 增量处理批次并更新热力图
    每次接收一个批次数据后,计算每个样本所属的X块,批量更新聚合数组,然后刷新热力图。用np.add.at可以避免循环每个样本,大幅提升效率:

    # 初始化热力图
    plt.figure(figsize=(12, 4))
    im = plt.imshow(heat_sum.T, aspect='auto', origin='lower', cmap='viridis')
    plt.colorbar()
    plt.xlabel('X Bins (each = {} samples)'.format(bin_size))
    plt.ylabel('Y Dimension')
    plt.title('Incremental Heatmap')
    
    # 模拟批次数据传入(实际替换成你的数据接收逻辑)
    for batch_idx in range(total_x // batch_size):
        # 生成模拟批次数据(替换成你的真实数据)
        batch_data = np.random.randn(batch_size, y_dim)
        
        # 计算当前批次的样本索引范围
        start_idx = batch_idx * batch_size
        end_idx = start_idx + batch_size
        
        # 计算每个样本对应的X块索引
        bin_indices = np.arange(start_idx, end_idx) // bin_size
        
        # 批量更新累计和与计数
        np.add.at(heat_sum, bin_indices, batch_data)
        np.add.at(bin_counts, bin_indices, 1)
        
        # 计算每个X块的均值(可替换为求和、最大值等你需要的统计量)
        valid_bins = bin_counts > 0
        heat_data = np.zeros_like(heat_sum)
        heat_data[valid_bins] = heat_sum[valid_bins] / bin_counts[valid_bins, np.newaxis]
        
        # 更新热力图数据(无需重新创建图像对象)
        im.set_data(heat_data.T)
        # 动态调整颜色范围(可选)
        im.set_clim(vmin=np.nanmin(heat_data), vmax=np.nanmax(heat_data))
        
        # 刷新图像(交互环境如Jupyter需加上plt.draw()或display(plt.gcf()))
        plt.draw()
        plt.pause(0.01)  # 可选,给图像留刷新时间
    

方案优势:

  • 内存占用极低:仅需存储聚合后的30万元素数组,完全不用担心溢出。
  • 计算效率高:用numpy批量操作替代循环,速度提升几个数量级。
  • 绘制高效:仅更新imshow的数据,不重建画布,避免冗余操作。

方案2:分块直接绘制(适合特殊场景)

如果你的需求是直接绘制每个X点的Y值(虽然视觉上无细节,但特殊场景可能需要),可以用pcolormesh分块绘制,每次只绘制当前批次的X范围,不存储历史数据:

import numpy as np
import matplotlib.pyplot as plt

total_x = 30_000_000
batch_size = 30_000
y_dim = 10

plt.figure(figsize=(12, 4))
plt.xlabel('X Index')
plt.ylabel('Y Dimension')
plt.title('Incremental Block Heatmap')

# 初始化坐标轴范围
plt.xlim(0, total_x)
plt.ylim(0, y_dim)

for batch_idx in range(total_x // batch_size):
    batch_data = np.random.randn(batch_size, y_dim)
    start_x = batch_idx * batch_size
    end_x = start_x + batch_size
    
    # 创建当前批次的X、Y网格
    x = np.arange(start_x, end_x + 1)
    y = np.arange(y_dim + 1)
    xx, yy = np.meshgrid(x, y)
    
    # 绘制当前批次的热力块
    plt.pcolormesh(xx, yy, batch_data.T, cmap='viridis', edgecolors='none')
    
    # 刷新图像
    plt.draw()
    plt.pause(0.01)

注意事项:

  • 这种方式会在画布上持续叠加新块,后期绘制速度会变慢。
  • 3000万X点会被压缩成一条线,视觉上无法区分细节,因此仅推荐特殊场景使用。

额外优化建议

  • 若X轴是连续数值而非样本索引,可以用增量式二维直方图统计(基于np.histogram2d的分批更新),再绘制热力图。
  • 优先选择小数据类型:float32/int32float64/int64节省一半内存。
  • 非实时场景可每处理N个批次再更新一次热力图,进一步提升效率。

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

火山引擎 最新活动