MATLAB 山脊图绘制全解析:从数据生成到可视化进阶

一、引言:当数据分布拥有「层次感」------ 山脊图的魅力​

在数据可视化的世界里,我们常常需要同时展示多个分布的形态差异。传统的重叠密度图虽然能呈现整体趋势,但当分布数量较多时,曲线交叠会让画面变得杂乱。这时候,山脊图(Ridgeline Plot)就像一位优雅的舞者,用层次分明的「数据山脉」展现每个分布的独特轮廓,让复杂数据瞬间变得清晰易懂。​

今天,我们将通过一段完整的 MATLAB 代码,带大家从零开始实现一个精美的山脊图。不仅会详细解析每一行代码的作用,还会告诉你如何替换成自己的数据,甚至玩转可视化细节优化。准备好了吗?让我们一起走进这场「数据山脉」的绘制之旅吧!🌄​

二、代码全景:先睹为快的完整实现​

在正式解析前,先放上完整的 MATLAB 代码,方便大家有一个整体认知:

Matlab 复制代码
% 生成山脊图数据并绘图
rng(42); % 设置随机种子保证可重复性

% 参数设置
numSamples = 10;   % 样本数 (A-J)
numPoints = 500;   % 每个样本的数据点数
x = linspace(0, 90, numPoints); % X轴范围0-90
yOffset = 1.5;     % 样本间的垂直间距

% 生成随机均值和标准差 (控制分布位置和宽度)
mus = linspace(20, 70, numSamples);
sigmas = linspace(4, 12, numSamples) .* rand(1, numSamples);

% 预计算核密度并绘图
figure('Color', 'white');
hold on;

% 颜色映射 (从蓝到红)
colors = parula(numSamples);

for i = numSamples:-1:1  % 倒序绘制确保标签位置正确
    % 生成正态分布随机数据
    data = mus(i) + sigmas(i) * randn(numPoints, 1);
    
    % 计算核密度估计
    [f, xi] = ksdensity(data, x, 'Bandwidth', 3);
    f_normalized = f / max(f) * 0.8; % 归一化高度
    
    % 计算当前样本的基线
    baseline = (numSamples - i) * yOffset;
    
    % 绘制填充区域
    fill([xi, fliplr(xi)], ...
         [baseline + f_normalized, fliplr(baseline*ones(1, numPoints))], ...
         colors(i, :), ...
         'FaceAlpha', 0.7, ...
         'EdgeColor', 'none');
    
    % 添加样本标签
    text(5, baseline + 0.3, ['Sample ', char('A' + numSamples - i)], ...
         'FontWeight', 'bold', ...
         'FontSize', 10);
end

% 美化图形
set(gca, 'YTick', [], 'YColor', 'none'); % 隐藏Y轴
xlabel('K (w)');
title('Ridgeline Plot', 'FontSize', 14);
grid on;
box on;
xlim([0, 90]);
ylim([0, yOffset*numSamples + 0.5]);
hold off;

运行这段代码,你会得到一个包含 10 个样本分布的山脊图,每个「山脊」代表一个样本的密度分布,垂直排列的设计让每个分布的位置、宽度和峰值一目了然。接下来,我们逐行拆解其中的关键逻辑。​

三、核心代码解析:数据生成与可视化的「山脉构造」​

  1. 随机种子设置:让结果可复现的「魔法咒语」
Matlab 复制代码
rng(42); % 设置随机种子保证可重复性
  • 作用:rng函数用于设置随机数生成器的种子,这里使用42作为种子(一个在编程界充满神秘色彩的数字😉)。设置后,每次运行代码生成的随机数序列都相同,确保结果可复现,这对学术研究或协作场景非常重要。
  • 可替换点:如果不需要固定随机结果,直接删除这行即可;若想换一个固定结果,修改括号内的数字(如rng(123))。
  1. 参数设置:定义「山脉」的基本框架
Matlab 复制代码
numSamples = 10;   % 样本数 (A-J)
numPoints = 500;   % 每个样本的数据点数
x = linspace(0, 90, numSamples); % X轴范围0-90
yOffset = 1.5;     % 样本间的垂直间距
  • numSamples:控制样本数量,代码中生成了 A-J 共 10 个样本,对应 10 个「山脊」。如果你有 20 个样本,这里就设为 20。
  • numPoints:每个样本生成的数据点数,数值越大,密度曲线越平滑(但计算量也会增加),通常 500-1000 是不错的选择。
  • x:定义 X 轴的范围和采样点,这里用linspace(0, 90, 500)生成从 0 到 90 均匀分布的 500 个点,作为密度估计的横轴坐标。
  • yOffset:控制每个样本在 Y 轴方向的间距,数值越大,「山脊」之间的间隔越宽,避免重叠过多;数值越小,画面更紧凑。
  1. 分布参数生成:赋予每个「山脊」独特形态
Matlab 复制代码
mus = linspace(20, 70, numSamples);
sigmas = linspace(4, 12, numSamples) .* rand(1, numSamples);
  • 均值mus:使用linspace生成从 20 到 70 的等差数列,作为每个样本正态分布的均值。这样样本的中心位置会从左到右逐渐移动,形成明显的位置差异。
  • 标准差sigmas:基础范围是 4 到 12,再乘以一个随机数(rand(1, numSamples)生成 0-1 之间的随机数),让每个样本的分布宽度既有规律又有变化。这样有的「山脊」瘦高(标准差小),有的矮胖(标准差大),模拟真实数据的多样性。
  1. 图形初始化与颜色映射:搭建画布与调色盘
Matlab 复制代码
figure('Color', 'white'); % 创建白色背景的画布
hold on; % 保持图形,允许后续添加元素
colors = parula(numSamples); % 生成与样本数匹配的颜色映射(Parula色阶,MATLAB默认的优秀色阶)
  • 画布设置:figure('Color', 'white')创建一个白色背景的图形窗口,避免默认灰色背景干扰数据展示。
  • 颜色映射:parula是 MATLAB 推荐的感知均匀色阶,适合连续数据可视化。numSamples决定颜色数量,每个样本对应一种颜色,这里从蓝到红渐变,视觉上容易区分。
  1. 循环绘制:逐个构建「山脊」的核心逻辑
Matlab 复制代码
for i = numSamples:-1:1  % 倒序绘制确保标签位置正确

这里使用倒序循环(从 10 到 1),是因为后续绘制时,底层的「山脊」会被上层覆盖,倒序可以先画底层样本,再画上层,避免标签被遮挡(后面会详细解释标签位置)。​

5.1 生成正态分布数据

Matlab 复制代码
data = mus(i) + sigmas(i) * randn(numPoints, 1);
  • 利用正态分布公式均值 + 标准差×随机数生成数据,randn生成服从标准正态分布的随机数,numPoints, 1表示生成 500 行 1 列的列向量。

5.2 核密度估计:让离散数据「平滑成山」

Matlab 复制代码
[f, xi] = ksdensity(data, x, 'Bandwidth', 3);
  • ksdensity函数:用于计算核密度估计(Kernel Density Estimation, KDE),将离散数据转换为连续的密度曲线。
  • data:输入的样本数据(列向量)。
  • x:指定计算密度的横轴坐标(即前面定义的 0-90 的 500 个点)。
  • 'Bandwidth':带宽参数,控制曲线平滑度。数值越大,曲线越平滑;越小,越贴近原始数据波动。这里设为 3,是一个平衡值。
  • 输出f是密度估计值,xi是对应的横轴坐标(与x相同,这里主要是为了代码一致性)。

5.3 归一化处理:控制「山脊」高度

Matlab 复制代码
f_normalized = f / max(f) * 0.8; % 归一化高度
  • 为什么归一化?直接使用密度值绘制会导致不同样本的「山脊」高度差异过大(因为密度值与数据范围相关),归一化后将每个样本的密度峰值缩放到 0.8 倍的相对高度,让所有「山脊」在视觉上高度统一,便于比较形状而非绝对密度。

5.4 基线计算:确定「山脊」的垂直位置

Matlab 复制代码
baseline = (numSamples - i) * yOffset;
  • 第一个样本(i=10)的基线是(10-10)*1.5=0,第二个(i=9)是(10-9)*1.5=1.5,依此类推,最后一个样本(i=1)是(10-1)*1.5=13.5。这样每个样本从上到下依次排列,间距为 1.5。

5.5 绘制填充区域:用颜色填充「山体」

Matlab 复制代码
fill([xi, fliplr(xi)], ...
     [baseline + f_normalized, fliplr(baseline*ones(1, numPoints))], ...
     colors(i, :), ...
     'FaceAlpha', 0.7, ...
     'EdgeColor', 'none');
  • 填充原理:fill函数通过指定顶点坐标绘制多边形。这里将密度曲线的点(xi, baseline + f_normalized)与基线的反向点(fliplr(xi), baseline)连接,形成一个闭合区域(类似梯形 + 曲线的形状)。
  • fliplr(xi):将 xi 反转,用于闭合路径的底部横轴。
  • baseline*ones(1, numPoints):生成与 xi 等长的基线向量,反转后作为底部 Y 坐标,形成从曲线到基线的闭合区域。
  • 视觉参数:
  • FaceAlpha=0.7:设置填充颜色透明度,避免完全不透明导致下层「山脊」被遮挡。
  • EdgeColor='none':不绘制边框,让图形更简洁。

5.6 添加样本标签:给每座「山」命名

Matlab 复制代码
text(5, baseline + 0.3, ['Sample ', char('A' + numSamples - i)], ...
     'FontWeight', 'bold', ...
     'FontSize', 10);
  • 标签逻辑:利用倒序循环,当 i=10 时,numSamples - i=0,标签为 'A';i=9 时,numSamples - i=1,标签为 'B',依此类推,最终生成 A-J 的标签。
  • 位置设置:X 坐标固定为 5(位于画布左侧),Y 坐标为基线 + 0.3,确保标签显示在对应「山脊」的左侧空白处,不与图形重叠。

四、用户数据替换指南:让代码适配你的场景​

现在重点来了!如果你想将这段代码用于自己的数据,需要修改哪些部分呢?以下是详细的替换指南:​

  1. 替换原始数据:从随机生成到真实输入​

当前代码使用正态分布随机数据,如果你有真实数据,步骤如下:​

场景一:已知每个样本的原始数据​

  • 数据格式:假设你有 10 个样本,每个样本的数据存储为单元格数组data_samples,其中data_samples{1}是样本 A 的数据,data_samples{2}是样本 B 的数据,依此类推。
  • 修改代码:
  • 删除mus和sigmas的生成代码。
  • 在循环中,将data = mus(i) + sigmas(i) * randn(...)替换为data = data_samples{i};。
  • 确保每个样本的数据点数可以不同(但ksdensity会自动处理,无需统一长度)。

场景二:已知每个样本的均值和标准差​

  • 如果你只有均值和标准差(比如通过统计得到),直接替换mus和sigmas为你的数据:
Matlab 复制代码
mus = [25, 30, 35, 40, 45, 50, 55, 60, 65, 70]; % 自定义均值
sigmas = [5, 6, 7, 8, 9, 10, 9, 8, 7, 6]; % 自定义标准差
  1. 修改 X 轴范围和标签​
  • 当前 X 轴标签是 'K (w)',如果你是其他数据(如温度、时间、分数),直接修改xlabel内容:
Matlab 复制代码
xlabel('温度 (℃)'); % 示例:温度数据

X 轴范围x = linspace(0, 90, numPoints)可根据数据实际范围调整,比如数据在 10-80 之间,改为:

Matlab 复制代码
x = linspace(10, 80, 500);
  1. 调整样本数量和间距​
  • 增加 / 减少样本数:修改numSamples,比如设为 15,同时确保你的数据有 15 个样本。
  • 调整垂直间距:修改yOffset,数值越大,「山脊」之间越稀疏(建议 0.5-2 之间调整,根据样本数量选择:样本多则间距小,样本少可适当增大)。
  1. 自定义颜色和标签​
  • 颜色映射:默认使用parula色阶,可替换为其他色阶(如jet、hot、cool),或自定义颜色矩阵:
Matlab 复制代码
colors = [0 0 1; 0 0.5 1; 0 1 1; ...]; % 自定义RGB颜色,每行一个颜色

标签内容:当前标签是 'Sample A-J',可替换为自定义标签,比如样本名称数组:

Matlab 复制代码
labels = {'Group 1', 'Group 2', 'Group 3', ..., 'Group 10'}; % 定义标签数组
text(5, baseline + 0.3, labels{numSamples - i + 1}, ...); % 注意索引顺序

五、细节优化:让图形更专业美观​

  1. 坐标轴与网格设置
Matlab 复制代码
set(gca, 'YTick', [], 'YColor', 'none'); % 隐藏Y轴
grid on; % 显示网格线
box on; % 显示图形边框
xlim([0, 90]); % 固定X轴范围
ylim([0, yOffset*numSamples + 0.5]); % 自动计算Y轴范围
  • 隐藏 Y 轴:山脊图的 Y 轴通常不代表实际数据,只是垂直间距,所以隐藏 Y 轴刻度和标签,让焦点集中在 X 轴和分布形态上。
  • 网格与边框:网格线帮助观察 X 轴数值,边框让图形更规整。
  1. 标题与字体设置
Matlab 复制代码
title('Ridgeline Plot', 'FontSize', 14); % 主标题
% 可添加副标题:
text(0.5, 1.05, '多个样本分布对比', 'FontSize', 12, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'Units', 'normalized');
  • 使用text函数添加副标题,Units', 'normalized'让位置基于画布比例(0-1),HorizontalAlignment' 'center'使文字居中。
  1. 透明度与边缘处理
Matlab 复制代码
'FaceAlpha', 0.7, % 填充透明度
'EdgeColor', 'none', % 无边缘线
  • 透明度设置让下层「山脊」的轮廓隐约可见,增加层次感;去除边缘线避免画面杂乱。

六、应用场景:山脊图的「用武之地」​

山脊图因其独特的可视化效果,在多个领域都有出色表现:​

  • 生物学:展示不同物种的体长、体重分布差异。
  • 市场调研:对比不同地区的消费者年龄、消费金额分布。
  • 气象学:呈现不同月份的温度、降水量分布变化。
  • 教育学:分析不同班级的成绩分布形态(是否偏科、两极分化等)。
  • 金融学:可视化不同资产的收益率分布,识别风险差异。

举个例子:如果你是电商分析师,想对比 12 个月的用户购买金额分布,就可以用山脊图展示每个月的分布曲线,一眼看出哪些月份的购买金额均值更高、波动更大。​

七、常见问题与解决方案​

  1. 「山脊」重叠过多怎么办?​
  • 原因:yOffset太小或f_normalized的归一化系数太大(当前是 0.8,可减小到 0.6)。
  • 解决:增大yOffset(如从 1.5 改为 2),或减小归一化系数(如*0.6),降低「山脊」高度。
  1. 标签被图形遮挡怎么办?​
  • 原因:倒序循环时,标签位置计算错误,或baseline + 0.3的偏移量不够。
  • 解决:确保循环是倒序(numSamples:-1:1),并适当调整标签的 Y 坐标(如baseline + 0.5)。
  1. 颜色不够区分怎么办?​
  • 解决:使用colororder(colors)调整颜色顺序,或换用对比度更高的色阶(如lines色阶,但更适合少量样本)。

八、总结:动手打造你的专属数据山脉​

通过今天的分享,我们不仅掌握了山脊图的核心绘制逻辑,还学会了如何替换真实数据、调整可视化细节。从随机数据生成到核密度估计,从颜色映射到标签添加,每一步都是为了让数据分布以最清晰、美观的方式呈现。​

现在,轮到你动手实践啦!💻 打开 MATLAB,替换成你的数据,调整参数,看看属于你的「数据山脉」如何崛起。记得在遇到问题时回顾本文的「用户数据替换指南」和「常见问题」,相信你一定能绘制出令人惊叹的山脊图!​

如果觉得本文有用,欢迎分享给身边的数据分析小伙伴。下次我们将探讨更多高级可视化技巧,记得关注哦~ 祝你绘图愉快,数据可视化之路越走越顺!🚀

相关推荐
机器学习之心15 小时前
MATLAB基于改进云物元的模拟机协同训练质量评价
matlab·改进云物元
ytttr87316 小时前
MATLAB实现经验模态分解(EMD)与希尔伯特变换获取能量谱
人工智能·python·matlab
t1987512817 小时前
基于多假设跟踪(MHT)算法的MATLAB实现
开发语言·matlab
机器学习之心20 小时前
MATLAB多子种群混沌自适应哈里斯鹰算法优化BP神经网络回归预测
神经网络·算法·matlab
π同学1 天前
基于Matlab的递推最小二乘法参数估计
matlab·最小二乘法
小喵要摸鱼1 天前
【MATLBA】使用教程
matlab
listhi5202 天前
基于空时阵列最佳旋转角度的卫星导航抗干扰信号处理的完整MATLAB仿真
开发语言·matlab·信号处理
88号技师2 天前
2025年7月一区SCI优化算法-Logistic-Gauss Circle optimizer-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法
yugi9878382 天前
基于MATLAB的心电信号去噪
开发语言·matlab
gihigo19982 天前
MATLAB使用遗传算法解决车间资源分配动态调度问题
算法·matlab