In the following example, we would like to compare observation data with several simulation results, and the Taylor Diagram will be shown as:
% Compute statistics for Taylor Diagram
Data = [obs mod]; % obs should be a vector, mod can be a vector or matrix but with same length of obs on the 2nd dimension
STATS = zeros(4,size(Data,2));
for ii = 1:size(Data,2)
STATS(:,ii) = SStats(Data(:,1),Data(:,ii)); % SStats is a function, which is attached below. It will return [MEAN, STD, RMSD, CORR]
end
STATS(1,:) = [];
%STATS = SStats(obs_matched, mod_matched);
%STATS(1) = []; % Remove mean value, as it's not needed for Taylor Diagram
% Create Taylor Diagram Axes
figure('Units', 'normalized', 'Position', [.2, .1, .22, .32]);
TD = STaylorDiag(STATS); % STaylorDiag is a classdef by Zhaoxu Liu, attached below.
% Plot Model Data on Taylor Diagram
% STATS(1) RMSE
% STATS(2) std
% STATS(3) correlation
for ii = 1:size(Data,2)
% plot marker
TD.SPlot(STATS(1,ii),STATS(2,ii),STATS(3,ii),'Marker','o','MarkerSize',10,...
'Color','k','MarkerFaceColor','r');
% add text
if ii==1
TD.SText(STATS(1,1), STATS(2,1)+5, STATS(3,1)-0.003, ' Observation', ...
'FontWeight', 'bold', 'FontSize', 14, ...
'FontName', 'Times New Roman');
else
TD.SText(STATS(1,ii), STATS(2,ii), STATS(3,ii), [' Run',num2str(name_num(ii-1))], ...
'FontWeight', 'bold', 'FontSize', 14, ...
'FontName', 'Times New Roman', ...
'VerticalAlignment', 'middle', 'HorizontalAlignment', 'left');
end
end
% Set other properties
%TD.set('TickLength',[.015,.05])
% if (i==1)
% TD.set('SLim',[0,100])
% TD.set('RLim',[0,100])
% TD.set('STickValues',0:20:100)
% TD.set('SMinorTickValues',0:20:100)
% TD.set('RTickValues',0:20:90)
% %TD.set('CTickValues',[.1,.2,.3,.4,.5,.6,.7,.8,.9,.95,.99])
% elseif (i==2)
% TD.set('SLim',[0,30])
% TD.set('RLim',[0,30])
% TD.set('STickValues',0:5:30)
% TD.set('SMinorTickValues',0:5:30)
% TD.set('RTickValues',0:5:30)
% elseif (i==3)
% TD.set('SLim',[0,300])
% TD.set('RLim',[0,300])
% TD.set('STickValues',0:5:300)
% TD.set('SMinorTickValues',0:5:300)
% TD.set('RTickValues',0:5:300)
% end
% Set Grid
TD.set('SGrid','Color',[.7,.7,.7],'LineWidth',1.5)
TD.set('RGrid','Color',[.77,.6,.18],'LineWidth',1.5)
TD.set('CGrid','Color',[0,0,.8],'LineStyle',':','LineWidth',.8);
% Set Tick Label
TD.set('STickLabelX','Color',[.8,0,0],'FontWeight','bold')
TD.set('STickLabelY','Color',[.8,0,0],'FontWeight','bold')
TD.set('RTickLabel','Color',[.77,.6,.18],'FontWeight','bold')
TD.set('CTickLabel','Color',[0,0,.8],'FontWeight','bold')
% Set Label
TD.set('SLabel','Color',[.8,0,0],'FontWeight','bold')
TD.set('CLabel','Color',[0,0,.8],'FontWeight','bold')
% Set Axis
TD.set('SAxis','Color',[.8,0,0],'LineWidth',2)
TD.set('CAxis','Color',[0,0,.8],'LineWidth',2)
% Set Tick and MinorTick
TD.set('STick','Color',[.8,0,0],'LineWidth',8)
TD.set('CTick','Color',[0,0,.8],'LineWidth',8)
TD.set('SMinorTick','Color',[.8,0,0],'LineWidth',5)
TD.set('CMinorTick','Color',[0,0,.8],'LineWidth',5)
title(['CHL-a'], 'FontSize', fs);
% ft=40;
% set(gca, 'FontSize', ft); % set axis font size
% set(findall(gcf, '-property', 'FontSize'), 'FontSize', ft); % set text font size
saveas(gcf,['fig_leem_P2_Taylor_CHL.png'],'png');
function Data=SStats(Cr,Cf)
% Copyright (c) 2023, Zhaoxu Liu / slandarer
Cr = Cr(:);
Cf = Cf(:);
nanInd = isnan(Cr)|isnan(Cf);
Cr(nanInd) = [];
Cf(nanInd) = [];
% N = length(Cf);
MEAN = mean(Cf);
STD = std(Cf,1);
RMSD = std(Cf-Cr,1);
COR = corrcoef(Cf,Cr);
Data = [MEAN, STD, RMSD, COR(1,2)].';
%% calculation formula of STD RMSD and COR
% N = length(Cf);
%
% / sum[ {C-mean(C)} .^2] \
% STD = sqrt| --------------------- |
% \ N /
%
% Equivalent calculation formula:
% STD = sqrt(sum((Cf-mean(Cf)).^2)./N);
% STD = norm(Cf-mean(Cf))./sqrt(N);
% STD = rms(Cf-mean(Cf));
% STD = sqrt(var(Cf,1));
% STD = std(Cf,1);
% / sum[ { [C-mean(C)] - [Cr-mean(Cr)] }.^2 ] \
% RMSD = sqrt| ------------------------------------------- |
% \ N /
%
% Equivalent calculation formula:
% RMSD = sqrt(sum((Cf-mean(Cf)-(Cr-mean(Cr))).^2)./N);
% RMSD = norm(Cf-mean(Cf)-Cr+mean(Cr))./sqrt(N);
% RMSD = rmse(Cr-mean(Cr),Cf-mean(Cf));
% RMSD = rms(Cf-mean(Cf)-Cr+mean(Cr));
% RMSD = sqrt(var(Cf-Cr,1));
% RMSD = std(Cf-Cr,1);
% sum( [C-mean(C)].*[Cr-mean(Cr)] )
% COR = ---------------------------------
% N*STD(C)*STD(Cr)
%
% Equivalent calculation formula:
% COR = sum((Cf-mean(Cf)).*(Cr-mean(Cr)))./N./std(Cf,1)./std(Cr,1);
% COR = (Cf-mean(Cf)).'*(Cr-mean(Cr))./N./std(Cf,1)./std(Cr,1);
% COR = [1,0]*cov(Cf,Cr,1)./std(Cf,1)./std(Cr,1)*[0;1];
% COR = [1,0]*corrcoef(Cf,Cr)*[0;1];
end
classdef STaylorDiag < handle
% Copyright (c) 2023, Zhaoxu Liu / slandarer
properties
STATS; % STD RMSD and COR
ax; TickLength=[.015,.008];
% S-Axis means STD Axis
SAxis; RAxis; CAxis;
SGrid; RGrid; CGrid;
SLabel; RLabel; CLabel;
STickLabelX; STickLabelY; RTickLabel; CTickLabel;
% STD Tick and MinorTick
STick; SMinorTick; STickValues; SMinorTickValues; SLim;
%
RTickValues; RLim;
%
CTick; CMinorTick; CLim = [0,1];
CTickValues=[.1,.3,.5,.7,.9,.95,.99];
CMinorTickValues=[.05,.15,.2,.25,.35,.4,.45,.55,.6,.65,.75,.8,.85,.91,.92,.93,.94,.96,.97,.98,1];
end
methods
function obj = STaylorDiag(varargin)
if isa(varargin{1},'matlab.graphics.axis.Axes')
obj.ax = varargin{1}; varargin(1) = [];
else
obj.ax = gca;
end
obj.STATS = varargin{1};
obj.ax.Parent.Color = [1,1,1];
obj.ax.NextPlot = 'add';
obj.ax.XGrid = 'off';
obj.ax.YGrid = 'off';
obj.ax.Box = 'off';
obj.ax.DataAspectRatio = [1,1,1];
% -------------------------------------------------------------
obj.SLim = [0,max(obj.STATS(1,:)).*1.15];
obj.ax.XLim = obj.SLim;
obj.STickValues = obj.ax.XTick;
obj.RLim = [0,max(obj.STATS(2,:)).*1.15];
obj.ax.XLim = obj.RLim;
obj.RTickValues = obj.ax.XTick;
obj.RTickValues(1) = [];
obj.ax.XLim = obj.SLim; obj.ax.YLim = obj.SLim;
obj.SMinorTickValues = ...
linspace(obj.STickValues(1),obj.STickValues(end),(length(obj.STickValues)-1).*5+1);
obj.SMinorTickValues=setdiff(obj.SMinorTickValues,obj.STickValues);
obj.CMinorTickValues=setdiff(obj.CMinorTickValues,obj.CTickValues);
% -------------------------------------------------------------
% STD Tick, MinorTick, Grid, TickLabel
[tYTickX,tYTickY,tYTickMX,tYTickMY,tRGridX,tRGridY] = obj.getSValue();
obj.SGrid = plot(obj.ax,tRGridX(:),tRGridY(:),'LineWidth',.5,'Color','k');
obj.SAxis = plot(obj.ax,[0,0,obj.SLim(2)],[obj.SLim(2),0,0],'LineWidth',1.2,'Color','k');
obj.STick = plot(obj.ax,[tYTickX(:);tYTickY(:)],[tYTickY(:);tYTickX(:)],'LineWidth',.8,'Color','k');
obj.SMinorTick = plot(obj.ax,[tYTickMX(:);tYTickMY(:)],[tYTickMY(:);tYTickMX(:)],'LineWidth',.8,'Color','k');
obj.STickLabelX = text(obj.ax,obj.STickValues,0.*obj.STickValues,string(obj.STickValues),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','top','HorizontalAlignment','center');
obj.STickLabelY = text(obj.ax,0.*obj.STickValues(2:end),obj.STickValues(2:end),string(obj.STickValues(2:end))+" ",...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','middle','HorizontalAlignment','right');
obj.SLabel = text(obj.ax,obj.ax.YLabel.Position(1),obj.ax.YLabel.Position(2),'Standard Deviation',...
'FontName','Times New Roman','FontSize',20,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',90);
obj.SGrid.Annotation.LegendInformation.IconDisplayStyle='off';
obj.SAxis.Annotation.LegendInformation.IconDisplayStyle='off';
obj.STick.Annotation.LegendInformation.IconDisplayStyle='off';
obj.SMinorTick.Annotation.LegendInformation.IconDisplayStyle='off';
% -------------------------------------------------------------
% RMSD Grid TickLabel
[tR2GridX,tR2GridY] = obj.getRValue();
obj.RGrid = plot(obj.ax,tR2GridX(:),tR2GridY(:),'LineWidth',.5,'Color','k','LineStyle','--');
obj.RTickLabel = text(obj.ax,cos(pi*4.5/6).*obj.RTickValues+obj.STATS(1,1),sin(pi*4.5/6).*obj.RTickValues,string(obj.RTickValues),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',60);
obj.RGrid.Annotation.LegendInformation.IconDisplayStyle='off';
% -------------------------------------------------------------
% RMSD^2 = STD_r^2 + STD_f^2 - 2*STD_r*STD_f*COR
%
% STD_r^2 + STD_f^2 - RMSD^2
% COR = ----------------------------
% 2*STD_r*STD_f
%
[tCAxisX,tCAxisY,tTRMSD,tCTickX,tCTickY,tCTickMX,tCTickMY,tRMSDGridX,tRMSDGridY]=obj.getCValue();
obj.CGrid = plot(obj.ax,tRMSDGridX(:),tRMSDGridY(:),'LineWidth',.5,'Color',[.8,.8,.8],'LineStyle','-');
obj.CAxis = plot(obj.ax,tCAxisX,tCAxisY,'LineWidth',1.2,'Color','k');
obj.CTick = plot(obj.ax,tCTickX(:),tCTickY(:),'LineWidth',.8,'Color','k');
obj.CMinorTick = plot(obj.ax,tCTickMX(:),tCTickMY(:),'LineWidth',.8,'Color','k');
for i=1:length(tTRMSD)
obj.CTickLabel{i} = text(obj.ax,cos(tTRMSD(i)).*obj.SLim(2),sin(tTRMSD(i)).*obj.SLim(2)," "+string(obj.CTickValues(i)),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','middle','HorizontalAlignment','left','Rotation',tTRMSD(i)./pi.*2.*90);
end
obj.CLabel = text(obj.ax,cos(pi/4).*(obj.SLim(2)-obj.ax.YLabel.Position(1)),...
sin(pi/4).*(obj.SLim(2)-obj.ax.YLabel.Position(1)),'Correlation',...
'FontName','Times New Roman','FontSize',20,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',-45);
obj.CGrid.Annotation.LegendInformation.IconDisplayStyle='off';
obj.CAxis.Annotation.LegendInformation.IconDisplayStyle='off';
obj.CTick.Annotation.LegendInformation.IconDisplayStyle='off';
obj.CMinorTick.Annotation.LegendInformation.IconDisplayStyle='off';
% -------------------------------------------------------------
obj.ax.XColor = 'none';
obj.ax.YColor = 'none';
end
% =================================================================
function [tYTickX,tYTickY,tYTickMX,tYTickMY,tRGridX,tRGridY]=getSValue(obj)
tTPi_2 = linspace(0,pi/2,200).';
tYTickX = [0.*obj.STickValues;obj.STickValues.*0+obj.TickLength(1).*obj.SLim(2);nan.*obj.STickValues];
tYTickY = [obj.STickValues;obj.STickValues;nan.*obj.STickValues];
tYTickMX = [0.*obj.SMinorTickValues;obj.SMinorTickValues.*0+obj.TickLength(2).*obj.SLim(2);nan.*obj.SMinorTickValues];
tYTickMY = [obj.SMinorTickValues;obj.SMinorTickValues;nan.*obj.SMinorTickValues];
tRGridX = [obj.STickValues.*cos(repmat(tTPi_2,[1,length(obj.STickValues)]));nan.*obj.STickValues];
tRGridY = [obj.STickValues.*sin(repmat(tTPi_2,[1,length(obj.STickValues)]));nan.*obj.STickValues];
end
function [tR2GridX,tR2GridY]=getRValue(obj)
tTPi = linspace(0,pi,200).';
tR2GridX = [obj.RTickValues.*cos(repmat(tTPi,[1,length(obj.RTickValues)]));nan.*obj.RTickValues]+obj.STATS(1,1);
tR2GridY = [obj.RTickValues.*sin(repmat(tTPi,[1,length(obj.RTickValues)]));nan.*obj.RTickValues];
tR2GridN = sqrt(tR2GridX.^2+tR2GridY.^2)>obj.SLim(2);
tR2GridX(tR2GridN) = nan; tR2GridY(tR2GridN) = nan;
end
function [tCAxisX,tCAxisY,tTRMSD,tCTickX,tCTickY,tCTickMX,tCTickMY,tRMSDGridX,tRMSDGridY]=getCValue(obj)
tTPi_2 = linspace(0,pi/2,200).';
tCAxisX = cos(tTPi_2).*obj.SLim(2);
tCAxisY = sin(tTPi_2).*obj.SLim(2);
tRMSD = sqrt(2.*(obj.STATS(1,1).^2)-2.*(obj.STATS(1,1).^2).*obj.CTickValues);
tTRMSD = asin(tRMSD./2./obj.STATS(1,1)).*2;
tCTickX = [cos(tTRMSD).*obj.SLim(2);cos(tTRMSD).*obj.SLim(2).*(1-obj.TickLength(1));tTRMSD.*nan];
tCTickY = [sin(tTRMSD).*obj.SLim(2);sin(tTRMSD).*obj.SLim(2).*(1-obj.TickLength(1));tTRMSD.*nan];
tRMSDM = sqrt(2.*(obj.STATS(1,1).^2)-2.*(obj.STATS(1,1).^2).*obj.CMinorTickValues);
tTRMSDM = asin(tRMSDM./2./obj.STATS(1,1)).*2;
tCTickMX = [cos(tTRMSDM).*obj.SLim(2);cos(tTRMSDM).*obj.SLim(2).*(1-obj.TickLength(2));tTRMSDM.*nan];
tCTickMY = [sin(tTRMSDM).*obj.SLim(2);sin(tTRMSDM).*obj.SLim(2).*(1-obj.TickLength(2));tTRMSDM.*nan];
tRMSDGridX = [cos(tTRMSD).*obj.SLim(2);0.*tTRMSD;nan.*tTRMSD];
tRMSDGridY = [sin(tTRMSD).*obj.SLim(2);0.*tTRMSD;nan.*tTRMSD];
end
% =================================================================
function pltHdl=SPlot(obj,STD,~,COR,varargin)
tTheta = acos(COR);
pltHdl = plot(obj.ax,cos(tTheta).*STD,sin(tTheta).*STD,varargin{:},'LineStyle','none');
end
function txtHdl=SText(obj,STD,~,COR,varargin)
tTheta = acos(COR);
txtHdl = text(obj.ax,cos(tTheta).*STD,sin(tTheta).*STD,varargin{:});
end
% =================================================================
function set(obj,target,varargin)
if isa(varargin{1},'char')||isa(varargin{1},'string')
switch target
case {'SAxis','CAxis','SLabel','CLabel','STick','CTick','SMinorTick','CMinorTick','RTickLabel','SGrid','RGrid','CGrid'}
set(obj.(target),varargin{:});
case 'STickLabelX'
set(obj.STickLabelX,varargin{:});
case 'STickLabelY'
set(obj.STickLabelY,varargin{:});
case 'CTickLabel'
for i=1:length(obj.CTickLabel)
set(obj.CTickLabel{i},varargin{:});
end
end
else
oriRLim = obj.RLim;
obj.(target) = varargin{1};
obj.ax.XColor = 'k';
obj.ax.YColor = 'k';
if abs(obj.SLim(2)-obj.ax.XLim(2))>eps||abs(obj.RLim(2)-oriRLim(2))>eps
obj.ax.XLim = obj.SLim;
obj.STickValues = obj.ax.XTick;
obj.ax.XLim = obj.RLim;
obj.RTickValues = obj.ax.XTick;
obj.RTickValues(1) = [];
obj.ax.XLim = obj.SLim; obj.ax.YLim = obj.SLim;
obj.SMinorTickValues = ...
linspace(obj.STickValues(1),obj.STickValues(end),(length(obj.STickValues)-1).*5+1);
end
obj.(target) = varargin{1};
obj.SMinorTickValues=setdiff(obj.SMinorTickValues,obj.STickValues);
obj.CMinorTickValues=setdiff(obj.CMinorTickValues,obj.CTickValues);
%
[tYTickX,tYTickY,tYTickMX,tYTickMY,tRGridX,tRGridY] = obj.getSValue();
set(obj.SGrid,'XData',tRGridX(:),'YData',tRGridY(:));
set(obj.SAxis,'XData',[0,0,obj.SLim(2)],'YData',[obj.SLim(2),0,0]);
set(obj.STick,'XData',[tYTickX(:);tYTickY(:)],'YData',[tYTickY(:);tYTickX(:)]);
set(obj.SMinorTick,'XData',[tYTickMX(:);tYTickMY(:)],'YData',[tYTickMY(:);tYTickMX(:)]);
delete(obj.STickLabelX);delete(obj.STickLabelY);delete(obj.SLabel)
obj.STickLabelX = text(obj.ax,obj.STickValues,0.*obj.STickValues,string(obj.STickValues),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','top','HorizontalAlignment','center');
obj.STickLabelY = text(obj.ax,0.*obj.STickValues(2:end),obj.STickValues(2:end),string(obj.STickValues(2:end))+" ",...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','middle','HorizontalAlignment','right');
obj.SLabel = text(obj.ax,obj.ax.YLabel.Position(1),obj.ax.YLabel.Position(2),'Standard Deviation',...
'FontName','Times New Roman','FontSize',20,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',90);
%
[tR2GridX,tR2GridY] = obj.getRValue();
set(obj.RGrid,'XData',tR2GridX(:),'YData',tR2GridY(:));
delete(obj.RTickLabel)
obj.RTickLabel = text(obj.ax,cos(pi*5/6).*obj.RTickValues+obj.STATS(1,1),sin(pi*5/6).*obj.RTickValues,string(obj.RTickValues),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',60);
%
[tCAxisX,tCAxisY,tTRMSD,tCTickX,tCTickY,tCTickMX,tCTickMY,tRMSDGridX,tRMSDGridY]=obj.getCValue();
set(obj.CGrid,'XData',tRMSDGridX(:),'YData',tRMSDGridY(:));
set(obj.CAxis,'XData',tCAxisX,'YData',tCAxisY);
set(obj.CTick,'XData',tCTickX(:),'YData',tCTickY(:));
set(obj.CMinorTick,'XData',tCTickMX(:),'YData',tCTickMY(:));
for i=length(obj.CTickLabel):-1:1
delete(obj.CTickLabel{i});
end
delete(obj.CLabel);
for i=1:length(tTRMSD)
obj.CTickLabel{i} = text(obj.ax,cos(tTRMSD(i)).*obj.SLim(2),sin(tTRMSD(i)).*obj.SLim(2)," "+string(obj.CTickValues(i)),...
'FontName','Times New Roman','FontSize',13,'VerticalAlignment','middle','HorizontalAlignment','left','Rotation',tTRMSD(i)./pi.*2.*90);
end
obj.CLabel = text(obj.ax,cos(pi/4).*(obj.SLim(2)-obj.ax.YLabel.Position(1)),...
sin(pi/4).*(obj.SLim(2)-obj.ax.YLabel.Position(1)),'Correlation',...
'FontName','Times New Roman','FontSize',20,'VerticalAlignment','bottom','HorizontalAlignment','center','Rotation',-45);
obj.ax.XColor = 'none';
obj.ax.YColor = 'none';
end
end
end
end