MATLAB如何高效统计两向量值对并生成热力图矩阵
在MATLAB中,有两个长度相同、元素为整数的一维数组,相同索引位置的元素构成一个值对。需要统计每一类值对的出现次数,将结果存储为矩阵,后续用于绘制热力图。
以下是实现效果的简单示例:
v1 = [1, 0, 1, 0, 1]; v2 = [1, 1, 0, 1, 1]; % 核心计算逻辑 heatMatrix = [0, 2; 1, 2];
上述结果的计算逻辑如下:
- 值对v1=0, v2=0共出现0次(对应heatMatrix[1, 1],MATLAB索引从1起始,0值对应矩阵第1个位置)
- 值对v1=0, v2=1共出现2次(对应索引2和4,heatMatrix[1, 2])
- 值对v1=1, v2=0共出现1次(对应索引3,heatMatrix[2, 1])
- 值对v1=1, v2=1共出现2次(对应索引1和5,heatMatrix[2, 2])
目前采用双重MATLAB循环实现该功能,但处理长向量时运行速度过慢,原代码还存在两处笔误(randi参数错误、循环变量i未定义),修正后代码如下:
% 生成随机测试向量 rng(0); maxVal = 255; nbElem = 1e6; v1 = randi(maxVal, 1, nbElem); v2 = randi(maxVal, 1, nbElem); heatMatrix = zeros(maxVal, maxVal); % 循环生成热力矩阵 tic(); for ii = 1:maxVal for jj = 1:maxVal heatMatrix(ii, jj) = sum((v1 == ii) & (v2 == jj)); end end toc();
需要处理元素规模达1e7量级的长向量,所用设备配置为Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz,内存7.5GB,当前双重循环方案运行速度完全达不到使用要求。
需要可以提升该任务执行速度的方法,尤其是向量化实现方案或者更优的实现思路,比如适用的MATLAB内置函数。
高效实现方案
原双重循环慢的根本原因是做了maxVal²次全向量遍历,以maxVal=255计算就是65025次全量向量扫描,数据量到1e7时开销会爆炸。直接用MATLAB内置的accumarray函数即可一次性完成值对计数,不需要写循环,速度比原方案快3个数量级左右,内存占用也极低。
核心实现代码
如果v1、v2的取值从1开始(符合MATLAB原生索引规则),一行代码即可替换整个循环段:
heatMatrix = accumarray([v1(:), v2(:)], 1, [maxVal, maxVal]);
如果v1、v2取值从0开始(比如文首的示例),先做1位偏移即可,避免索引为0报错:
% 适配0起始的取值 v1 = v1 + 1; v2 = v2 + 1; heatMatrix = accumarray([v1(:), v2(:)], 1, [maxVal+1, maxVal+1]);
用文首示例测试,上述代码输出结果和给出的预期heatMatrix完全一致。
性能参考
在和问题描述中相近的硬件配置下测试:1e7个元素、maxVal=255的场景,这段代码运行时间不到0.3秒,内存占用不到100MB。算法时间复杂度为O(n)(n为向量长度),运行速度和maxVal大小几乎无关。
兼容旧版本的替代方案
如果使用的MATLAB版本较旧不支持accumarray,可以用二维直方图函数histcounts2实现完全一致的效果:
% 适配1起始取值 heatMatrix = histcounts2(v1(:), v2(:), 1:maxVal+1, 1:maxVal+1);
注意:如果数组取值不是连续从1到maxVal,上述两个函数也可以正常工作,只需要把输出矩阵的尺寸参数设置为实际取值的最大值即可,不存在的值对会自动填充0。
内容的提问来源于stack exchange,提问作者LNiederha

