MATLAB 雷达图画图函数

本人终于写了第一篇博客

因毕业设计画图需要,用MATLAB写了一个自动画雷达图的程序,可以画两种雷达图:多边形风格和圆风格的雷达图,有需要的朋友可以参考。

一、多边形风格雷达图

MATLAB 雷达图画图函数_第1张图片

画图的原理是,先一条一条画直线,然后再标注文字,然后填充图形。画直线用plot函数,标文字用text函数,填充图形用fill函数。这里有几个小技巧:

  1. 后画的内容会遮挡先画的内容,由于直线不便调透明, 所以先画直线再写字。
  2. 使用默认的对齐方式时,文字有时候会重叠在图上,不美观。所以需要根据文字的位置来设置合适的对其方式。
  3. 使用axes equal使横纵坐标相同,可以更方便地计算坐标。可以把图像中心定义为0,0点,然后用极坐标的形式去确定画图坐标。
  4. 为了使图窗大小正好是雷达图的大小,防止有些文字显示在图窗外面,需要读取各文字的extent属性,确定他们的坐标,然后调整绘图大小使得画图区足够容纳他们。

画图函数整合成draw_radar(data,lim,prefer_range,labels)函数,data是要画图的数据(根据数据的个数来确定雷达图的轴数),lim是各指标画图上下限范围,prefer_range是各指标参考上下限范围,labels是坐标轴名称。

function draw_radar(data,lim,prefer_range,labels)
    n=length(data);
    adj_data=zeros(n,1);
    point=zeros(n,2);
    adj_preferl=zeros(n,1);
    preferl_point=zeros(n,2);
    adj_preferu=zeros(n,1);
    preferu_point=zeros(n,2);
    
    set(gca,'units','normal','pos',[0 0 1 1]);
    axis off
    axis equal
    hold on
    theta_last=pi/2;
    for i=1:n
        theta=2*pi/n*i+pi/2;
        plot([0,500*cos(theta)],[0,500*sin(theta)],'k-','linewidth',2);
        for j=1:5
           plot([j*100*cos(theta_last),j*100*cos(theta)],[j*100*sin(theta_last),j*100*sin(theta)],'--','linewidth',0.75,'color',[0.5,0.5,0.5]);
        end
        
        theta_last=theta;
        if data(i)lim(i,2)
            adj_data(i)=500;
        else
            adj_data(i)=(data(i)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        end
        point(i,1:2)=[adj_data(i)*cos(theta);adj_data(i)*sin(theta)];
        adj_preferl(i)=(prefer_range(i,1)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        preferl_point(i,1:2)=[adj_preferl(i)*cos(theta);adj_preferl(i)*sin(theta)];
        adj_preferu(i)=(prefer_range(i,2)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        preferu_point(i,1:2)=[adj_preferu(i)*cos(theta);adj_preferu(i)*sin(theta)];
        text_around(510*cos(theta),510*sin(theta),labels{i},theta);
    end
    
    plot([preferl_point(:,1);preferl_point(1,1)],[preferl_point(:,2);preferl_point(1,2)],'b-','linewidth',1.5);
    plot([preferu_point(:,1);preferu_point(1,1)],[preferu_point(:,2);preferu_point(1,2)],'r-','linewidth',1.5);
    for i=1:n
        theta=2*pi/n*i+pi/2;
        for j=1:5
            text_around(j*100*cos(theta),j*100*sin(theta),num2str(lim(i,1)+(lim(i,2)-lim(i,1))/5*j),theta+pi/2,7);
        end
    end
    plot([point(:,1);point(1,1)],[point(:,2);point(1,2)],'k-','linewidth',0.2);
    fill(point(:,1),point(:,2),[0.9 0.9 0.7])
    alpha(0.5);
    texts=findobj(gca,'Type','Text');
    minx=-300;
    maxx=300;
    miny=-300;
    maxy=300;
    for i=1:length(texts)
        rect=get(texts(i),'Extent');
        x=rect(1);
        y=rect(2);
        dx=rect(3);
        dy=rect(4);
        if xmaxx
            maxx=x+dx;
        end
        if ymaxy
            maxy=y+dy;
        end
    end
    axis([minx-50,maxx+50,miny-20,maxy+20]);
end

function text_around(x,y,txt,theta,fontsize)
    if nargin==4
        fontsize=10;
    end
    section=mod(theta+pi/12,2*pi);
    if section>pi+pi/6
        %上对齐
        if section>1.5*pi+pi/6
            %左对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','left','Fontsize',fontsize);
        elseif section>1.5*pi
            %中对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','center','Fontsize',fontsize);
        else
            %右对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','right','Fontsize',fontsize);
        end
    elseif section>pi
        %中、右对齐
        text(x,y,txt,'VerticalAlignment','middle','HorizontalAlignment','right','Fontsize',fontsize);
    elseif section>pi/6
        %下对齐
        if section>0.5*pi+pi/6
            %右对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','right','Fontsize',fontsize);
        elseif section>0.5*pi
            %中对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','center','Fontsize',fontsize);
        else
            %左对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','left','Fontsize',fontsize);
        end
    else
        %中、左对齐
        text(x,y,txt,'VerticalAlignment','middle','HorizontalAlignment','left','Fontsize',fontsize);
    end
end

函数调用的例子:

prefer=[4,7;20,50;85,95;75,85;1.2,2.5;5,6];
limit=[2,10;10,70;70,110;65,95;0.5,4;3,9];
clf;
draw_radar([6,35,96,70,2,5],limit,prefer,...
    {'指标1/s','指标2/s','指标3/℃','指标4/℃','指标5/(g/s)','指标6/%'});

二、圆形风格雷达图

鉴于导师不太喜欢这种多边形的风格,又画了一种圆形风格的雷达图。当各指标都相等时,图像是一个完美的圆,否则会是一个凹凸不平的土豆(感觉比较丑,暂时没想到更好的办法,各位有更好的思路请留言~)。

MATLAB 雷达图画图函数_第2张图片

画图的原理和多边形图基本相同,但有两处不太一样:

  1. 为了让上下限范围变成一个圆,需要计算得到各坐标轴的刻度。
  2. 数据曲线采用三次样条插值来做。具体来说,是以0~2π为x,以数据点为y,进行插值。然后再将(x,y)换算成极坐标画在图上。为了使得插值曲线首尾能平滑连接,使用周期性边界条件来求解插值曲线。

画图函数整合成draw_radar_circle(data,prefer_range,labels)函数,data是要画图的数据(根据数据的个数来确定雷达图的轴数),prefer_range是各指标参考上下限范围,labels是坐标轴名称。

function draw_radar_circle(data,prefer_range,labels)
    n=length(data);
    lim=zeros(n,2);
    for j=1:n
        lim(j,1)=prefer_range(j,1)-(prefer_range(j,2)-prefer_range(j,1));
        lim(j,2)=prefer_range(j,2)+(prefer_range(j,2)-prefer_range(j,1));
    end
    adj_data=zeros(n,1);
    adj_preferl=zeros(n,1);
    preferl_point=zeros(n,2);
    adj_preferu=zeros(n,1);
    preferu_point=zeros(n,2);
    
    set(gca,'units','normal','pos',[0 0 1 1]);
    axis off
    axis equal
    hold on
    for i=1:5
        draw_circle(0,0,i*100,'--',[0.5,0.5,0.5],0.75);
    end
    for i=1:n
        theta=2*pi/n*i+pi/2;
        plot([0,500*cos(theta)],[0,500*sin(theta)],'k-','linewidth',2);
        if data(i)lim(i,2)
            adj_data(i)=500;
        else
            adj_data(i)=(data(i)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        end
        adj_preferl(i)=(prefer_range(i,1)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        preferl_point(i,1:2)=[adj_preferl(i)*cos(theta);adj_preferl(i)*sin(theta)];
        adj_preferu(i)=(prefer_range(i,2)-lim(i,1))/(lim(i,2)-lim(i,1))*500;
        preferu_point(i,1:2)=[adj_preferu(i)*cos(theta);adj_preferu(i)*sin(theta)];
        text_around(510*cos(theta),510*sin(theta),labels{i},theta);
    end
    draw_circle(0,0,500/3,'-',[0.2,0.2,0.9],1.5);
    draw_circle(0,0,500/3*2,'-',[0.9,0.2,0.2],1.5);
    for i=1:n
        theta=2*pi/n*i+pi/2;
        for j=1:5
            text_around(j*100*cos(theta),j*100*sin(theta),num2str(lim(i,1)+(lim(i,2)-lim(i,1))/5*j),theta+pi/2,7);
        end
    end
    y=[adj_data;adj_data(1)];
    x=[2*pi/n*(1:n)'+pi/2;2*pi/n*(n+1)+pi/2];
    m=length(x)*2-1;
    x_2=zeros(m,1);
    y_2=zeros(m,1);
    for i=1:m
        if mod(i,2)==0
            x_2(i)=(x(i/2)+x(i/2+1))/2;
            y_2(i)=(y(i/2)+y(i/2+1))/2;
        else
            x_2(i)=x((i+1)/2);
            y_2(i)=y((i+1)/2);
        end
    end
    xx=linspace(x_2(1),x_2(end),100);  
    yy=spline_period(x_2,y_2,xx);
    point=zeros(length(yy),2);
    for i=1:length(point)
        point(i,1:2)=[yy(i)*cos(xx(i));yy(i)*sin(xx(i))];
    end
    fill(point(:,1),point(:,2),[0.9 0.9 0.7])
    alpha(0.5);
    texts=findobj(gca,'Type','Text');
    minx=-500;
    maxx=500;
    miny=-500;
    maxy=500;
    for i=1:length(texts)
        rect=get(texts(i),'Extent');
        x=rect(1);
        y=rect(2);
        dx=rect(3);
        dy=rect(4);
        if xmaxx
            maxx=x+dx;
        end
        if ymaxy
            maxy=y+dy;
        end
    end
    axis([minx-50,maxx+50,miny-20,maxy+20]);
end

function [] = draw_circle(x,y,r,line_type,color,linewidth)  
    theta=0:0.01:2*pi;  
    circlex=x+r*cos(theta);  
    circley=y+r*sin(theta);  
    plot(circlex,circley,line_type,'color',color,'linewidth',linewidth);  
end  

function yy=spline_period(x,y,xx)
    n=length(x)-1; %n与书上的定义相同
    h=zeros(n,1);
    lambda=zeros(n,1);
    mu=zeros(n,1);
    g=zeros(n,1);
    for k=1:n        
        h(k)=x(k+1)-x(k);   %h(i)与书上的标号相同
    end
    for k=1:(n-1)                  %计算μ和λ
       mu(k)=h(k)/(h(k+1)+h(k));
       lambda(k)=1-mu(k);
       g(k)=6/(h(1+k)+h(k))*((y(k+2)-y(k+1))/h(k+1)-(y(k+1)-y(k))/h(k));
    end
    mu(n)=h(n)/(h(1)+h(n));
    lambda(n)=1-mu(n);
    g(n)=6/(h(1)+h(n))*((y(2)-y(1))/h(1)-(y(n+1)-y(n))/h(n));
    A=zeros(n,n);
    for k=1:(n-1)
        A(k,k)=2;
        A(k,k+1)=lambda(k);
        A(k+1,k)=mu(k+1);
    end
    A(n,n)=2;
    A(1,n)=mu(1);
    A(n,1)=lambda(n);
    [L,U,p] = lu(A,'vector');
    M = U\(L\(g(p,:)));
    M=[M(n);M];  %M0,M1,M2..Mn
    yy=zeros(size(xx));
    k=2;
    for i=1:length(xx)
        if xx(i)>x(k) && kpi+pi/6
        %上对齐
        if section>1.5*pi+pi/6
            %左对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','left','Fontsize',fontsize);
        elseif section>1.5*pi
            %中对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','center','Fontsize',fontsize);
        else
            %右对齐
            text(x,y,txt,'VerticalAlignment','cap','HorizontalAlignment','right','Fontsize',fontsize);
        end
    elseif section>pi
        %中、右对齐
        text(x,y,txt,'VerticalAlignment','middle','HorizontalAlignment','right','Fontsize',fontsize);
    elseif section>pi/6
        %下对齐
        if section>0.5*pi+pi/6
            %右对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','right','Fontsize',fontsize);
        elseif section>0.5*pi
            %中对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','center','Fontsize',fontsize);
        else
            %左对齐
            text(x,y,txt,'VerticalAlignment','bottom','HorizontalAlignment','left','Fontsize',fontsize);
        end
    else
        %中、左对齐
        text(x,y,txt,'VerticalAlignment','middle','HorizontalAlignment','left','Fontsize',fontsize);
    end
end

函数调用的例子:

prefer=[4,7;20,50;85,95;75,85;1.2,2.5;5,6];
clf;
draw_radar_circle([6,35,96,70,2,5],prefer,...
    {'指标1/s','指标2/s','指标3/℃','指标4/℃','指标5/(g/s)','指标6/%'});

三、其他轴数时的效果

当图像为5个坐标轴时,效果如下,用一个完美的雷达图来收尾。

多边形风格:

MATLAB 雷达图画图函数_第3张图片

圆风格:

MATLAB 雷达图画图函数_第4张图片


你可能感兴趣的:(MATLAB 雷达图画图函数)