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,替换成你的数据,调整参数,看看属于你的「数据山脉」如何崛起。记得在遇到问题时回顾本文的「用户数据替换指南」和「常见问题」,相信你一定能绘制出令人惊叹的山脊图!​

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

相关推荐
我爱C编程10 小时前
基于Stackelberg博弈论的频谱共享调度算法matlab仿真,对比Bertrand博弈论
matlab·stackelberg博弈·bertrand博弈·频谱共享调度
鱼会上树cy16 小时前
空间解析几何10:三维圆弧拟合【附MATLAB代码】
开发语言·matlab
机器学习之心2 天前
光伏功率预测 | RF随机森林多变量单步光伏功率预测(Matlab完整源码和数据)
算法·随机森林·matlab·多变量单步光伏功率预测
神仙别闹3 天前
基于 Matlab 和 Truetime 的网络控制系统仿真
网络·matlab·php
leo__5205 天前
matlab实现非线性Granger因果检验
人工智能·算法·matlab
IT猿手5 天前
动态多目标进化算法:基于迁移学习的动态多目标粒子群优化算法(TrMOPSO)求解IEEE CEC 2015,提供完整MATLAB代码
算法·matlab·迁移学习·动态多目标进化优化·动态多目标算法
jz_ddk5 天前
[学习] FIR多项滤波器的数学原理详解:从多相分解到高效实现(完整仿真代码)
学习·算法·matlab
IT猿手5 天前
动态多目标进化算法:基于迁移学习的动态多目标遗传算法Tr-NSGA-II求解CEC2015,提供完整MATLAB代码
人工智能·算法·机器学习·matlab·迁移学习·动态多目标进化算法·动态多目标优化算法
简简单单做算法6 天前
基于PSO粒子群优化的VMD-LSTM时间序列预测算法matlab仿真
算法·matlab·lstm·时间序列预测·pso·vmd-lstm·pso-vmd-lstm