Skip to content

One one tick in a log-scale axis #8768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
szsdk opened this issue Jun 16, 2017 · 23 comments · Fixed by #18754
Closed

One one tick in a log-scale axis #8768

szsdk opened this issue Jun 16, 2017 · 23 comments · Fixed by #18754
Milestone

Comments

@szsdk
Copy link

szsdk commented Jun 16, 2017

Bug report

For the data in a small range, there is just on tick in the default log scale axis, which means nothing.

Code for reproduction

import matplotlib.pylab as plt
plt.loglog([ 0.53,  0.41],[ 0.53,  0.41] )
plt.show()

Actual outcome

image

Expected outcome
Give at least two ticks for each axis.

Matplotlib version

  • Operating System: Ubuntu 16.04
  • Matplotlib Version: 2.02
  • Python Version: 3.6.0
    I install the library by conda.
@afvincent
Copy link
Contributor

What supplementary tick value would you expect? 0.41? Or would you expect the ylim to be automatically extended to include 0.4 (for example)?

Currently, one simple workaround solution may be to simply use plt.xlim and plt.ylim to include 0.4, which results in a second tick being used.

By pure curiosity, why is there the need of a logarithmic plot on such a short (meaning << 1 decade) range? Is it related to a specific field of research?

@dstansby
Copy link
Member

An even worse case, where no tick labels are present:

import matplotlib.pylab as plt
plt.loglog([ 0.45,  0.46],[ 0.45,  0.46] )
plt.show()

@afvincent
Copy link
Contributor

@dstansby What would be the correct behavior in this case? I agree that no tick is a bad choice. But it also seems wrong to me to use a log scale on such a small range of values as it should be more or less linear anyway, shouldn't it? In your example, one wants to plot values log10(0.45 + x) with x in [0, 0.01] (i.e. x << 0.45) so

log10(0.45 + x) = log10(0.45) + log10(1 + x/0.45)  # log scale
                ~ log10(0.45) + x/0.45  # linear scale

Would you suggest the logarithmic locators to switch to a linear-like behavior for ranges where one or no tick at all would be displayed?

@efiring
Copy link
Member

efiring commented Jun 16, 2017

All of this is inherent in the way log locators and formatters work. @afvincent's suggestion is correct: the solution to this is an AutoLogLocator that switches to using a MaxNLocator when the range drops below a threshold, probably a little larger than the largest minor tick interval.

@szsdk
Copy link
Author

szsdk commented Jun 17, 2017

@afvincent The reason I want to use log-scale is that the theory shows that the data should behave the power law in this range. Indeed, there is no big difference between log-scale or linear-scale in this range.
Probably, the ticks could be

4.0e-1 4.5e-1 5.5e-1

Somehow, give readers a sense that this is not a linear scale axis.

@tacaswell tacaswell added this to the 2.2 (next next feature release) milestone Jun 17, 2017
@tacaswell
Copy link
Member

If I put on my physicist hat, I am deeply skeptical of power law claims at less than 3 decades (as a default position), but with my Matplotlib hat on we probably should make sure we do not create plots with too few ticks to be useful.

For now @szsdk I suggest manually setting the ticks.

@danijar
Copy link

danijar commented Jul 25, 2017

Running into the same problem with much more data points. Setting limits manually is hard since I only want to include the previous and next tick and not more.

@stevennoyce
Copy link

stevennoyce commented Apr 18, 2018

Although it may not always make sense to plot on a log scale over a small range, it can become necessary for some applications. I have been building a matplotlib backend to a test and measurement system. The expected data spans several decades and is best suited for a log scale. By nature of doing research and measuring devices with properties that are not known in advance, however, there will be some devices that do not function properly and produce data spanning only a small range. It does not make much sense to plot on a linear scale because I want to compare poorly functioning devices to those that function well.

I really appreciate that this was added to a milestone, but I noticed that milestone has been deleted. I upgraded to 2.2.2 and the issue persists, so I just wanted to check: what is the status on this? Is it in the plans? Is anyone working on it?

Oh, and another simple code block to reproduce this is:

from matplotlib import pyplot as plt
plt.semilogy([0,1,2],[0.31,0.32,0.33])
plt.show()

Thanks all!

@rudrathegreat
Copy link

Another way is to set the x and y scales by simply -

fig ,ax = plt.subplots(figsize=(12, 6)) # Create figure and subplot

ax.set_xlim(0, 255)
ax.set_ylim(0, 255)

Or you can create a single dimensional array using NumPy, especially a linspace.

np.linspace allows you to create a single dimensional array with values ranging from a start number, x, and an end number, y, all equally spaced out. You can set the length of the array by setting how many 'ticks' I want in between those those two numbers. You can try this yourself by simply -

import numpy as np

x = np.linspace(1, 10, 5)
print(x)

You can use this on the x/y-scale as follows -

fig, ax = plt.subplots(figsize=(12, 6))

x = np.linspace(0, 10**6, 10**3)

y, = ax.semilogx(x)
ax.set_ylim(1000)

ax.plot([10**1, 10**2, 10**3], [10**1, 10**2, 10**3])
plt.show()

I hope that answered your issue!

Requirements

  • Python 3.7
  • Matplotlib 2.2.3
  • NumPy 1.10.0

@anntzer
Copy link
Contributor

anntzer commented Jan 7, 2019

Closed by #12865.

@anntzer anntzer closed this as completed Jan 7, 2019
@tacaswell tacaswell modified the milestones: needs sorting, v3.1 Jan 12, 2019
@aggna
Copy link

aggna commented Oct 16, 2020

Still getting this issue. Using python 3.6.8, matplotlib 3.1.1 on Windows 10.

plt.figure(figsize=(6,2))
plt.semilogy(np.arange(6),[9, 5e1, 1e2, 6e2, 9e2, 2e3])

generates the following output.
tickeg

For me, the ideal case would be to have minor ticks in the above plot. But when I tried to force-add them with the following three methods, none worked:

  1. ax.tick_params(axis='y', colors='k',which = 'both')

  2. ax.yaxis.set_minor_locator(ticker.AutoMinorLocator())

ax.minorticks_on()
ax.xaxis.set_tick_params(which='minor', bottom=False)

For now, I just artificially imposed ticks by increasing the ylim:
plt.ylim(1,1e4)
which gives the second example.
tickeg2

@jklymak
Copy link
Member

jklymak commented Oct 16, 2020

You must have things set in your rcParams that are causing this, or we fixed something: I get:

image

@tacaswell
Copy link
Member

tacaswell commented Oct 16, 2020

if you make the window small enough you you can get it to go to 1 tick (I think it is font dependent). For me if I make it ~1.2 in tall I can get it to drop to one tick.

@tacaswell
Copy link
Member

The really upsetting part is that the ticks are in-range just not shown!

womp_womp

@tacaswell
Copy link
Member

ax.yaxis.get_major_locator().numticks = 4 makes it behave better for me locally.

@tacaswell
Copy link
Member

and if you set ax.yaxis.get_major_locator().numticks = 3 it is broken independent of figure size (the small figure here is needed to trigger a small enough number out of ax.yaxis.get_tick_space() and it looks like @aggna is using latex for the tick labels which places the exponent a bit higher than the defaults so they triggered the problem with a slightly bigger figure than @jklymak and I did.

I'm trying to puzzle through the LogLocator code to sort out what is going wrong...

@tacaswell
Copy link
Member

ok, got this sorted, PR coming.

@aggna
Copy link

aggna commented Oct 16, 2020

Thanks for looking into this so promptly!

You must have things set in your rcParams that are causing this, or we fixed something

That's right. I am using slightly bigger font in rcParams.

size=14
plt.rcParams.update({'font.size': size})
plt.rcParams.update({'font.family':'Times New Roman'})

In the MWE there's no latex involvement, but perhaps its auto-enabled in my matplotlib (I do use math mode for axis labels etc, so it probably is).

The really upsetting part is that the ticks are in-range just not shown!

I completely agree. As can be seen from the numbers in the MWE, its 2 orders of magnitude and deserves at least minor ticks (without labels) if there isn't space for the major tick labels.

@tacaswell
Copy link
Member

The underlying source of the bug is when we ask for very small numbers of ticks we can end up deciding the best stride between the log ticks is equal to the approximate range of your data which means unless you set the limits just right you'll only see one. In your case we are trying to tick at (10^0, 10^2, 10^4) which given our heuristic of trying to keep ticks "spread out" is sensible.

My proposed fix is to make sure the stride is always less than the estimated range which leads to crunched tick labels, but that seems like a better trade off to make than showing only one tick.

tacaswell added a commit to tacaswell/matplotlib that referenced this issue Oct 16, 2020
If we have both a small target number of ticks and a small displayed
range we would previously pick a tick stride that was the same size or
bigger than the visible data range which would result in only 1 tick
being visible on the axis.

This patch ensures that, with a floor at 1, the stride is smaller than
the estimated data range.

re-closes matplotlib#8768  (same symptoms, different cause)
@aggna
Copy link

aggna commented Oct 17, 2020

Thanks!

ax.yaxis.get_major_locator().numticks = 4 worked well for me too.

By any chance, do you know how I'd turn on the minor ticks?

@dmylnikov
Copy link

I'm still able to get 1 tick axis with the following code using matplotlib 3.3.4:
plt.semilogy([1.5, 50])
2021-07-30 02_02_40-Figure 1

@jklymak
Copy link
Member

jklymak commented Jul 29, 2021

I think that is expected. The problem before was there were no minor ticks...

@doronbehar
Copy link
Contributor

@dmylnikov the issue you are describing was further described in #29414 and was fixed in #29054 - should be part of the matplotlib 3.11 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.