0

I recently stumbled upon some weird behavior when trying to generate several subplots with a logarithmic y-scale. The subplots are constructed in a loop so that each subplot is generated exactly the same way, still, the Y-scale differs between the various subplots even though they all have the same limits and tick definition, which does not make any sense to me. How can I make sure that the Y-scale and ticks are equal in all subplots? Below is an example that illustrates the problem (MATLAB R2019a).

figure;
    for i=1:9
        ax = subplot(3,3,i);
        plot(rand(5));
        yticks([0:0.2:1]);
        ylim([0, 1]);
       set(ax, "YScale", "log");
    end

Output:

Inconsistent y-scale

Adriaan
  • 17,741
  • 7
  • 42
  • 75

1 Answers1

4

You're setting the lower ylim() to zero, which is undefined for logarithmic axis. I presume there's some internal magic of resetting limits when incompatible with the chosen axis representation. Probably MATLAB internally figures out that the lower limit doesn't work, and resets it to automatic before plotting. This is evidenced by the fact that all your plots have a lower limit on exactly the lowest data point in said plot, i.e. using MATLAB's default lower limit of min(data(:)).

When using 0.001, or any other non-zero positive number, as axis limit it does work:

figure;
for ii=1:9 % don't use i
    ax = subplot(3,3,ii);
    plot(rand(5));
    yticks(0:0.2:1); % square brackets are superfluous
    ylim([1e-3, 1]); % Lower limit to 0.001, not 0
    % set(ax, "YScale", "log");
    ax.YScale = 'log';  % Object oriented handling of properties is preferred nowadays
end

enter image description here


Side notes:

  • I'd recommend against using i and j as variables as they denote the imaginary unit and can lead to hard to debug errors.

  • Square brackets when creating an array using colon syntax, such as in your yticks(), are unnecessary. When creating an array by listing comma/space separated values, such as in ylim([]) it is necessary to use square brackets.

  • Figure/line/axis handles are nowadays object oriented in style and can be set using the dot-indexing, similar to structures. This can simplify your code by omitting set and get arguments.

Adriaan
  • 17,741
  • 7
  • 42
  • 75
  • 2
    Nice answer and comments. About simplifying code you could also mention the positive side of using `set`: you can set multiple properties in one instruction, Something like : `set(ax, 'YScale', 'log','Ylim',[1e-3, 1],'YTicks',(0:0.2:1));` would reduce 3 lines into one. I believe it is also marginally faster than using the dot notation. Unfortunately, the `get` command does not have the same benefit (retrieving multiple properties in one call). – Hoki May 05 '20 at 10:32
  • 1
    @Hoki speed might be a valid argument for me, putting everything on one line not. When initialising variables I also take one variable per line, even though I could do `a=zeros(3); b = zeros(6); c = zeros(4,3)` on a single line. I prefer the less obfuscated way. – Adriaan May 05 '20 at 11:24
  • I'm not sure that a single `set` is faster than setting each of the properties in turn, as the graph is not updated until a `redraw` is issued or the code ends. But it *might* be, if setting a property re-calculates some other property. – Cris Luengo May 05 '20 at 14:59
  • I quite agree with not obfuscating the code by craming too many things in one line. For an example of when `set` is useful: I often need to use `set` when I am changing `XData` and `YData` of a curve with new sets of different length (it avoids the anoying warning about `X` and `Y` not being the same length and the display not being rendered). – Hoki May 05 '20 at 15:39
  • Since you mention *Object oriented handling of properties is preferred nowadays*, shouldn't you use `ax.YTick` and `ax.YLim` ? – Paolo May 05 '20 at 15:48
  • @UnbearableLightness interesting, hadn't thought about that yet. I'd say no though; sorry for the ambiguity. Things that can be set through a single command, like limits and ticks, are fine IMO to leave without an axis call if that doesn't mess things up between (sub-)plots. I meant it for things which used to go with `set`, where `set` should nowadays be replaced by OO handling. – Adriaan May 06 '20 at 07:12