分享一下“绳结图”的画法,附源代码

文摘   2024-07-24 08:00   北京  
点击上方“博硕科研绘图”,关注我的公众号。
设为“星标”,原创文章第一时间推送。
资源获取,技术援助请私信~


我们经常需要表示若干个变量之间的关系,例如,在一项人们购买健康食品的调查研究中,我们获得了以下参数:收入、年龄、体重指数、每月食品花费、锻炼频率、食品偏好指数

这6项参数的每2个参数之间存在相关关系,例如:

收入和每月食品花费:很可能存在正相关关系,即收入较高的人群可能更愿意花更多的钱购买食品。


年龄和体重指数:通常会看到这两者之间存在正相关关系,随着年龄增长,人的体重指数也可能增加。


体重指数和食品偏好指数:可能存在负相关关系,即体重指数较高的人可能偏好不太健康的食品。


这时候为了表示这些参数之间存在相关关系以及存在何种程度的相关关系,我们会想到用一种好看的图去表示。假设每两种参数之间都存在一种关系,那么6项参数之间共存在(5+4+3+2+1=15)种关系,这些关系可以用线条连接起来,并用不同的线条粗细表示相关程度的大小


15条关系线需要很好地处理它们的位置,以使整体看上去不杂乱且有序,我们想到了圆,那么,找到圆的六等分点作为6个参数的位置,以连接两点之间的弦为基础画一个弧形,这个弧形中点与弦的中点存在一个距离关系,称为矢高,这是控制弧形形状的参数。有了这些想法我们就可以在Matlab中实现了。


我们还是先构建一个函数,然后在脚本中调用这个函数,函数的功能就是:当我们输入参数种类的个数num_points,圆的直径D和矢高sagitta的时候,程序就会计算点和线的位置,并帮我们画出来。

function draw_arc_segments(num_points, D, sagitta)
% 定义圆心和直径 center = [0, 0]; r = D / 2; % 半径
% 生成圆周上的n等分点 theta = linspace(0, 2*pi, num_points+1); % 生成0到2pi之间等间隔的角度 x_points = center(1) + r * cos(theta(1:end-1)); y_points = center(2) + r * sin(theta(1:end-1));
% 绘制圆 figure; hold on; theta_circle = linspace(0, 2*pi, 100); x_circle = center(1) + r * cos(theta_circle); y_circle = center(2) + r * sin(theta_circle); plot(x_circle, y_circle, 'b', 'LineWidth', 2);
% 标记圆周上的6等分点 plot(x_points, y_points, 'ro', 'MarkerSize', 10, 'MarkerFaceColor', 'r');
% 连接每两个点作为弦,并绘制弧线% sagitta = 0.2; % 矢高 for i = 1:num_points for j = i+1:num_points % 连接弦的起点和终点% plot([x_points(i), x_points(j)], [y_points(i), y_points(j)], 'k--');
% 计算弧线的控制点 p1 = [x_points(i), y_points(i)]; p2 = [x_points(j), y_points(j)]; mid_point = (p1 + p2) / 2;
% 计算正交方向的单位向量 direction_vector = p2 - p1; perpendicular_vector = [-direction_vector(2), direction_vector(1)]; perpendicular_vector = perpendicular_vector / norm(perpendicular_vector);
% 计算弧线的中点控制点 control_point = mid_point + perpendicular_vector * sagitta;
% 用三次贝塞尔曲线绘制弧线 t = linspace(0, 1, 100); bezier_x = (1-t).^2 * p1(1) + 2*(1-t).*t * control_point(1) + t.^2 * p2(1); bezier_y = (1-t).^2 * p1(2) + 2*(1-t).*t * control_point(2) + t.^2 * p2(2); plot(bezier_x, bezier_y, 'k'); end end
axis equal; hold off;
end


我们在主程序中调用这个函数,调用格式为:

draw_arc_segments(6,5,0.5)

6代表我们总共有6个参数,5代表圆的直径为5,0.5代表矢高为0.5。下图便是程序运行的结果,已经基本符合我们的想法了。


接下来,我们选择编辑>复制图窗,粘贴到Visio软件中,取消组合,得到以下图形元素,每个线条都是可以单独编辑的。


理论上,两个点之间只有一条连线,而我们要区分不同的线束,就得定义由某点发射出去的线为某一种特定的颜色,所以,这里我们要实现一个小小的变动,假设两个参数之间的关系是存在先后顺序的,例如,收入的增加促进了每月食品花费而每月食品花费增加反而降低了总收入。所以,在原先的弧形基础上,我们还要再画出沿弦对称的弧形。因为本身图形是对称的,这些线条我们可以从图中直接复制,或者用铅笔工具直接绘制即可。如下图。


然后我们可以根据需求去给每个线条配色了6个参数选择6种不同的颜色,线也具有6种不同的颜色,粗细按照相关程度调整,如下图。


我们发现还是存在一些问题,那就是线条太多,分不清主次,没关系,我们还可以通过修改线条的透明度和层次来增加主次的视觉效果。相关性较弱的线条可以把透明度调高,相关性较强的线条透明度调低。下图这样的感觉就比上图要好很多。


然后我们给每个参数加上漂亮的绳结以及文字说明,就这样我们就可以得到一幅好看的关系网络图了。如果还有特殊需求,可以改变线条的线型来突出表达一些重要的数据,总之,Matlab的作用是构图,Visio的作用是配色和改进




如果你觉得我的分享对你有帮助的话,欢迎大家在这里点赞、在看、分享。当然,也欢迎大家在这里打赏。互动越多,更新越快哦~

声明:本公众号的所有原创内容,在未经允许的情况下,不得用于任何商业用途,违者必究。

博硕科研绘图
在读博士的科研经历,专注分享论文写作和科研画图的相关知识。
 最新文章