matlab 奇怪的 xtick/ytick 标签行为

matlab's odd xtick/ytick label behavior

我偶然发现了 matlab 的 xticklabels 和 yticklabels 行为的不直观行为。我将 xticklabels 的输出存储到一个变量中,对其进行修改,然后再次应用它,并注意到标签现在已移动。这是预期的行为吗,如果是的话为什么(可选:为什么将此作为预期的行为有意义)?

想法:每一步都显示 xticks,但每第二步只显示标签。

%setup a figure to display
figure;
data=randn(1,21);
plot(-10:10,data);
xlim([-5 10]) %cut out a piece of interest from the data
xticks(-10:2:10); %adjust tick spacing to favorite

%now: keep xticks but remove every 2nd label
lbls=xticklabels;
lbls(1:2:end)={' '}; %set every 2nd label to empty space
pause(1); %take a deep breath...
xticklabels(lbls)

问题在于,虽然 xtick 函数 returns 所有现有刻度(可见和隐藏)的值,但 xticklabels 函数仅 returns可见刻度的标签。我猜这种行为是由于 MATLAB 没有为不可见的刻度分配任何标签。 因此,您的 xtick 向量是:

[-10 -8 -6 -4 -2 0 2 4 6 8 10]

而您的 xticklabels 元胞数组是:

{'-4'}, {'-2'}, {'0'}, {'2'}, {'4'}, {'6'}, {'8'}, {'10'}

并且,替换后:

{' '}, {'-2'}, {' '}, {'2'}, {' '}, {'6'}, {' '}, {'10'}

由于您的图片中唯一可见的刻度是分配给范围从-4到10的刻度,因此显示的相应标签是元胞数组中从第四个到最后一个的元素。没有分配标签的刻度(因为标签比刻度少)以空单元格为特征:

{'2'}, {' '}, {'6'}, {' '}, {'10'}, {empty}, {empty}, {empty}

解决方法: 你有多种解决方案。第一个是删除不在 x 轴范围内的刻度:

%setup a figure to display
figure;
data=randn(1,21);
plot(-10:10,data);
xlim([-5 10]) %cut out a piece of interest from the data
xticks(-10:2:10);  %adjust tick spacing to favorite

% Delete external ticks
xt = xticks();
xl = xlim(); xt(xt<xl(1)) = []; xt(xt>xl(2)) = []; xticks(xt);

%now: keep xticks but remove every 2nd label
lbls=xticklabels;
lbls(1:2:end)={' '}; %set every 2nd label to empty space
pause(1); %take a deep breath...
xticklabels(lbls)

Result(1)

另一个解决方案是手动分配标签,将 xticks 返回的数字转换为字符串值:

%setup a figure to display
figure;
data=randn(1,21);
plot(-10:10,data);
xlim([-5 10]) %cut out a piece of interest from the data
xticks(-10:2:10);  %adjust tick spacing to favorite

%now: keep xticks but remove every 2nd label
lbls= arrayfun(@(x) num2str(x), xticks(), 'UniformOutput', false);
lbls(1:2:end)={' '}; %set every 2nd label to empty space
pause(1); %take a deep breath...
xticklabels(lbls)

Result(2)

您会注意到两个解决方案之间的刻度不同。这是由于 even/odd 个元素在两个向量之间不同,因此,不同的元素被替换为空格。

中所述,问题是由于 Matlab 在读取 (labels = xticklabels) 和指定它们 (xticklabels(labels)) 时对刻度标签的处理方式不同。但实际行为更有趣...

读取和指定刻度标签时的行为

这些是我测试后发现的。感觉比它应该的更复杂(也许我遗漏了一些可以使描述更简单的东西?)。

  1. 如果 'XTickLabelMode' 属性 的值为 'Auto',刻度标签是 自动 根据当前选择的滴答声。

    示例

    clf
    plot(1:8)
    xlim([2 7])
    xticks, xticklabels.'
    

    给予

    ans =
         2     3     4     5     6     7
    ans =
      1×6 cell array
        {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}
    

    即使手动设置轴刻度,此刻度行为也是相同的:

    clf
    plot(1:8)
    xticks(3:7)
    xticks, xticklabels.'
    

    给予

    ans =
     3     4     5     6     7
    ans =
      1×5 cell array
        {'3'}    {'4'}    {'5'}    {'6'}    {'7'}
    

    显然,行为会受到要处理的图形事件分组的影响:

    clf
    plot(1:8)
    xlim([2 7])
    drawnow % force axis creation before setting 'XTickLabelMode'
    set(gca, 'XTickLabelMode', 'manual')
    xticklabels.'
    

    给出与上面一致的结果:

    ans =
      1×6 cell array
        {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}
    

    而删除 drawnow 会产生不同的结果:

    clf
    plot(1:8)
    xlim([2 7])
    set(gca, 'XTickLabelMode', 'manual')
    xticklabels.'
    

    给予

    ans =
      0×0 empty char array
    

    可能的解释是,如果没有 drawnow,两个图形事件(轴创建和设置 属性)都是一次性处理的,这会以某种方式影响结果。

  2. 如果 'XTickLabelMode' 属性 的值为 'Manual',则按原样读取刻度标签 returns 标签 由用户设置 (更多信息请参见下面的 (3) 和 (4)),这可能与当前在图表上看到的标签不一致。所以现在返回的结果可能与当前轴限制不匹配。

    使用 xticklabels(...)set(gca, 'xticklabels', ...) 隐式 手动设置刻度标签会导致 'XTickLabelMode' 属性 具有值'Manual'。此外,此 属性 可能 显式 设置为 'Manual'set(gca, 'XTickLabelMode', 'Manual')

    示例

    clf
    plot(1:7)
    xticklabels({'a' 'b' 'c'})
    xlim([2 6])
    get(gca, 'XTickLabelMode')
    xticklabels.'
    

    给予

    ans =
        'manual'
    ans =
      1×7 cell array
        {'a'}    {'b'}    {'c'}    {0×0 char}    {0×0 char}    {0×0 char}    {0×0 char}
    
    

    绘图显示了这些刻度标签:

  3. 手动设置刻度标签时,它们被解释为参考当前轴刻度,无论当前轴限制,因此无论有多少轴刻度实际可见。

    示例:在上图中,请注意第一个标签 ('a') 是如何不可见的。这是因为轴刻度是(默认情况下)[1 2 3 4 5 6 7]:

    clf
    plot(1:7)
    xticklabels({'a' 'b' 'c'})
    xlim([2 6])
    xticks
    ans =
         1     2     3     4     5     6     7
    

    第一个标签指的是第一个刻度,它在 xlim 设置的轴限制之外,因此看不到。

  4. 使用xticklabels(...)手动设置刻度标签时,如果提供的标签数量少于刻度数量,则缺少的标签最后被假定为 空字符串 .

    示例:在上图中,注意'c'以外的标签是空的。

  5. set(gca, 'xticklabels', ...)手动设置刻度标签时,如果提供的标签数量小于Matlab的刻度数量在显示的图表中循环重复使用它们以匹配刻度数。

    示例

    clf
    plot(1:7)
    xticks(1:7)
    set(gca, 'xticklabels', {'a' 'b' 'c'})
    xlim([2 6])
    xticklabels.'
    

    给出以下内容(另请注意,第一个标签因 (3) 而未显示):

    ans =
      1×3 cell array
        {'a'}    {'b'}    {'c'}
    

描述的行为有意义吗?

我认为是的。乍一看可能看起来很复杂,但它是一致的。

示例(刻度标签模式的隐式更改):

>> clf
plot(1:7)
xticks(0:7)
get(gca, 'XTickLabelMode'), xticklabels.'
disp('Pausing...'), pause % press key to continue
labels = xticklabels; % read current tick labels
xticklabels(labels) % this causes 'XLabelMode' to take the value 'Manual'
get(gca, 'XTickLabelMode'), xticklabels.'

给予

ans =
    'auto'
ans =
  1×7 cell array
    {'1'}    {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}
Pausing...
ans =
    'manual'
ans =
  1×8 cell array
    {'1'}    {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}    {0×0 char}

暂停前,刻度标签处于 'Auto' 模式,因此它们是根据当前可见的刻度自动选择的。

暂停后,代码读取轴刻度标签,写回刚刚读取的内容并再次读取;这给出了与暂停前的读数不同的结果。原因是两个读数之间对刻度标签的解释已从 (1) 更改为 (2)。但是没有歧义,因为轴的 'XTickLabelMode' 属性 已经从 'Auto' 变成了 'Manual' 来反映。

在此示例中,'XTickLabelMode' 的更改已由用户手动设置刻度标签隐式触发。如果 属性 明确更改为 'Manual' 会怎样?结果还是一致的,如下图。

示例(刻度标签模式的显式更改):

clf
plot(1:7)
xticks(0:7)
get(gca, 'XTickLabelMode'), xticklabels.'
disp('Pausing...'), pause % press key to continue
set(gca, 'XTickLabelMode', 'Manual')
get(gca, 'XTickLabelMode'), xticklabels.'

给予

ans =
    'auto'
ans =
  1×7 cell array
    {'1'}    {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}
Pausing...
ans =
    'manual'
ans =
  1×7 cell array
    {'1'}    {'2'}    {'3'}    {'4'}    {'5'}    {'6'}    {'7'}

暂停前的情节在 'Auto' 模式下有刻度标签,它们按预期工作。

暂停后的情节在 'Manual' 模式下有刻度标签。刻度标签与以前相同,但在这种模式下,它们适用于所有刻度,甚至是那些超出轴限制的刻度。所以有一个刻度标签比需要的少,Matlab 根据上面的(5)重用最后一个刻度的第一个标签:

  • 之前:

  • 之后:

因此检查 'XTickLabelMode' 属性 是 有效的 方法来确定读取的刻度标签是否应根据 (1) 或 ( 2).