Matlab的坐标轴刻度标签存在奇怪的行为

4
我发现了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)

enter image description here enter image description here

2个回答

4
问题在于,虽然xtick函数返回所有存在的刻度值(可见和隐藏的刻度),但xticklabels函数仅返回可见刻度的标签。我猜测这种行为是因为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)

结果(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)

结果(2)

您会注意到两个解决方案之间的勾选标记不同。这是由于两个向量的偶数/奇数元素不同,因此,不同的元素被替换为空格。


有什么想法为什么xticklabels只返回显示的xticks,但是在设置时,它假定您想指定所有可见和不可见的刻度线? - user2305193
1
不错的答案!然而,并不总是正确说“xticklabel函数只返回可见刻度的标签”。它似乎要复杂一些;请参考我的回答。 - Luis Mendo

4
根据 Gianfranco Di Domenico 的答案所述,这个问题是由于Matlab在读取刻度标签时(labels = xticklabels)和指定刻度标签时(xticklabels(labels))以不同的方式处理它们所导致的。但实际行为有点更有趣...。

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

经过一些测试,这是我发现的情况。它感觉比应该的要复杂(也许我错过了某些简化描述的东西?)。
  1. If the 'XTickLabelMode' property has the value 'Auto', tick labels are automatically chosen based on the current ticks.

    Example:

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

    gives

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

    This tick behaviour is the same even even if the axis ticks are set manually:

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

    gives

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

    Apparently, the behaviour can be affected by the grouping of graphics events to be processed:

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

    gives a result consistent with the above:

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

    whereas removing drawnow produces a different result:

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

    gives

    ans =
      0×0 empty char array
    

    The likely explanation is that, without drawnow, both graphics events (axis creation and setting the property) are processed in one go and that somehow affects the result.

  2. If the 'XTickLabelMode' property has the value 'Manual', reading the tick labels returns the labels as they were set by the user (more on this in (3) and (4) below), which may not coincide with the labels that are currently seen on the graph. So now the returned result may not match the current axis limits.

    Setting the tick labels manually with xticklabels(...) or with set(gca, 'xticklabels', ...) implicitly causes the 'XTickLabelMode' property to have the value 'Manual'. Also, this property may be explicitly set to 'Manual' with set(gca, 'XTickLabelMode', 'Manual').

    Example:

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

    gives

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

    and the plot shows these tick labels:

    enter image description here

  3. When setting the tick labels manually they are interpreted as referring to the current axis ticks, regardless of the current axis limits, and thus regardless of how many of the axis ticks are actually visible.

    Example: in the above figure, note how the first label ('a') is not visible. This is because the axis ticks are (by default) [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
    

    The first label refers to the first tick, which is outside of the axis limits set by xlim, and thus it is not seen.

  4. When setting the tick labels manually with xticklabels(...), if the number of provided labels is less than the number of ticks, the missing labels at the end are assumed to be the empty string.

    Example: in the above figure, note how labels beyond 'c' are empty.

  5. When setting the tick labels manually with set(gca, 'xticklabels', ...), if the number of provided labels is less than the number of ticks Matlab cyclically reuses them in the displayed graph to match the number of ticks.

    Example:

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

    gives the following (note also that the first label is not seen because of (3)):

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

    enter image description here

描述的行为是否有意义?

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

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

>> 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}

在暂停之前,刻度标签处于“自动”模式,因此它们是根据当前可见的刻度自动选择的。
在暂停后,代码读取轴刻度标签,然后再次写回刚刚读取的内容并重新读取;这导致与暂停前的读取结果不同。原因在于,在两次读取之间,刻度标签的解释已从(1)更改为(2)。但是,没有歧义,因为轴的“XTickLabelMode”属性已从“自动”更改为“手动”,以反映这一点。
在本例中,“XTickLabelMode”的更改是由用户手动设置刻度标签隐式触发的。如果该属性被明确更改为“手动”会怎样呢?下面的结果仍然是一致的。
示例(刻度标签模式的显式更改):
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)重复使用第一个标签作为最后一个刻度标签:

  • 以前:

    enter image description here

  • 之后:

    enter image description here

因此检查'XTickLabelMode'属性是确定读取刻度标签应该按照(1)还是(2)解释的一种有效方法。


2
对我来说,显而易见的结论是始终同时设置刻度和刻度标签。这个行为非常奇怪,我想知道这是否是 HG2 的问题?我不记得在过去遇到过这种情况。 - Cris Luengo
1
这个答案涵盖了一个迷你博士学位... 这个不一致的漏洞可能比任何人想象的都要深。感谢您的工作,我认为它应该成为未记录的MATLAB博客文章,或直接报告为错误。参考资料:我使用的是MATLAB ver 2020B。 - user2305193
1
@user2305193 谢谢!这个问题很有趣,我今天花了很多时间进行测试。是的,看起来像是一个 bug。 - Luis Mendo
1
@user2305193 我刚刚把描述发送给了我在Mathworks认识的一位工程师,也许他可以从开发人员那里得到解释。我认为这可能比错误报告更好,因为目前还不完全清楚这是一个错误还是只是一个怪癖。 - Luis Mendo
2
@user2305193 我从Mathworks得到了一些反馈。根据他们的解释,我意识到我看到的不一致实际上并不存在(我在示例中选择了错误的值,这误导了我)。我已经编辑了答案。希望现在变得更清晰了。 - Luis Mendo
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接