MATLAB | 如何绘制高颜值 pairplot (scatter plot matrix | 散点图矩阵)

封面

工具获取

由于工具可能会实时维护,推文内代码更新较为麻烦,因此建议从以下仓库获取完整代码和例子,文末提供工具函数完整代码(但该代码在代码维护后可能不会同步更新)。

gitee

github

fileexchange

数据格式

代码有单矩阵和双矩阵两种输入模式:单矩阵输入(X)是二维数值矩阵,S 行 N 列,其中每一行代表一个样本(sample),每一列代表一个属性,双矩阵输入则要求一个大小为 S 行 N 列的矩阵(X)与一个 S 行 M 列的矩阵(Y),

例如矩阵输入(X)可以长这样:

数据的分组 (Group) 是一个 S 行 1 列的列向量,可以是元胞数组或者数值数组。例如分组 (Group)可以长这样:

基本使用:单矩阵输入

以鸢尾花数据集为例:

matlab 复制代码
% Load Fisher's iris dataset (150 samples, 4 features, 3 species)
% 加载鸢尾花数据集(150个样本,4个特征,3个品种)
fisher = load("fisheriris.mat");
X = fisher.meas;            % 150×4 feature matrix / 特征矩阵
group = fisher.species;     % Species labels / 品种标签
label = {'sepal-length', 'sepal-width', 'petal-length', 'petal-width'};

% Create pairplot object with customized visualization
% 创建散点图矩阵对象,自定义可视化风格
SP = SPairplot(X, 'Group', group, 'DiagType', 12, 'UpperType', 11, 'LowerType', 13);
SP.draw();

% Set X-labels and Y-labels
% 设置 X 轴标签和 Y 轴标签
SP.setXLabelString(label);
SP.setYLabelString(label);

全部绘图样式

DiagType, BottomType and RightType

UpperType and LowerType

基本使用:双矩阵输入

双矩阵输入的时候,UpperType 和 DiagType 不起作用:

matlab 复制代码
% Generate synthetic data with three groups (150 samples, 6 features total)
% 生成包含三个分组的合成数据(150个样本,共6个特征)
P1 = mvnrnd([0,20,0,10,8,7], eye(6).*6, 50);
P2 = mvnrnd([10,5,15,0,4,2], eye(6).*8, 50);
P3 = mvnrnd([10,15,0,0,2,3], eye(6).*[3,9,2,1,4,4], 50);
G  = [ones(50,1)*1; ones(50,1)*2; ones(50,1)*3];  % Group labels / 分组标签

% Split into X (first 4 variables) and Y (last 2 variables)
% 拆分为 X(前4个变量)和 Y(后2个变量)
XY = [P1; P2; P3];
X = XY(:, 1:4);   
Y = XY(:, 5:6);   

% Create pairplot with two variable sets (X vs Y)
% 创建双变量集的散点图矩阵(X 与 Y 的对比)
% Note: When X and Y are both provided, only LowerType (off-diagonal below)
% is effective. Upper triangle and diagonal are automatically disabled.
% 注意:当同时输入 X 和 Y 时,仅 LowerType(下三角区域)生效,
% 上三角和对角线区域自动禁用。
SP = SPairplot(X, Y, 'Group', G, 'LowerType', 9);
SP.draw();

子图属性设置

使用函数:

  • obj.setAxes()
    设置子图属性
matlab 复制代码
fisher = load("fisheriris.mat");
X = fisher.meas;            
group = fisher.species;     
label = {'sepal-length', 'sepal-width', 'petal-length', 'petal-width'};

SP = SPairplot(X, 'Group', group, 'DiagType', 1, 'UpperType', 9, 'LowerType', 2);

% set CData
SP.CData = [119,69,133; 56,108,155; 33,160,122] ./ 255;
SP.draw();

% Set X-labels and Y-labels
SP.setXLabelString(label);
SP.setYLabelString(label);


% Customize subplot appearance
% 自定义子图外观
SP.setAxes('Box','off', 'LineWidth',1.5, 'XGrid','on', 'YGrid','on')
SP.setXLabel('FontWeight','bold', 'Color',[0,0,.8])
SP.setYLabel('FontWeight','bold', 'Color',[.8,0,0])

添加图例

matlab 复制代码
fisher = load("fisheriris.mat");
X = fisher.meas;            
group = fisher.species; 
label = {'sepal-length', 'sepal-width', 'petal-length', 'petal-width'};

SP = SPairplot(X, 'Group', group, 'DiagType', 10, 'UpperType', 9, 'LowerType', 2);
% set CData
SP.CData = [211, 43, 43;  61, 96,137; 249,206, 61;  76,103, 86;  80, 80, 80]./255;

SP.Padding = [.08, .08, .2, .04];
SP.draw();

% Set X-labels and Y-labels
SP.setXLabelString(label);
SP.setYLabelString(label);


legend(SP.axMat{4, 4}, SP.GroupLabel, 'FontSize',17, 'Position',[.82, .88, .12, .08])

单矩阵输入:添加边际图

matlab 复制代码
% Load Fisher's iris dataset (150 samples, 4 features, 3 species)
% 加载鸢尾花数据集(150个样本,4个特征,3个品种)
fisher = load("fisheriris.mat");
X = fisher.meas;            % 150×4 feature matrix / 特征矩阵
group = fisher.species;     % Species labels / 品种标签
label = {'sepal-length', 'sepal-width', 'petal-length', 'petal-width'};

% Create pairplot object with customized visualization
% 创建散点图矩阵对象,自定义可视化风格
SP = SPairplot(X, 'Group', group);
SP.DiagType = 14;
SP.UpperType = 11;
SP.LowerType = 14;
SP.BottomType = 12;
SP.RightType = 11;
SP.CornerType = 1;
SP.draw();

% Set X-labels and Y-labels
% 设置 X 轴标签和 Y 轴标签
SP.setXLabelString(label);
SP.setYLabelString(label);

双矩阵输入:添加边际图

matlab 复制代码
% Generate synthetic data with three groups (150 samples, 6 features total)
% 生成包含三个分组的合成数据(150个样本,共6个特征)
P1 = mvnrnd([0,20,0,10,8,7], eye(6).*6, 50);
P2 = mvnrnd([10,5,15,0,4,2], eye(6).*8, 50);
P3 = mvnrnd([10,15,0,0,2,3], eye(6).*[3,9,2,1,4,4], 50);
G  = [ones(50,1)*1; ones(50,1)*2; ones(50,1)*3];  % Group labels / 分组标签

% Split into X (first 4 variables) and Y (last 2 variables)
% 拆分为 X(前4个变量)和 Y(后2个变量)
XY = [P1; P2; P3];
X = XY(:, 1:4);   
Y = XY(:, 5:6);   

% Create pairplot with two variable sets (X vs Y)
% 创建双变量集的散点图矩阵(X 与 Y 的对比)
% Note: When X and Y are both provided, only LowerType (off-diagonal below)
% is effective. Upper triangle and diagonal are automatically disabled.
% 注意:当同时输入 X 和 Y 时,仅 LowerType(下三角区域)生效,
% 上三角和对角线区域自动禁用。
fig = figure('Units','normalized', 'Position',[.1,.2,.7,.65]);
SP = SPairplot(fig, X, Y, 'Group', G);
SP.GroupLabel = {'G1','G2','G3'};
SP.LowerType = 7;
SP.BottomType = 12;
SP.RightType = 14;
SP.CornerType = 1;
SP.draw();


SP.setXLabelString({'X1', 'X2', 'X3', 'X4'});
SP.setYLabelString({'Y1', 'Y2'});

完整代码

matlab 复制代码
classdef SPairplot < handle
% SPairplot Create and customize pairplot (scatter plot matrix | 散点图矩阵)
%   SP = SPairplot(X); creates a pairplot from an S-by-N numerical matrix X,
%   where rows (S) are samples and columns (N) are variables.
%   通过 SxN 大小的数值矩阵 X 创建散点图矩阵对象,行 (S) 表示样本,列 (N) 表示变量。
%
%   SP = SPairplot(X, Y); creates a pairplot comparing two datasets. X is S-by-N
%   and Y is S-by-M, showing relationships between X-variables and Y-variables.
%   通过两组数据矩阵 X 和 Y 创建散点图矩阵对象,X 为 SxN 大小,Y 为 SxM 大小,
%   展示 X 变量与 Y 变量之间的关系。
%
%   SP = SPairplot(fig, ___); creates a pairplot in the specified figure.
%   在指定图窗中生成散点图矩阵对象。
%
%   SP = SPairplot(___, propName, propVal); specifies property name-value pairs
%   when creating the pairplot object.
%   创建散点图矩阵对象时为其设置属性。
%
%   SP.propName = propVal; sets properties for the pairplot object after
%   creation, before rendering.
%   创建散点图矩阵对象后,绘图前为其设置属性。
%
%   SP = SP.draw(); renders the pairplot.
%   渲染散点图矩阵对象。

    properties
        fig = [] % Figure handle.
        axMat    % 
        axProp = {'Box','on', 'FontName','Times New Roman', 'FontSize',12, ...
            'TickDir','out', 'LineWidth',1}
        axWidth
        axHeight

        arginList = {'CData', 'Group', 'Padding','SPacing', ...
            'DiagType', 'UpperType', 'LowerType', 'BottomType','RightType'}

        X = []   % S-by-N matrix : X = [X1, X2, ..., XN], where Xn is column vector
        Y = []   % S-by-M matrix : Y = [Y1, Y2, ..., YM], where Ym is column vector

        Group = []
        GroupLabel
        GroupNum
        
        LowerType = 2
        UpperType = 1
        % ===========================================================================
        %  No. | Type         | Description
        % -----|--------------|------------------------------------------------------
        %   0  | 'none'       | None
        %   1  | 'scatter'    | Scatter plot with filled markers
        %   2  | 'contour'    | Contour plot based on kernel density estimation
        %   3  | 'pred'       | Linear regression with prediction interval (polyfit)
        %   4  | 'convhull'   | Scatter plot with convex hull outline
        %   5  | 'cover'      | Scatter plot with buffered/expanded convex hull (rounded)
        %   6  | 'ellipse'    | Scatter plot with confidence ellipse (99%)
        %   7  | 'centroid'   | Star plot: points connected to group centroid
        %   8  | 'errorbar'   | Cross error bar (mean ± std) for each group
        %   9  | 'conf'       | Linear regression with confidence interval (fitlm)
        %  10  | 'dot'        | Scatter plot using '.' marker (single-pixel points)
        %  11  | 'corr'       | Display data correlations and significance markers.
        %  12  | 'heatmap'    | Heatmap with data correlations
        %  13  | 'contourf'   | Filled contour plot based on kernel density estimation
        %  14  | 'cmp-contour'| Contour plot with custom colormap
        %  15  | 'cmp-scatter'| Scatter plot with custom colormap 
        % ---------------------------------------------------------------------------

        
        DiagType = 1
        BottomType = 0
        RightType  = 0
        % ===========================================================================
        %  No. | Type           | Description
        % -----|----------------|----------------------------------------------------
        %   0  | 'none'         | None
        %   1  | 'hist'         | Standard histogram
        %   2  | 'kd-area'      | Kernel density estimation area (filled)
        %   3  | 'kd-line'      | Kernel density estimation line only
        %   4  | 'kd-both'      | Kernel density area + line
        %   5  | 'kd-hist'      | Histogram overlayed with kernel density line
        %   6  | 'box'          | Box plot (median, quartiles, outliers)
        %   7  | 'violin'       | Full violin plot (symmetric KDE)
        %   8  | 'rug'          | Rug plot (vertical/horizontal line scatter)
        %   9  | 'joyplot'      | Joyplot / Ridgeline plot (stacked KDE)
        %  10  | 'half-violin'  | Half violin plot (right side only, compact)
        %  11  | 'raincloud'    | Raincloud plot (half-violin + scatter)
        %  12  | 'cdf-hist'     | Empirical cumulative distribution function (histogram-based ECDF)
        %  13  | 'cdf-line'     | Empirical cumulative distribution function (step/line ECDF)
        %  14  | 'ridge-hist'   | Ridge-style histogram with vertical offsets
        % ---------------------------------------------------------------------------


        CornerType = 0
        % ===========================================================================
        %  No. | Type           | Description
        % -----|----------------|----------------------------------------------------
        %   0  | 'none'         | None
        %   1  | 'legend'       | Legend
        %   2  | 'bar'          | Bar chart showing the number of samples in each group
        % ---------------------------------------------------------------------------

        % For a (M+1)-by-(N+1) size plot matrix:
        % ┌────────────────────────────────────────────────────────┐
        % | ┌─────────┐ ┌─────────┐     ┌─────────┐    ┌─────────┐ |
        % | │  Diag   │ │  Upper  │ ... │  Upper  │    │  Right  │ |
        % | |  (1,1)  | |  (1,2)  | ... |  (1,N)  |    | (1,N+1) | |
        % | └─────────┘ └─────────┘     └─────────┘    └─────────┘ |
        % | ┌─────────┐ ┌─────────┐     ┌─────────┐    ┌─────────┐ |
        % | │  Lower  │ │  Diag   │ ... │  Upper  │    │  Right  │ |
        % | |  (2,1)  | |  (2,2)  | ... |  (2,N)  |    | (2,N+1) | |
        % | └─────────┘ └─────────┘     └─────────┘    └─────────┘ |
        % |     ⋮  ⋮         ⋮  ⋮             ⋮  ⋮           ⋮  ⋮     |
        % | ┌─────────┐ ┌─────────┐     ┌─────────┐    ┌─────────┐ |
        % | │  Lower  │ │  Lower  │ ... │  Diag   │    │  Right  │ |
        % | |  (M,1)  | |  (M,2)  | ... |  (M,N)  |    | (M,N+1) | |
        % | └─────────┘ └─────────┘     └─────────┘    └─────────┘ |
        % |                                                        |
        % | ┌─────────┐ ┌─────────┐     ┌─────────┐    ┌─────────┐ |
        % | │ Bottom  │ │ Bottom  │ ... │ Bottom  │    │ Corner  │ |
        % | | (M+1,1) | | (M+1,2) | ... | (M+1,N) |    |(M+1,N+1)| |
        % | └─────────┘ └─────────┘     └─────────┘    └─────────┘ |
        % └────────────────────────────────────────────────────────┘



        Padding = [.08, .08, .04, .04];   % [left(l), bottom(b), right(r), top(t)]
        Spacing = [.1, .1];               % [horizontal(h), vertical(v)]

        % For a R-by-C size plot matrix:
        % ┌──────────────────────────────────────────────────────────────────┐
        % |              ↑                                                   |
        % |         padding top                                              |
        % |              ↓                                                   |
        % |         ┌─────────┐            ┌─────────┐   ┌─────────┐         |
        % |         │ subplot │ ←--------→ │ subplot │...│ subplot │         |
        % |         |  (1,1)  | spacing(h) |  (1,2)  |...|  (1,C)  |         |
        % |         └─────────┘  /(C - 1)  └─────────┘   └─────────┘         |
        % |              ↑                      ↑             ↑              |
        % |      spacing(v)/(R - 1)            spacing(v)/(R - 1)            |                      
        % |              ↓                      ↓             ↓              |
        % |         ┌─────────┐            ┌─────────┐   ┌─────────┐         |
        % |         │ subplot │ ←--------→ │ subplot │...│ subplot │         |
        % |         |  (2,1)  |            |  (2,2)  |...|  (2,C)  |         |
        % |         └─────────┘ spacing(h) └─────────┘   └─────────┘         |
        % |             ⋮  ⋮      /(C - 1)      ⋮  ⋮           ⋮  ⋮             |
        % |         ┌─────────┐            ┌─────────┐   ┌─────────┐         |
        % | ←-----→ │ subplot │ ←--------→ │ subplot │...│ subplot │ ←-----→ |
        % | padding |  (R,1)  |            |  (R,2)  |...|  (R,C)  | padding |
        % |  left   └─────────┘            └─────────┘   └─────────┘  right  |
        % |              ↑                                                   |
        % |       padding bottom                                             |
        % |              ↓                                                   |
        % └──────────────────────────────────────────────────────────────────┘



        CData = [102,173,194;  36, 59, 66; 232, 69, 69; 194,148,102;  54, 43, 33]./255;
        % Alternative color schemes :
        % CData = [211, 43, 43;  61, 96,137; 249,206, 61;  76,103, 86;  80, 80, 80]./255;
        % CData = [223,122, 94;  60, 64, 91; 130,178,154; 244,241,222; 240,201,134]./255;
        % CData = [122,117,119; 255,163, 25; 135,146, 73; 126, 15,  4;  30, 93,134]./255;
        % CData = [198,199,201;  38, 74, 96; 209, 80, 51; 241,174, 44;  12, 13, 15]./255;
        % CData = [235, 75, 55;  77,186,216;  58, 84,141;   2,162,136; 245,155,122]./255;
        % CData = [ 23, 23, 23; 121, 17, 36;  31, 80, 91;  44,  9, 75;  61, 36, 42]./255;
        % CData = [ 47, 62, 66; 203,129, 70;   0, 64,115; 152, 58, 58;  20, 72, 83]./255;
        Colormap = [.97, .99, .94; .95, .98, .92; .92, .97, .90;
            .90, .96, .88; .88, .95, .86; .86, .94, .83; 
            .84, .94, .81; .82, .93, .79; .79, .92, .77; 
            .75, .90, .75; .72, .89, .74; .68, .88, .72; 
            .64, .86, .72; .60, .84, .73; .55, .83, .75;
            .51, .81, .76; .46, .79, .78; .41, .76, .79; 
            .37, .74, .81; .32, .71, .82; .28, .68, .81; 
            .25, .64, .79; .21, .60, .77; .18, .56, .75; 
            .14, .52, .73; .11, .49, .71; .07, .45, .69; 
            .04, .41, .68; .03, .37, .64; .03, .33, .59;
            .03, .29, .55; .03, .25, .51];
        corrProp = {'Type','Pearson'}
        pvalLevels = [0.05, 0.01, 0.001]
        corrCLim = [-1, 1];
        corrColormap =  [.62, .00, .26; .69, .08, .28; .76, .16, .29;
            .83, .24, .31; .87, .30, .30; .91, .36, .28;
            .95, .42, .27; .97, .49, .29; .98, .58, .33;
            .99, .66, .37; .99, .73, .42; .99, .79, .47;
            1.0, .85, .52; 1.0, .90, .58; 1.0, .94, .65;
            1.0, .98, .72; .98, .99, .72; .95, .98, .68;
            .92, .97, .63; .87, .95, .60; .80, .92, .62;
            .72, .89, .63; .64, .86, .64; .56, .82, .64;
            .47, .79, .65; .39, .75, .65; .32, .67, .68;
            .26, .60, .71; .20, .53, .74; .26, .45, .70;
            .31, .38, .67; .37, .31, .64];
    end

    properties (Hidden)
        S, M, N, R, C  
    end

    methods
        function obj = SPairplot(varargin)
            if isa(varargin{1}, 'matlab.ui.Figure')
                obj.fig = varargin{1}; varargin(1) = [];
            end

            obj.Colormap = obj.Colormap(end:-1:1, :);
            obj.X = varargin{1};
            varargin(1) = [];
            obj.S = size(obj.X, 1);
            obj.N = size(obj.X, 2);

            if (~isempty(varargin)) && isnumeric(varargin{1})
                obj.Y = varargin{1};
                varargin(1) = [];
                obj.M = size(obj.Y, 2);
            else
                obj.M = size(obj.X, 2);
            end

            % Parse name-value input arguments.
            for i = 1:2:(length(varargin) - 1)
                tIndex = ismember(lower(obj.arginList), lower(varargin{i}));
                if any(tIndex)
                    obj.(obj.arginList{tIndex}) = varargin{i + 1};
                end
            end
        end

        function obj = draw(obj)
            if isempty(obj.fig)
                obj.fig = figure('Units','normalized', 'Position',[.05,.05,.65,.88]);
            end
            set(obj.fig, 'Color',[1,1,1])

            if (isnumeric(obj.BottomType)&&obj.BottomType == 0) || ...
              (~isnumeric(obj.BottomType)&&strcmpi(obj.BottomType, 'none'))
                obj.R = obj.M;
            else
                obj.R = obj.M + 1;
            end
            if (isnumeric(obj.RightType)&&obj.RightType == 0) || ...
              (~isnumeric(obj.RightType)&&strcmpi(obj.RightType, 'none'))
                obj.C = obj.N;
            else
                obj.C = obj.N + 1;
            end


            % Process group labels: convert to consecutive indices starting from 1
            % 处理分组标签:转换为从1开始的连续索引
            if isempty(obj.Group)
                obj.Group = ones(obj.S, 1);
                if isempty(obj.GroupLabel)
                    obj.GroupLabel = 'group-1';
                end
                obj.GroupNum = 1;
            else
                if isnumeric(obj.Group)
                    [TGL, ~, obj.Group] = unique(obj.Group);
                    if isempty(obj.GroupLabel)
                        obj.GroupLabel = compose('group-%d', TGL);
                    end
                    obj.GroupNum = length(TGL);
                else
                    if isempty(obj.GroupLabel)
                        [obj.GroupLabel, ~, obj.Group] = unique(obj.Group, 'stable');
                        obj.GroupNum = length(obj.GroupLabel);
                    else
                        [~, obj.Group] = ismember(obj.Group, obj.GroupLabel);
                        obj.Group = obj.Group(:);
                        obj.GroupNum = length(obj.GroupLabel);
                    end
                end
            end
            obj.GroupLabel = obj.GroupLabel(:).';

            if size(obj.CData, 1) < obj.GroupNum
                obj.CData = [obj.CData; .3 + .6.*rand([obj.GroupNum, 3])];
            end

            % Ensure non-negative spacing and padding
            % 确保间距和内边距为非负值
            obj.Padding = abs(obj.Padding);
            obj.Spacing = abs(obj.Spacing);

            % Calculate total horizontal/vertical space occupied by padding and spacing
            % 计算内边距和间距占用的总水平/垂直空间
            tW = obj.Spacing(1) + obj.Padding(1) + obj.Padding(3);
            tH = obj.Spacing(2) + obj.Padding(2) + obj.Padding(4);

            % Scale down if total exceeds 0.5 (limit to 50% of figure)
            % 若总占用超过0.5则等比缩放到50%
            if tW > .5
                obj.Spacing(1) = obj.Spacing(1)./tW.*.5;
                obj.Padding(1) = obj.Padding(1)./tW.*.5;
                obj.Padding(3) = obj.Padding(3)./tW.*.5;
                tW = .5;
            end
            obj.axWidth = (1 - tW)/(obj.C);
            if tH > .5
                obj.Spacing(2) = obj.Spacing(2)./tH.*.5;
                obj.Padding(2) = obj.Padding(2)./tH.*.5;
                obj.Padding(4) = obj.Padding(4)./tH.*.5;
                tH = .5;
            end
            obj.axHeight = (1 - tH)/(obj.R); 


            % Create subplot grid and draw
            % 创建子图网格并绘图
            
            XLimSet = repmat([inf, -inf], [obj.C, 1]);
            YLimSet = repmat([inf, -inf], [obj.R, 1]);
            XLimUpdate = ones(obj.R, obj.C) > 0;
            YLimUpdate = ones(obj.R, obj.C) > 0;
            for m = 1:obj.R
                for n = 1:obj.C
                    obj.axMat{m, n} = axes('Parent',obj.fig, ...
                        'Position',[obj.Padding(1) + (n - 1)*(obj.axWidth + obj.Spacing(1)/(obj.C - 1)), ...
                                    obj.Padding(2) + (obj.R - m)*(obj.axHeight + obj.Spacing(2)/(obj.R - 1)), ...
                                    obj.axWidth,obj.axHeight], obj.axProp{:}, 'NextPlot','add');
                    tAx = obj.axMat{m, n}; tX = obj.X(:, min(n, obj.N));
                    xlen = max(max(obj.X)) - min(min(obj.X));
                    if isempty(obj.Y)
                        tY = obj.X(:, min(m, obj.M));
                        ylen = max(max(obj.X)) - min(min(obj.X));
                    else
                        tY = obj.Y(:, min(m, obj.M));
                        ylen = max(max(obj.Y)) - min(min(obj.Y));
                    end
                    
                    for k = 1:obj.GroupNum
                        tXk = tX(obj.Group == k);
                        tYk = tY(obj.Group == k);


                        if m == obj.R && n == obj.C && obj.R ~= obj.M && obj.C ~= obj.N
                            XLimUpdate(m, n) = false;
                            YLimUpdate(m, n) = false;

                            switch obj.CornerType
                                case {'legend', 1}
                                    tAx.XLim = [0, 1];
                                    tAx.YLim = [0, 1];
                                    tAx.XTick = [];
                                    tAx.YTick = [];
                                    fill(tAx, [0,1,0] - 2, [0,0,1] - 2, obj.CData(k,:),'FaceAlpha',0.3, ...
                                        'EdgeColor',obj.CData(k,:), 'LineWidth',1)
                                    if k == obj.GroupNum
                                        lgdHdl = legend(tAx, obj.GroupLabel, 'FontSize',17, 'Box','off');
                                        lgdHdl.Position(1) = tAx.Position(1) + tAx.Position(3)/2 - lgdHdl.Position(3)/2;
                                        lgdHdl.Position(2) = tAx.Position(2) + tAx.Position(4)/2 - lgdHdl.Position(4)/2;
                                    end
                                case {'bar', 2}
                                    fill(tAx, [-.4,.4,.4,-.4] + k, [0,0,1,1].*length(tXk), obj.CData(k,:) ,'FaceAlpha',0.3, ...
                                        'EdgeColor',obj.CData(k,:), 'LineWidth',1)
                                    if k == obj.GroupNum
                                        tAx.XTick = 1:obj.GroupNum;
                                        tAx.XLim = [0, obj.GroupNum + 1];
                                        tAx.XTickLabel = obj.GroupLabel;
                                    end

                            end



                        elseif n == obj.C && obj.C ~= obj.N && m <= obj.M
                            switch obj.RightType
                                case {'hist', 1}
                                    histogram(tAx, tYk, linspace(min(tY), max(tY), 20), 'FaceColor',obj.CData(k,:),...
                                        'Orientation','horizontal', 'EdgeColor',[1 1 1]*.1, 'FaceAlpha',0.6, 'LineWidth',.8)
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'kd-area', 2}
                                    [f, yi] = ksdensity(tYk);
                                    fill(tAx, [f, 0], [yi, yi(1)], obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none')
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'kd-line', 3}
                                    [f, yi] = ksdensity(tYk);
                                    plot(tAx, f, yi, 'Color',obj.CData(k,:), 'LineWidth',2)
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'kd-both', 4}
                                    [f, yi] = ksdensity(tYk);
                                    fill(tAx, [f, 0], [yi, yi(1)], obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none')
                                    plot(tAx, f, yi, 'Color',obj.CData(k,:), 'LineWidth',2, 'HandleVisibility','off')
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'kd-hist', 5}
                                    HHdl = histogram(tAx, tYk, linspace(min(tY), max(tY), 20), 'FaceColor',obj.CData(k,:),...
                                        'Orientation','horizontal', 'EdgeColor',[1 1 1]*.1, 'FaceAlpha',0.6, 'LineWidth',.8);
                                    [f, yi] = ksdensity(tYk);
                                    plot(tAx, f.*size(tYk, 1).*HHdl.BinWidth, yi, 'Color',obj.CData(k,:), 'LineWidth',2, 'HandleVisibility','off')
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'box', 6}
                                    tjDataY = tYk; fullDataY = tjDataY;
                                    outliBool = isoutlier(tjDataY, 'quartiles');
                                    outli = tjDataY(outliBool);
                                    scatter(tAx, repmat(k, [sum(outliBool), 1]), outli, 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataY(outliBool) = [];
                                    qt25 = quantile(fullDataY, 0.25);
                                    qt75 = quantile(fullDataY, 0.75);
                                    med = median(fullDataY);
                                    plot(tAx, [k, k], [max(tjDataY), qt75], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [k, k], [min(tjDataY), qt25], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [-0.15, 0.15] + k, [max(tjDataY), max(tjDataY)], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [-0.15, 0.15] + k, [min(tjDataY), min(tjDataY)], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [-0.25, 0.25] + k, [med, med], 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    fill(tAx, k + 0.25 .* [-1, 1, 1, -1], [qt25, qt25, qt75, qt75], obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    tAx.XLim = [0, obj.GroupNum + 1];
                                    tAx.XTick = [];
                                case {'violin', 7}   
                                    tjDataY = tYk; fullDataY = tjDataY;
                                    [f, yi] = ksdensity(tjDataY);
                                    f(yi > max(tjDataY)) = []; yi(yi > max(tjDataY)) = [];
                                    f(yi < min(tjDataY)) = []; yi(yi < min(tjDataY)) = [];
                                    fill(tAx, [f, -f(end:-1:1)].*ylen./25 + k, [yi, yi(end:-1:1)], obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    outliBool = isoutlier(tjDataY, 'quartiles');
                                    outli = tjDataY(outliBool);
                                    scatter(tAx, repmat(k, [sum(outliBool), 1]), outli, 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataY(outliBool) = [];
                                    qt25 = quantile(fullDataY, 0.25);
                                    qt75 = quantile(fullDataY, 0.75);
                                    med = median(fullDataY);
                                    plot(tAx, [k, k], [max(tjDataY), qt75], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [k, k], [min(tjDataY), qt25], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    fill(tAx, k + 0.15 .* [-1, 1, 1, -1], [qt25, qt25, qt75, qt75], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2, 'HandleVisibility','off');
                                    plot(tAx, [-0.15, 0.15] + k, [med, med], 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    tAx.XLim = [0, obj.GroupNum + 1];
                                    tAx.XTick = [];
                                case {'rug', 8}
                                    LY = [tYk, tYk, tYk.*nan]';
                                    LX = repmat([k - .3, k + .3, nan], [length(tYk), 1])';
                                    line(tAx, LX(:), LY(:), 'Color',[obj.CData(k,:), .4], 'lineWidth',1)
                                    tAx.XLim = [0, obj.GroupNum + 1];
                                    tAx.XTick = [];
                                case {'joyplot', 'ridgeline', 9}
                                    [f,yi] = ksdensity(tYk);
                                    fill(tAx, [f, 0].*ylen./25 + (k - 1).*.5, [yi, yi(1)], obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none');
                                    plot(tAx,f.*ylen./25 + (k - 1).*.5,  yi, 'Color',obj.CData(k,:), 'LineWidth',1.2)
                                    tAx.XTick = [];
                                    tAx.YLim = [min(tY), max(tY)];
                                case {'half-violin', 10}
                                    tjDataY = tYk; fullDataY = tjDataY;
                                    [f, yi] = ksdensity(tjDataY);
                                    f(yi > max(tjDataY)) = []; yi(yi > max(tjDataY)) = [];
                                    f(yi < min(tjDataY)) = []; yi(yi < min(tjDataY)) = [];
                                    fill(tAx, [0, f, 0].*ylen./25 + k + 0.15, [yi(1), yi, yi(end)], obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    outliBool = isoutlier(tjDataY, 'quartiles');
                                    outli = tjDataY(outliBool);
                                    scatter(tAx, repmat(k - 0.2, [sum(outliBool), 1]), outli, 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataY(outliBool) = [];
                                    qt25 = quantile(fullDataY, 0.25);
                                    qt75 = quantile(fullDataY, 0.75);
                                    med = median(fullDataY);
                                    plot(tAx, [k, k] - 0.2, [max(tjDataY), qt75], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [k, k] - 0.2, [min(tjDataY), qt25], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    fill(tAx, k - 0.2 + 0.15 .* [-1, 1, 1, -1], [qt25, qt25, qt75, qt75], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2, 'HandleVisibility','off');
                                    plot(tAx, [-0.15, 0.15] + k - 0.2, [med, med], 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    tAx.XLim = [0, obj.GroupNum + 1];
                                    tAx.XTick = [];
                                case {'raincloud', 11}
                                    tjDataY = tYk; fullDataY = tjDataY;
                                    [f, yi] = ksdensity(tjDataY);
                                    f(yi > max(tjDataY)) = []; yi(yi > max(tjDataY)) = [];
                                    f(yi < min(tjDataY)) = []; yi(yi < min(tjDataY)) = [];
                                    fill(tAx, [0, f, 0].*ylen./25 + k + 0.15, [yi(1), yi, yi(end)], obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    scatter(tAx, tYk .* 0 + k - 0.2 + 0.12 .* (rand(length(tYk), 1) - 0.5), tYk, ...
                                        45, 'filled', 'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.3)
                                    outliBool = isoutlier(tjDataY, 'quartiles');
                                    tjDataY(outliBool) = [];
                                    qt25 = quantile(fullDataY, 0.25);
                                    qt75 = quantile(fullDataY, 0.75);
                                    med = median(fullDataY);
                                    plot(tAx, [k, k] - 0.2, [max(tjDataY), qt75], 'LineWidth',1.2, 'Color',[0,0,0])
                                    plot(tAx, [k, k] - 0.2, [min(tjDataY), qt25], 'LineWidth',1.2, 'Color',[0,0,0])
                                    fill(tAx, k - 0.2 + 0.15 .* [-1, 1, 1, -1], [qt25, qt25, qt75, qt75], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    plot(tAx, [-0.15, 0.15] + k - 0.2, [med, med], 'LineWidth',1.2, 'Color',obj.CData(k,:))
                                    tAx.XLim = [0, obj.GroupNum + 1];
                                    tAx.XTick = [];
                                case {'cdf-hist', 12}
                                    histogram(tAx, tYk, linspace(min(tY), max(tY), 20), 'FaceColor',obj.CData(k,:), 'Normalization', 'cdf', 'Orientation','horizontal');
                                case {'cdf-line', 13}
                                    [f, yi] = ecdf(tXk);
                                    plot(tAx, f, yi,'Color', obj.CData(k,:),'LineWidth',1.5); 
                                case {'ridge-hist',14}
                                    tobj = histogram(tAx, tYk, linspace(min(tY), max(tY), 20));
                                    binEdges = tobj.BinEdges;
                                    counts = tobj.Values;
                                    delete(tobj);
                                    for i = 1:length(counts)
                                        y = [binEdges(i), binEdges(i), binEdges(i+1), binEdges(i+1)];
                                        x = [0, counts(i), counts(i), 0]./max(counts);
                                        fill(tAx, x + k - 1, y, [0.2, 0.4, 0.8], 'FaceAlpha',0.6, ...
                                            'LineWidth',.8, 'FaceColor',obj.CData(k,:),'EdgeColor',[1 1 1]*.1);
                                        plot(tAx, [k - 1, k - 1], ([min(tY), max(tY)] - mean(tY)).*2 + mean(tY), 'LineWidth',1, 'Color','k')
                                    end
                                    tAx.XLim = [0, k];
                                    tAx.YLim = [min(tY), max(tY)];
                            end



                        elseif ((m == n && isempty(obj.Y)) || (m == obj.R && obj.R ~= obj.M)) && ...
                                ((n < obj.C && obj.C ~= obj.N) || (n <= obj.C && obj.C == obj.N))
                            
                            if m == obj.R && obj.R ~= obj.M
                                tType = obj.BottomType;
                            else
                                tType = obj.DiagType; YLimUpdate(m, n) = false;
                            end
                            switch tType
                                case {'none', 0}
                                    XLimUpdate(m, n) = false;
                                    YLimUpdate(m, n) = false;
                                    set(tAx, 'XColor','none','YColor','none')
                                case {'hist', 1}
                                    histogram(tAx, tXk, linspace(min(tX), max(tX), 20), 'FaceColor',obj.CData(k,:),...
                                        'EdgeColor',[1 1 1]*.1, 'FaceAlpha',0.6, 'LineWidth',.8)
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'kd-area', 2}
                                    [f,xi] = ksdensity(tXk);
                                    fill(tAx, [xi, xi(1)], [f, 0], obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none')
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'kd-line', 3}
                                    [f, xi] = ksdensity(tXk);
                                    plot(tAx, xi, f, 'Color',obj.CData(k,:), 'LineWidth',2)
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'kd-both', 4}
                                    [f, xi] = ksdensity(tXk);
                                    fill(tAx, [xi, xi(1)], [f, 0], obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none')
                                    plot(tAx, xi, f, 'Color',obj.CData(k,:), 'LineWidth',2, 'HandleVisibility','off')
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'kd-hist', 5}
                                    HHdl = histogram(tAx, tXk, linspace(min(tX), max(tX), 20), 'FaceColor',obj.CData(k,:),...
                                        'EdgeColor',[1 1 1]*.1, 'FaceAlpha',0.6, 'LineWidth',.8);
                                    [f, xi] = ksdensity(tXk);
                                    plot(tAx, xi, f.*size(tXk, 1).*HHdl.BinWidth, 'Color',obj.CData(k,:), 'LineWidth',2, 'HandleVisibility','off')
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'box', 6}
                                    tjDataX = tXk; fullDataX = tjDataX;
                                    outliBool = isoutlier(tjDataX, 'quartiles');
                                    outli = tjDataX(outliBool);
                                    scatter(tAx, outli, repmat(obj.GroupNum + 1 - k, [sum(outliBool), 1]), 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataX(outliBool) = [];
                                    qt25 = quantile(fullDataX, 0.25);
                                    qt75 = quantile(fullDataX, 0.75);
                                    med = median(fullDataX);
                                    plot(tAx, [max(tjDataX), qt75], obj.GroupNum + 1 - [k, k], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [min(tjDataX), qt25], obj.GroupNum + 1 - [k, k], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [max(tjDataX), max(tjDataX)], [-0.15, 0.15] + obj.GroupNum + 1 - k, 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [min(tjDataX), min(tjDataX)], [-0.15, 0.15] + obj.GroupNum + 1 - k, 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [med, med], [-0.25, 0.25] + obj.GroupNum + 1 - k, 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    fill(tAx, [qt25, qt25, qt75, qt75], obj.GroupNum + 1 - k + 0.25 .* [-1, 1, 1, -1], obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    tAx.YLim = [0, obj.GroupNum + 1];
                                    tAx.YTick = [];
                                case {'violin', 7}   
                                    tjDataX = tXk; fullDataX = tjDataX;
                                    [f, xi] = ksdensity(tjDataX);
                                    f(xi > max(tjDataX)) = []; xi(xi > max(tjDataX)) = [];
                                    f(xi < min(tjDataX)) = []; xi(xi < min(tjDataX)) = [];
                                    fill(tAx, [xi, xi(end:-1:1)], [f, -f(end:-1:1)].*xlen./25 + obj.GroupNum + 1 - k, obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    outliBool = isoutlier(tjDataX, 'quartiles');
                                    outli = tjDataX(outliBool);
                                    scatter(tAx, outli, repmat(obj.GroupNum + 1 - k, [sum(outliBool), 1]), 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataX(outliBool) = [];
                                    qt25 = quantile(fullDataX, 0.25);
                                    qt75 = quantile(fullDataX, 0.75);
                                    med = median(fullDataX);
                                    plot(tAx, [max(tjDataX), qt75], obj.GroupNum + 1 - [k, k], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [min(tjDataX), qt25], obj.GroupNum + 1 - [k, k], 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    fill(tAx, [qt25, qt25, qt75, qt75], obj.GroupNum + 1 - k + 0.15 .* [-1, 1, 1, -1], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2, 'HandleVisibility','off');
                                    plot(tAx, [med, med], [-0.15, 0.15] + obj.GroupNum + 1 - k, 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    tAx.YLim = [0, obj.GroupNum + 1];
                                    tAx.YTick = [];
                                case {'rug', 8}
                                    LX = [tXk, tXk, tXk.*nan]';
                                    LY = repmat([obj.GroupNum + 1 - k - .3, obj.GroupNum + 1 - k + .3, nan], [length(tXk), 1])';
                                    line(tAx, LX(:), LY(:), 'Color',[obj.CData(k,:), .4], 'lineWidth',1)
                                    tAx.YLim = [0, obj.GroupNum + 1];
                                    tAx.YTick = [];
                                case {'joyplot', 'ridgeline', 9}
                                    [f,xi]=ksdensity(tXk);
                                    fill(tAx, [xi, xi(1)], [f, 0].*xlen./25 + (obj.GroupNum + 1 - k - 1).*.5,obj.CData(k,:),...
                                        'FaceAlpha',0.3, 'EdgeColor','none');
                                    plot(tAx, xi, f.*xlen./25 + (obj.GroupNum + 1 - k - 1).*.5, 'Color',obj.CData(k,:), 'LineWidth',1.2)
                                    tAx.YTick = [];
                                    tAx.XLim = [min(tX), max(tX)];
                                case {'half-violin', 10}
                                    tjDataX = tXk; fullDataX = tjDataX;
                                    [f, xi] = ksdensity(tjDataX);
                                    f(xi > max(tjDataX)) = []; xi(xi > max(tjDataX)) = [];
                                    f(xi < min(tjDataX)) = []; xi(xi < min(tjDataX)) = [];
                                    fill(tAx, [xi(1), xi, xi(end)], [0, f, 0].*xlen./25 +obj.GroupNum + 1 -  k + 0.15, obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    outliBool = isoutlier(tjDataX, 'quartiles');
                                    outli = tjDataX(outliBool);
                                    scatter(tAx, outli, repmat(obj.GroupNum + 1 - k - 0.2, [sum(outliBool), 1]), 45, 'filled', ...
                                        'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.8, 'HandleVisibility','off')
                                    tjDataX(outliBool) = [];
                                    qt25 = quantile(fullDataX, 0.25);
                                    qt75 = quantile(fullDataX, 0.75);
                                    med = median(fullDataX);
                                    plot(tAx, [max(tjDataX), qt75], obj.GroupNum + 1 - [k, k] - 0.2, 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    plot(tAx, [min(tjDataX), qt25], obj.GroupNum + 1 - [k, k] - 0.2, 'LineWidth',1.2, 'Color',[0,0,0], 'HandleVisibility','off')
                                    fill(tAx, [qt25, qt25, qt75, qt75], obj.GroupNum + 1 - k - 0.2 + 0.15 .* [-1, 1, 1, -1], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2, 'HandleVisibility','off');
                                    plot(tAx, [med, med], [-0.15, 0.15] + obj.GroupNum + 1 - k - 0.2, 'LineWidth',1.2, 'Color',obj.CData(k,:), 'HandleVisibility','off')
                                    tAx.YLim = [0, obj.GroupNum + 1];
                                    tAx.YTick = [];
                                case {'raincloud', 11}
                                    tjDataX = tXk; fullDataX = tjDataX;
                                    [f, xi] = ksdensity(tjDataX);
                                    f(xi > max(tjDataX)) = []; xi(xi > max(tjDataX)) = [];
                                    f(xi < min(tjDataX)) = []; xi(xi < min(tjDataX)) = [];
                                    fill(tAx, [xi(1), xi, xi(end)], [0, f, 0].*xlen./25 + obj.GroupNum + 1 - k + 0.15, obj.CData(k,:), ...
                                        'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    scatter(tAx, tXk, tXk .* 0 + obj.GroupNum + 1 - k - 0.2 + 0.12 .* (rand(length(tXk), 1) - 0.5), ...
                                        45, 'filled', 'CData',obj.CData(k,:), 'LineWidth',1.2, 'MarkerFaceAlpha',0.3)
                                    outliBool = isoutlier(tjDataX, 'quartiles');
                                    tjDataX(outliBool) = [];
                                    qt25 = quantile(fullDataX, 0.25);
                                    qt75 = quantile(fullDataX, 0.75);
                                    med = median(fullDataX);
                                    plot(tAx, [max(tjDataX), qt75], obj.GroupNum + 1 - [k, k] - 0.2, 'LineWidth',1.2, 'Color',[0,0,0])
                                    plot(tAx, [min(tjDataX), qt25], obj.GroupNum + 1 - [k, k] - 0.2, 'LineWidth',1.2, 'Color',[0,0,0])
                                    fill(tAx, [qt25, qt25, qt75, qt75], obj.GroupNum + 1 - k - 0.2 + 0.15 .* [-1, 1, 1, -1], [1,1,1], ...
                                        'FaceAlpha',0.95, 'EdgeColor',obj.CData(k,:), 'LineWidth',1.2);
                                    plot(tAx, [med, med], [-0.15, 0.15] + obj.GroupNum + 1 - k - 0.2, 'LineWidth',1.2, 'Color',obj.CData(k,:))
                                    tAx.YLim = [0, obj.GroupNum + 1];
                                    tAx.YTick = [];
                                case {'cdf-hist', 12}
                                    histogram(tAx, tXk, linspace(min(tX), max(tX), 20), 'FaceColor',obj.CData(k,:), 'Normalization', 'cdf');
                                case {'cdf-line', 13}
                                    [f, xi] = ecdf(tXk);
                                    plot(tAx, xi, f,'Color', obj.CData(k,:),'LineWidth',1.5); 
                                case {'ridge-hist',14}
                                    tobj = histogram(tAx, tXk, linspace(min(tX), max(tX), 20));
                                    binEdges = tobj.BinEdges;
                                    counts = tobj.Values;
                                    delete(tobj);
                                    for i = 1:length(counts)
                                        x = [binEdges(i), binEdges(i), binEdges(i+1), binEdges(i+1)];
                                        y = [0, counts(i), counts(i), 0]./max(counts);
                                        fill(tAx, x, y - k, [0.2, 0.4, 0.8], 'FaceAlpha',0.6, ...
                                            'LineWidth',.8, 'FaceColor',obj.CData(k,:),'EdgeColor',[1 1 1]*.1);
                                        plot(tAx, ([min(tX), max(tX)] - mean(tX)).*2 + mean(tX), [-k, -k], 'LineWidth',1, 'Color','k')
                                    end
                                    tAx.YLim = [-k, 0];
                                    tAx.XLim = [min(tX), max(tX)];
                            end

                        else


                            if (~isempty(obj.Y)) || m > n
                                tType = obj.LowerType;
                            else
                                tType = obj.UpperType;
                            end
                            switch tType
                                case {'none', 0}
                                    XLimUpdate(m, n) = false;
                                    YLimUpdate(m, n) = false;
                                    set(tAx, 'XColor','none','YColor','none')
                                case {'scatter', 1}
                                    scatter(tAx, tXk, tYk, 50, 'filled', ...
                                        'CData',obj.CData(k,:), 'MarkerFaceAlpha',.6);
                                case {'contour', 2}
                                    tNum = min(max(50, round(sqrt(obj.S)))*2, 200);
                                    gridx1 = linspace(min(tXk), max(tXk), tNum); gridx1 = (gridx1 - mean(gridx1))*2 + mean(gridx1);
                                    gridx2 = linspace(min(tYk), max(tYk), tNum); gridx2 = (gridx2 - mean(gridx2))*2 + mean(gridx2);
                                    [x1, x2] = meshgrid(gridx1, gridx2); sz = size(x1);
                                    xi = [x1(:), x2(:)]; h = ksdensity([tXk, tYk], xi);
                                    contour(tAx, x1, x2, reshape(h, sz), 'LineWidth',1.2, 'EdgeColor',obj.CData(k,:));
                                    tAx.XLim = [min(tX), max(tX)]; tAx.YLim = [min(tY), max(tY)];
                                case {'pred',3}
                                    [p, tS] = polyfit(tXk, tYk, 1);
                                    x_fit = linspace(min(tXk), max(tXk), 100);
                                    [y_fit, delta] = polyval(p, x_fit, tS);
                                    uy = y_fit + 2.*delta;
                                    dy = y_fit - 2.*delta;
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:),'MarkerFaceAlpha',.6);
                                    plot(tAx, x_fit, y_fit, 'Color',obj.CData(k,:), 'LineWidth',2.5)
                                    fill(tAx, [x_fit, x_fit(end:-1:1)], [uy, dy(end:-1:1)], obj.CData(k,:), 'EdgeColor','none', 'FaceAlpha',.15)
                                case {'convhull', 4}
                                    [ind, ~]=convhull([tXk, tYk]);
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:), 'MarkerFaceAlpha',.9);
                                    fill(tAx, tXk(ind), tYk(ind), obj.CData(k,:), 'FaceAlpha',.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',2)
                                case {'cover', 5}
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:), 'MarkerFaceAlpha',.9);
                                    ratio = max(max(tXk) - min(tXk), max(tXk) - min(tXk))./round(sqrt(size(tXk, 1)));
                                    tt = linspace(0,2*pi,100);
                                    [ind,~] = convhull([tXk, tYk]);
                                    kX = repmat(tXk(ind), [1, 100]) + repmat(cos(tt).*ratio, [length(ind), 1]);
                                    kY = repmat(tYk(ind), [1, 100]) + repmat(sin(tt).*ratio, [length(ind), 1]);
                                    kX = kX(:); kY = kY(:);
                                    [ind, ~] = convhull([kX, kY]);
                                    fill(tAx, kX(ind), kY(ind), obj.CData(k,:), 'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',2)
                                case {'ellipse', 6}
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:), 'MarkerFaceAlpha',.9);
                                    Mu = mean([tXk, tYk]);
                                    YY = [tXk, tYk] - repmat(Mu, size(tXk,1), 1);
                                    Sigma = cov(YY);% Sigma=(Y'*(ones(size(pntSet,1),1).*Y))./(size(pntSet,1)-1);
                                    [XX, YY] = getEllipse(Mu, Sigma, 9.21, 100);
                                    fill(tAx, XX, YY, obj.CData(k,:), 'FaceAlpha',0.3, 'EdgeColor',obj.CData(k,:), 'LineWidth',2)
                                case {'centroid', 7}
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:), 'MarkerFaceAlpha',.9);
                                    Mu = mean([tXk, tYk]);
                                    LX = [tXk, Mu(1).*ones(size(tXk, 1), 1), nan(size(tXk, 1), 1)].';
                                    LY = [tYk, Mu(2).*ones(size(tYk, 1), 1), nan(size(tYk, 1), 1)].';
                                    plot(tAx, LX(:), LY(:), 'Color',[obj.CData(k,:),.4], 'LineWidth',1.2);
                                case {'errorbar', 8}
                                    Mu = mean([tXk, tYk]);
                                    SX = std(tXk);
                                    SY = std(tYk);
                                    errorbar(tAx, Mu(1), Mu(2), SY, SY, SX, SX, 'o', 'LineWidth',1.5, 'Color',obj.CData(k,:), 'MarkerSize',10);
                                case {'conf',9}
                                    mdl = fitlm(tXk, tYk);
                                    x_fit = linspace(min(tXk), max(tXk), 100).';
                                    [y_fit, ci] = predict(mdl, x_fit, 'Alpha',.05);
                                    scatter(tAx, tXk, tYk, 30, 'filled', ...
                                        'CData',obj.CData(k,:),'MarkerFaceAlpha',.6);
                                    plot(tAx, x_fit, y_fit, 'Color',obj.CData(k,:), 'LineWidth',2.5)
                                    fill(tAx, [x_fit; flipud(x_fit)], [ci(:,2); flipud(ci(:,1))], obj.CData(k,:), 'EdgeColor','none', 'FaceAlpha',.15);
                                case {'dot',10}
                                    plot(tAx, tXk, tYk, ...
                                        'Color',obj.CData(k,:), 'Marker','.', 'LineStyle','none', 'MarkerSize',9);
                                case {'corr', 11}
                                    XLimUpdate(m, n) = false;
                                    YLimUpdate(m, n) = false;
                                    tAx.XLim = [-1, 1];
                                    tAx.YLim = [-obj.GroupNum - .5, .5];
                                    tAx.XTick = [];
                                    tAx.YTick = [];
                                    if obj.GroupNum == 1
                                        [rho, pval] = corr(tX, tY);
                                        marker = obj.pval2stars(pval, obj.pvalLevels);
                                        text(tAx, 0, 0, ['Corr:',num2str(rho), marker], 'FontSize',13, ...
                                            'FontName','Times New Roman', 'Color','k','HorizontalAlignment','center')

                                    else
                                        [rho, pval] = corr(tXk, tYk);
                                        marker = obj.pval2stars(pval, obj.pvalLevels);
                                        
                                        text(tAx, 0, -k, [obj.GroupLabel{k},':',num2str(rho), marker], 'FontSize',13, ...
                                            'FontName','Times New Roman', 'Color',obj.CData(k,:),'HorizontalAlignment','center')
                                        if k == obj.GroupNum
                                            [rho, pval] = corr(tX, tY);
                                            marker = obj.pval2stars(pval, obj.pvalLevels);
                                            text(tAx, 0, 0, ['Corr:',num2str(rho), marker], 'FontSize',13, ...
                                                'FontName','Times New Roman', 'Color','k','HorizontalAlignment','center')
                                        end
                                    end
                                case {'heatmap', 12}
                                    XLimUpdate(m, n) = false;
                                    YLimUpdate(m, n) = false;
                                    tAx.XLim = [-1, 1];
                                    tAx.YLim = [-obj.GroupNum - .5, .5];
                                    tAx.XTick = [];
                                    tAx.YTick = [];

                                    if obj.GroupNum == 1
                                        [rho, pval] = corr(tX, tY);
                                        marker = obj.pval2stars(pval, obj.pvalLevels);
                                        text(tAx, 0, 0, ['Corr:',num2str(rho), marker], 'FontSize',13, ...
                                            'FontName','Times New Roman', 'Color','k','HorizontalAlignment','center')

                                    else
                                        [rho, pval] = corr(tXk, tYk);
                                        marker = obj.pval2stars(pval, obj.pvalLevels);
                                        color = obj.val2Color(rho, obj.corrCLim, obj.corrColormap);
                                        fill(tAx, [-1,1,1,-1], [-1,-1,1,1]./2-k, color, 'EdgeColor','k','LineWidth',1)
                                        text(tAx, 0, -k, [obj.GroupLabel{k},':',num2str(rho), marker], 'FontSize',13, ...
                                            'FontName','Times New Roman', 'Color', [1,1,1].*(mean(color) < .5),'HorizontalAlignment','center')
                                        if k == obj.GroupNum
                                            [rho, pval] = corr(tX, tY);
                                            marker = obj.pval2stars(pval, obj.pvalLevels);
                                            color = obj.val2Color(rho, obj.corrCLim, obj.corrColormap);
                                            fill(tAx, [-1,1,1,-1], [-1,-1,1,1]./2, color, 'EdgeColor','k','LineWidth',1)
                                            text(tAx, 0, 0, ['Corr:',num2str(rho), marker], 'FontSize',13, ...
                                                'FontName','Times New Roman', 'Color', [1,1,1].*(mean(color) < .5),'HorizontalAlignment','center')
                                        end
                                    end
                                case {'contourf', 13}
                                    tNum = min(max(50, round(sqrt(obj.S)))*2, 200);
                                    gridx1 = linspace(min(tXk), max(tXk), tNum); gridx1 = (gridx1 - mean(gridx1))*2 + mean(gridx1);
                                    gridx2 = linspace(min(tYk), max(tYk), tNum); gridx2 = (gridx2 - mean(gridx2))*2 + mean(gridx2);
                                    [x1, x2] = meshgrid(gridx1, gridx2); sz = size(x1);
                                    xi = [x1(:), x2(:)]; h = ksdensity([tXk, tYk], xi);
                                    contour(tAx, x1, x2, reshape(h, sz), 'LineWidth',1.2, 'EdgeColor',obj.CData(k,:), 'UserData',k);
                                    tAx.XLim = [min(tX), max(tX)]; tAx.YLim = [min(tY), max(tY)];
                                    tobj = findobj(tAx, 'UserData', k);
                                    tZ = tobj.ZData; tLL = [min(min(tZ)), tobj.LevelList];
                                    tZ = reshape(tLL(sum(tZ(:) >= tLL, 2)), size(tZ));
                                    tZ = (tZ - min(min(tZ)))./(max(max(tZ)) - min(min(tZ)));
                                    surf(tAx, tobj.XData, tobj.YData, tobj.XData.*0, 'FaceColor',obj.CData(k,:),...
                                        'AlphaData',tZ,'EdgeColor','none','FaceAlpha','interp');
                                case {'cmp-contour', 14}
                                    if k == obj.GroupNum
                                        tNum = min(max(50, round(sqrt(obj.S)))*2, 200);
                                        gridx1 = linspace(min(tX), max(tX), tNum); gridx1 = (gridx1 - mean(gridx1))*2 + mean(gridx1);
                                        gridx2 = linspace(min(tY), max(tY), tNum); gridx2 = (gridx2 - mean(gridx2))*2 + mean(gridx2);
                                        [x1, x2] = meshgrid(gridx1, gridx2); sz = size(x1);
                                        xi = [x1(:), x2(:)]; h = ksdensity([tX, tY], xi);
                                        contourf(tAx, x1, x2, reshape(h, sz), 'LineWidth',1.2, 'EdgeColor','k');
                                        tAx.XLim = [min(tX), max(tX)]; tAx.YLim = [min(tY), max(tY)];
                                        colormap(tAx, obj.Colormap)
                                    end
                                case {'cmp-scatter', 15}
                                    if k == obj.GroupNum
                                        tNum = min(max(50, round(sqrt(obj.S)))*2, 200);
                                        gridx1 = linspace(min(tX), max(tX), tNum); gridx1 = (gridx1 - mean(gridx1))*2 + mean(gridx1);
                                        gridx2 = linspace(min(tY), max(tY), tNum); gridx2 = (gridx2 - mean(gridx2))*2 + mean(gridx2);
                                        [x1, x2] = meshgrid(gridx1, gridx2); sz = size(x1);
                                        xi = [x1(:), x2(:)]; h = ksdensity([tX, tY], xi); gridz = reshape(h, sz);
                                        hh = interp2(x1, x2, gridz, tX, tY);
                                        scatter(tAx, tX,tY, 'filled','CData',hh);
                                        tAx.XLim = [min(tX), max(tX)]; tAx.YLim = [min(tY), max(tY)];
                                        colormap(tAx, obj.Colormap)
                                    end
                            end
                        end
                    end
                    tAx.XLabel.FontSize = 17;
                    tAx.YLabel.FontSize = 17;
                end
            end
            for m = 1:obj.R
                for n = 1:obj.C
                    if XLimUpdate(m, n)
                        XLimSet(n, 1) = min(XLimSet(n, 1), obj.axMat{m, n}.XLim(1));
                        XLimSet(n, 2) = max(XLimSet(n, 2), obj.axMat{m, n}.XLim(2));
                    end
                    if YLimUpdate(m, n)
                        YLimSet(m, 1) = min(YLimSet(m, 1), obj.axMat{m, n}.YLim(1));
                        YLimSet(m, 2) = max(YLimSet(m, 2), obj.axMat{m, n}.YLim(2));
                    end
                end
            end

            for m = 1:obj.R
                for n = 1:obj.C
                    if XLimUpdate(m, n)
                        obj.axMat{m, n}.XLim = XLimSet(n, :);
                    end
                    if YLimUpdate(m, n)
                        obj.axMat{m, n}.YLim = YLimSet(m, :);
                    end
                end
            end

            function [X, Y] = getEllipse(Mu, Sigma, S, pntNum)
                % 95%:5.991 | 99%:9.21 | 90%:4.605
                % (X - Mu)*inv(Sigma)*(X - Mu) = S

                invSig = inv(Sigma);

                [V, D] = eig(invSig);
                aa = sqrt(S/D(1));
                bb = sqrt(S/D(4));

                t = linspace(0, 2*pi, pntNum);
                XY = V*[aa*cos(t); bb*sin(t)];
                X = (XY(1,:) + Mu(1))';
                Y = (XY(2,:) + Mu(2))';
            end
        end
        
        function setAxes(obj, varargin)
            for m = 1:obj.R
                for n = 1:obj.C
                    set(obj.axMat{m, n}, varargin{:})
                end
            end
        end

        function setXLabel(obj, varargin)
            for j = 1:obj.C
                set(obj.axMat{obj.R, j}.XLabel, varargin{:});
            end
        end

        function setYLabel(obj, varargin)
            for i = 1:obj.R
                set(obj.axMat{i, 1}.YLabel, varargin{:});
            end
        end

        function setXLabelString(obj, strCell)
            for j = 1:min(obj.C, length(strCell))
                obj.axMat{obj.R, j}.XLabel.String = strCell{j};
            end
        end
        function setYLabelString(obj, strCell)
            for i = 1:min(obj.R, length(strCell))
                obj.axMat{i, 1}.YLabel.String = strCell{i};
            end
        end

        function stars = pval2stars(~, pval, levels)
            % pval2stars - Convert p-values to significance stars
            %   stars = obj.pval2stars(pval) returns significance stars:
            %       p < 0.05   -> '*'
            %       p < 0.01   -> '**'
            %       p < 0.001  -> '***'
            %
            %   stars = obj.pval2stars(pval, levels) custom significance thresholds
            %       levels = [0.05, 0.01, 0.001] (default)
            %
            % Examples:
            %   obj.pval2stars(0.03)   % returns '*'
            %   obj.pval2stars(0.003)  % returns '***'

            if nargin < 2
                levels = [0.05, 0.01, 0.001];
            end

            % Generate asterisk string based on significance level
            stars = repmat('*', 1, sum(pval < levels));
        end

        function C = val2Color(~, val, clim, cmap)
            % Create bin edges for quantizing data values to color
            % (创建分箱边界,将数据值量化到颜色)
            values = linspace(clim(1), clim(2), size(cmap, 1) + 1);
            tind = sum(val >= values);
            tind(tind <= 0) = 1;
            tind(tind > size(cmap, 1)) = size(cmap, 1);
            C = cmap(tind, :);
        end
    end
end