如何用Python为超大numpy memmap数组绘制箱线图与直方图?
解决超大numpy memmap数组绘制箱线图的内存不足问题
针对379亿条uint8类型的memmap数组绘制箱线图时的内存溢出问题,核心思路是避免加载全量数据,只计算箱线图所需的关键统计量(最小值、Q1、中位数、Q3、最大值),再手动绘制箱线图。以下是几种可行的实现方法:
方法1:基于值频率统计的精确计算(针对uint8最优)
因为你的数据是uint8类型,取值范围固定为0-255,仅256个可能值,可通过分块统计每个值的出现次数,从频率分布推导统计量,内存占用极小:
初始化计数数组并加载memmap:
import numpy as np count = np.zeros(256, dtype=np.uint64) # 用uint64避免计数溢出 data = np.memmap('your_data_file', dtype=np.uint8, mode='r', shape=(37906895000,))分块遍历数组,累加数值计数:
block_size = 10**8 # 每次读取1亿条,可根据内存调整大小 for i in range(0, len(data), block_size): block = data[i:i+block_size] count += np.bincount(block, minlength=256)从计数数组计算箱线图统计量:
total = count.sum() cumulative = count.cumsum() # 计算最小值、最大值 min_val = np.argmax(count > 0) max_val = 255 - np.argmax(count[::-1] > 0) # 计算Q1、中位数、Q3对应的位置及数值 q1_pos = total * 0.25 median_pos = total * 0.5 q3_pos = total * 0.75 q1_val = np.argmax(cumulative >= q1_pos) median_val = np.argmax(cumulative >= median_pos) q3_val = np.argmax(cumulative >= q3_pos)手动绘制箱线图:
import matplotlib.pyplot as plt stats = [min_val, q1_val, median_val, q3_val, max_val] box = plt.boxplot([stats], patch_artist=True) plt.title('Camera Sensor Data Boxplot') plt.show()
方法2:分块近似计算分位数(通用任意数据类型)
如果处理其他类型的超大数组,可通过分块计算每个子块的统计量,再合并得到整体近似统计量:
分块计算子块统计量:
import numpy as np data = np.memmap('your_data_file', dtype=np.uint8, mode='r', shape=(37906895000,)) block_size = 10**8 block_stats = [] for i in range(0, len(data), block_size): block = data[i:i+block_size] qs = np.percentile(block, [0, 25, 50, 75, 100]) block_stats.append(qs)合并子块统计量得到整体结果:
block_stats = np.array(block_stats) overall_stats = np.percentile(block_stats, [0, 25, 50, 75, 100], axis=0)绘制箱线图:
import matplotlib.pyplot as plt plt.boxplot([overall_stats.tolist()]) plt.title('Approximate Camera Sensor Data Boxplot') plt.show()
分块越小精度越高,可根据需求调整block_size。
方法3:用Dask自动处理分块(适合复杂分析场景)
如果后续有更多超大数组分析需求,可使用Dask库自动管理分块和内存:
安装Dask:
pip install dask[array]加载数据并计算统计量:
import dask.array as da import matplotlib.pyplot as plt dask_data = da.from_array( np.memmap('your_data_file', dtype=np.uint8, mode='r', shape=(37906895000,)), chunks=10**8 ) qs = da.percentile(dask_data, [0, 25, 50, 75, 100]).compute()绘制箱线图:
plt.boxplot([qs.tolist()]) plt.title('Camera Sensor Data Boxplot (Dask)') plt.show()
Dask会后台自动分块计算,无需手动处理分块逻辑。
内容的提问来源于stack exchange,提问作者Vasily Lavrentyev
相关产品推荐
相关产品推荐

