How to define the (minimum) number of ticks with "ytick mode auto"

Hi, to emphasize possible nonlinearities in scaling I need to have a minimum number of ticks, more than two for positive data, or mor than three for data if zero is included.
I am aware of all the "official" properties, such as YLim, YTick, YTickMode, YTickLabel, etc.,
But how to get more than three ticks when data are centered around zero automatically?
Or, when using the manual mode, how to get "nice numbers" as it is done within the "auto" tick mode function? E.g., such that Zero is included, the data are "round" and symmetric?

13 Kommentare

hello
what is the problem ?
if you know how to use YLim, YTick, YTickMode, YTickLabel,... and data formatting you can do anything you want
do you have an example / code ?
Obviously I am not advanced enough 🙈
Here is an example code:
function ticks = niceTicks(minVal, maxVal, numTicks)
% Calculate the range
range = maxVal - minVal;
% Determine the tick spacing
tickSpacing = range / (numTicks - 1);
% Round the tick spacing to a "nice" number
niceSpacing = 10^floor(log10(tickSpacing));
if tickSpacing / niceSpacing >= 5
niceSpacing = 5 * niceSpacing;
elseif tickSpacing / niceSpacing >= 2.5
niceSpacing = 2.5 * niceSpacing;
elseif tickSpacing / niceSpacing >= 2
niceSpacing = 2 * niceSpacing;
elseif tickSpacing / niceSpacing >= 1.5
niceSpacing = 1.5 * niceSpacing;
end
% Generate nice ticks
startTick = floor(minVal / niceSpacing) * niceSpacing;
endTick = ceil(maxVal / niceSpacing) * niceSpacing;
ticks = startTick:niceSpacing:endTick;
% Ensure the ticks are within the specified range
ticks = ticks(ticks >= minVal & ticks <= maxVal);
end
Works in most cases, but not in all
it works here
Case 1:
ticks = niceTicks(-5, 5, 5)
ticks = 1×5
-5.0000 -2.5000 0 2.5000 5.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
ticks =
-5 -2.5 0 2.5 5
but not here
Case 2: With zero in the specified interval
ticks = niceTicks(-.17, .12, 5)
ticks = 1×6
-0.1500 -0.1000 -0.0500 0 0.0500 0.1000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
the output has 6 ticks.
Case 3: Without zero in the specified interval
ticks = niceTicks(.003, .12, 5)
ticks = 1×4
0.0250 0.0500 0.0750 0.1000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
the output has 4 ticks.
I had the hope, that there is some internal function, which does the job for me.
sorry but I am still unsure to fully understand what you want to do
if I try to sum up what I have understood so far (guessing somehow)
if data is strictly pos or neg : output must be at least two ticks with TBD rounding
if it includes zero then one more tick (zero obviously)
then the general case with pos and neg data : I assume you want the zero tick
+ how many pos ticks and how many neg ticks ?
with equal spacing or equal number of ticks ? , symetrically arranged ? in the last case you may have a different spacing in neg and pos ticks ...
Could you specify the desired ticks for Case 2 and Case 3? This will allow us to verify the formulas and functions used in your code.
what I want:
  • either a way of automatic ticks where I can define the number of ticks
  • or a "niceTicks" function where I provide min, max, numberTicks where numbers are "round" and if zero is included, zero must be part of the "niceTicks"
Why I need it: the "auto" function sometimes provide too few ticks (for my purposes), since I often use customized nonlinear scalings by transfoming x data and plot it to a linear scale. Then I either correct the TickLabel or keep the TickLabel and shift it to the apropriate position according to my customized scaling.
The Problem:
  • if data is strictly pos or neg I need more than two ticks to emphasise the use of nonlinear scaling
  • if the data range from neg to pos, "auto" nicely includes ZERO, but then three ticks are not enough to emphasize (zero centered) nonlinear scales, so I need more than tree ticks
just a side note
maybe this could also be of some interest : Symmetric Log Scale Plot - File Exchange - MATLAB Central
I'll come back later on your topic but I have to work a liitle bit for my boss right now
@Mathieu NOE this is somewhat, whar I am doing as well, but often unsing "gamma" with xScale = x.^gamma !
Here some lines of code to emphasise the problem:
%% data
x=0:.1:1;
y=2*x.^3;
xscale = x.^3; % nonlinear scale
The plot with nonlinear scale
%% plot1 good
hFig=figure;
hFig.Position(3:4)=200;
plot(xscale,y);
h_ax = gca;
h_ax.FontSize=20;
myXTicks = h_ax.XTick;
for n=1:numel(myXTicks)
h_ax.XTickLabel{n}=num2str(round(myXTicks(n).^(1/3),2));
end
By increasing FontSize MATLAB uses less ticks
%% plot2 bad
hFig=figure;
hFig.Position(3:4)=200;
plot(xscale,y);
h_ax = gca;
h_ax.FontSize=40; % forces XTickMode="auto" to use less ticks
myXTicks = h_ax.XTick;
for n=1:numel(myXTicks)
h_ax.XTickLabel{n}=num2str(round(myXTicks(n).^(1/3),2));
end
side note: I did set the XTickLabel within a loop since I failed to calculate the inverse of x.^3 which should be xScale.^(1/3) when using x=-1:.1:1; 🙈
-1.^(1/3) shows -1 as expected,
-1.^(1/3)
ans =
-1
but (-1:.5:1).^(1/3) just provides complex numbers
(-1:.5:1)'.^(1/3)
ans =
0.5 + 0.86603i
0.39685 + 0.68736i
0 + 0i
0.7937 + 0i
1 + 0i
a more or less correct solution, but not what I expected
-1.^(1/3) shows -1 as expected,
-1 is one of the cube roots of -1, but it's not the only one. What you want to compute there is one of the principal cube root:
principalCubeRoot = (-1).^(1/3)
principalCubeRoot = 0.5000 + 0.8660i
or the real cube root:
realCubeRoot = nthroot(-1, 3)
realCubeRoot = -1
or all the roots:
roots([1 0 0 1])
ans =
-1.0000 + 0.0000i 0.5000 + 0.8660i 0.5000 - 0.8660i
@Steven Lord many thanks for the hint about "nthroot".
What is the difference between
-1.^(1/3)
ans =
-1
and
(-1).^(1/3)
ans =
0.5 + 0.86603i
Is the first rather -(1.^(1/3))?
@Sam Chak desired ticks for Case 2 and Case 3:
good point, I realized for the set of apropriate spacings such as [5, 2.5, 2, 1.5, 1] a scale with the limits, e.g., ylim([-1.6, 1.6]),
  • a non-apropriate spacing would be [ -1 0 1 ], because it contains only three ticks and linearity of scaling could not be judged
  • the next would be [ -1.5 -1 -.5 0 .5 1 1.5 ], which has 7 ticks already
Means, there is no "nice" set of ticks with only 4 or 5 ticks 🙈
So, the function "niceTicks" @Stephen23 (and me) provided do almost perfectly, what I wanted. They only need some fine tuning.
Is the first rather -(1.^(1/3))?
Correct. The .^ operator is at operator precedence level 2, while the unary minus operator - is at precedence level 4. [*]
When you write (-1).^(1/3) MATLAB computes the values inside each set of parentheses first (precedence level 1) then applies .^ to the results.
The () around the exponent are also necessary, as division is level 5. So this:
2.^1/3
ans = 0.6667
is
(2.^1)/3
ans = 0.6667
rather than
2.^(1/3) % or
ans = 1.2599
nthroot(2, 3)
ans = 1.2599
[*] Level 3 involves both power and unary minus, but is intended for things like this where unary minus is in the exponent:
2^-2 % 2 to the -2 power or 2^(-2)
ans = 0.2500
Many thanks for clarifying!
Thanks to your explanation I found the full desciption of Operator Precedence in MATLAB.
Hi @Andre Zeug, Thanks for your updates. It is good to know that you have adopted @Stephen23's niceTicks(). If you find the solution helpful and satisfactory, please consider clicking 'Accept' ✔ on his answer, as well as voting for other helpful contributions by the others.

Melden Sie sich an, um zu kommentieren.

Antworten (1)

Axes do not expose a “minimum tick count” property. In auto mode the tick positions are chosen internally from the axis limits, the figure size and the font size; you cannot tell the axes to “use at least N ticks”. The only time you have full control over tick placement is when you switch the ruler into manual mode and set the TickValues/YTick vector yourself:
If you want a custom number of ticks with “nice” values you therefore have to compute the ticks and pass them to the axis. There is no built‑in function that does this automatically, but you can implement the common “nice number” algorithm. The idea is to compute a raw spacing equal to (maxVal–minVal)/(numTicks-1), round this to a “nice” value (multiples of 1, 2, 2.5, 5, … × 10^n), and then generate tick positions that include zero if it lies in the range. Below is one possible implementation that tries to hit your requested number of ticks but uses “nice” spacing. When zero is in the range an odd number of ticks gives a symmetrical tick at zero; even numbers are likely to require an extra tick.
function ticks = niceTicks(minVal,maxVal,numTicks)
if minVal==maxVal
ticks = minVal;
return
elseif minVal>maxVal
[minVal,maxVal] = deal(maxVal,minVal);
end
includeZero = (minVal <= 0 && maxVal >= 0);
% Raw spacing and exponent
rawStep = (maxVal-minVal)./(numTicks-1);
exponent = floor(log10(rawStep));
baseStep = 10.^exponent;
% Candidate multipliers for “nice” spacing
mult = [1,2,2.5,5,10];
bestDiff = Inf;
for m = mult
step = m*baseStep;
tMin = floor(minVal/step)*step;
tMax = ceil (maxVal/step)*step;
ticks = tMin:step:tMax;
if includeZero && ~ismember(0,ticks), ticks = sort([ticks 0]); end
ticks = ticks(ticks>=minVal & ticks<=maxVal);
difft = abs(numel(ticks) - numTicks);
if difft < bestDiff || (difft==bestDiff && numel(ticks)>=numTicks)
bestTicks = ticks;
bestDiff = difft;
end
end
ticks = bestTicks;
end
You can call this function and then set the axis ticks manually:
x = linspace(0,10,100);
y = sin(x);
numTicks = 5;
ticks = niceTicks(min(y),max(y),numTicks)
ticks = 1×7
-0.7500 -0.5000 -0.2500 0 0.2500 0.5000 0.7500
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
plot(x,y);
ylim([min(y) max(y)]);
set(gca,'YTick',ticks) % fix tick positions
For your examples:
niceTicks(-5,5,5)
ans = 1×5
-5.0000 -2.5000 0 2.5000 5.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
niceTicks(-0.17,0.12,5) % (six ticks because of zero)
ans = 1×6
-0.1500 -0.1000 -0.0500 0 0.0500 0.1000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
niceTicks(0.003,0.12,5)
ans = 1×6
0.0200 0.0400 0.0600 0.0800 0.1000 0.1200
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
This approach gives you “round” tick values and includes zero when it lies in the range. When the requested count cannot be achieved exactly with “nice” spacing, the function favours having more ticks rather than fewer so that the non‑linear scaling is visible. If you need an exact number of tick labels regardless of “niceness” you can fall back on a simple linspace(minVal,maxVal,numTicks) call and use sprintf or YTickLabelFormat to format the numbers.

2 Kommentare

amost PERFECT!
Your function uses ore ore less the same strategy as mine, but better!
However
niceTicks(-1.5, 1.5, 4)
ans =
-1 0 1
provides 3 instead of at least 4 niceTicks
I changed 2 lines in your function, but still it is a bit curious to me
function ticks = niceTicks(minVal, maxVal, numTicks)
if minVal==maxVal
ticks = minVal;
return
elseif minVal>maxVal
[minVal,maxVal] = deal(maxVal,minVal);
end
includeZero = (minVal <= 0 && maxVal >= 0);
% Raw spacing and exponent
rawStep = (maxVal-minVal)./(numTicks-1+includeZero);
exponent = floor(log10(rawStep));
baseStep = 10.^exponent;
% Candidate multipliers for “nice” spacing
mult = [1,2,2.5,5,10];
bestDiff = Inf;
for m = mult
step = m*baseStep;
tMin = floor(minVal/step)*step;
tMax = ceil (maxVal/step)*step;
ticks = tMin:step:tMax;
if includeZero && ~ismember(0,ticks), ticks = sort([ticks 0]); end
ticks = ticks(ticks>=minVal & ticks<=maxVal);
difft = abs(numel(ticks) - numTicks);
if numel(ticks) >= numTicks && (difft < bestDiff || (difft==bestDiff && numel(ticks)>=numTicks))
bestTicks = ticks;
bestDiff = difft;
end
end
ticks = bestTicks;
end
I chaned
rawStep = (maxVal-minVal)./(numTicks-1+includeZero);
and
if numel(ticks) >= numTicks && (difft < bestDiff || (difft==bestDiff && numel(ticks)>=numTicks))
but did not understand, what the "abs" in
difft = abs(numel(ticks) - numTicks);
is good for.
I have the feeling that your function could still be improved, because due to my changes some parts are unnecessary or redundant. But maybe I'm wrong.
I further refined your function. Now I think it does what MATLABs internal function does anyway + the minimum number of ticks can be controlled by the 3rd input parameter rather than by axe and font size.
function ticks = niceTicks(minVal, maxVal, numTicks)
% ticks = niceTicks(minVal, maxVal, numTicks)
%
% Generates a set of "nice", i.e., most round ticks for a given minimum
% number of values.
%
% Inputs:
% minVal: The minimum value of the range.
% maxVal: The maximum value of the range.
% numTicks: The minimum number of ticks.
%
% Outputs:
% ticks: A vector of "nice" tick values, which are evenly spaced
% within the specified range, with zero included if it falls within
% the range or zero would be included if the range would be
% extended.
% So it returns [-.2 0 .2 ...] rather than [-.1 .1 .3 ...],
% or [1.2 1.4 1.6 ...] rather than [1.1 1.3 1.5 ...].
if minVal == maxVal
ticks = minVal;
return;
elseif minVal > maxVal
[minVal, maxVal] = deal(maxVal, minVal);
end
% Include zero if it lies within the range
includeZero = (minVal <= 0 && maxVal >= 0);
% Calculate raw step and exponent
rawStep = (maxVal - minVal) / (numTicks - 1 + includeZero);
exponent = floor(log10(rawStep));
baseStep = 10^exponent;
% Candidate multipliers for "nice" spacing
mult = [1, 2, 2.5, 5, 10];
bestTicks = [];
bestDiff = Inf;
% Calculate ticks according to multipliers
for m = mult
step = m * baseStep;
tMin = floor(minVal / step) * step;
tMax = ceil(maxVal / step) * step;
ticks = tMin:step:tMax;
ticks = ticks(-eps < ticks-minVal & ticks-maxVal < eps);
% ticks = ticks(ticks >= minVal & ticks <= maxVal);
% does not work for, e.g., niceTicks(-0.1, 0.3, 3) !!!
difft = numel(ticks) - numTicks;
if numel(ticks) >= numTicks && difft < bestDiff
bestTicks = ticks;
bestDiff = difft;
else
break
end
end
ticks = bestTicks;
end

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu Just for fun finden Sie in Hilfe-Center und File Exchange

Produkte

Version

R2024b

Gefragt:

am 4 Sep. 2025

Kommentiert:

am 6 Sep. 2025

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by