26

I am generating 2D arrays on log-spaced axes (for instance, the x pixel coordinates are generated using logspace(log10(0.95), log10(2.08), n).

I want to display the image using a plain old imshow, in its native resolution and scaling (I don't need to stretch it; the data itself is already log scaled), but I want to add ticks, labels, lines that are in the correct place on the log axes. How do I do this?

Ideally I could just use commands line axvline(1.5) and the line would be in the correct place (58% from the left), but if the only way is to manually translate between logscale coordinates and image coordinates, that's ok, too.

For linear axes, using extents= in the call to imshow does what I want, but I don't see a way to do the same thing with a log axis.

Example:

from matplotlib.colors import LogNorm

x = logspace(log10(10), log10(1000), 5)
imshow(vstack((x,x)), extent=[10, 1000, 0, 100], cmap='gray', norm=LogNorm(), interpolation='nearest')
axvline(100, color='red')

This example does not work, because extent= only applies to linear scales, so when you do axvline at 100, it does not appear in the center. I'd like the x axis to show 10, 100, 1000, and axvline(100) to put a line in the center at the 100 point, while the pixels remain equally spaced.

Community
  • 1
  • 1
endolith
  • 25,479
  • 34
  • 128
  • 192
  • Can you have some working code or image of what you want to achieve. Another question is whether you are flexible about using pcolor instead of imshow. – imsc Jul 16 '12 at 05:44
  • @imsc: added an example. I think pcolor is fine. – endolith Jul 16 '12 at 14:26

3 Answers3

21

In my view, it is better to use pcolor and regular (non-converted) x and y values. pcolor gives you more flexibility and regular x and y axis are less confusing.

import pylab as plt
import numpy as np
from matplotlib.colors import LogNorm
from matplotlib.ticker import LogFormatterMathtext

x=np.logspace(1, 3, 6)
y=np.logspace(0, 2,3)
X,Y=np.meshgrid(x,y)
z = np.logspace(np.log10(10), np.log10(1000), 5)
Z=np.vstack((z,z))

im = plt.pcolor(X,Y,Z, cmap='gray', norm=LogNorm())
plt.axvline(100, color='red')

plt.xscale('log')
plt.yscale('log')

plt.colorbar(im, orientation='horizontal',format=LogFormatterMathtext())
plt.show()

enter image description here

As pcolor is slow, a faster solution is to use pcolormesh instead.

im = plt.pcolormesh(X,Y,Z, cmap='gray', norm=LogNorm())
imsc
  • 7,492
  • 7
  • 47
  • 69
  • 1
    This looks like it would solve my problem. Here's a simpler example that gets at what I was trying to solve: https://gist.github.com/3124528 So pcolor is like an extremely slow imshow that draws each pixels as a rectangle? There's no way to do `xscale('log')` with imshow? – endolith Jul 16 '12 at 19:32
  • `pcolormesh` looks like a faster way to do the same thing. "pcolormesh uses a QuadMesh, a faster generalization of pcolor, but with some restrictions." Not sure what those restrictions are, but it seems to work. – endolith Jul 16 '12 at 19:45
  • `pcolormesh` seems to be a nice alternative. One of the restrictions is it can not be used with masked coordinate arrays. – imsc Jul 17 '12 at 05:24
  • unfortunately this results in interpolation errors in the resulting image compared to imshow, which does good interpolation. :/ waiiiiit a second..... I just tried imshow with extent and followed by `xscale('log')` and it works fine. – endolith Jul 18 '12 at 03:31
  • Can you put your solution as a new answer. – imsc Jul 18 '12 at 05:59
11

Actually, it works fine. I'm confused.

Previously I was getting errors about "Images are not supported on non-linear axes" which is why I asked this question. But now when I try it, it works:

import matplotlib.pyplot as plt
import numpy as np

x = np.logspace(1, 3, 5)
y = np.linspace(0, 2, 3)
z = np.linspace(0, 1, 4)
Z = np.vstack((z, z))

plt.imshow(Z, extent=[10, 1000, 0, 1], cmap='gray')
plt.xscale('log')

plt.axvline(100, color='red')

plt.show()

This is better than pcolor() and pcolormesh() because

  1. it's not insanely slow and
  2. is interpolated nicely without misleading artifacts when the image is not shown at native resolution.
endolith
  • 25,479
  • 34
  • 128
  • 192
  • 2
    I am equally confused. Previously, I tried `imshow` with `log` and it did not work, however it is working perfectly now. – imsc Jul 18 '12 at 15:34
  • 2
    We found that if you remove `extent` it won't work. That is, `plt.imshow(Z,cmap='gray'); plt.xscale('log')` raises the error. – Developer Jan 02 '14 at 06:56
  • 2
    @developer oh maybe because the default extent starts at 0? – endolith Jan 03 '14 at 14:03
  • When using the code above with matplotlib 1.4.3 I still get warnings: `C:\Python34\lib\site-packages\matplotlib\axes\_base.py:1166: UserWarning: aspect is not supported for Axes with xscale=log, yscale=linear 'yscale=%s' % (xscale, yscale))` and `C:\Python34\lib\site-packages\matplotlib\image.py:359: UserWarning: Images are not supported on non-linear axes. warnings.warn("Images are not supported on non-linear axes.")` Obviously it does not work correctly as aspect ratio is wrong etc. Not recommended to use this. – dayoda Apr 01 '15 at 18:26
  • 2
    Did this stop working in MPL2? ... probably: http://matplotlib.org/devdocs/users/whats_new.html#non-linear-scales-on-image-plots – endolith Mar 09 '17 at 04:01
  • 8
    Is this still working? I don't get any errors, but the image is stretch and deformed by the log scale. Like I would want all the squares to be the same size but they aren't. – Marses Feb 28 '19 at 20:30
  • I get wrong axis values using a non-uniform date axis with imshow. No errors, but the axis is clearly wrong :/ (each pixel is associated with a fixed date, but the dates represented in the image is slightly non-uniform) – olejorgenb May 13 '19 at 11:37
  • 2
    I think it does not work anymore, I also have stretched image. In the example provided @endolith, there are 4 different values that should change for x= 31.6, 100, and 316. However in the image the changes are around 250, 500 and 750. Something is clearly not working as expected! Anyone got a reference on how to do it? – BayesianMonk Jul 05 '22 at 09:39
-2

To display imshow with abscisse log scale:

ax = fig.add_subplot(nrow, ncol, i+1)
ax.set_xscale('log')
Matt
  • 7
  • 2