1

If the following code is run

import matplotlib.pyplot as plt
import numpy as np
a=np.random.random((1000,1000))
plt.imshow(a, cmap='Reds', interpolation='nearest')
plt.savefig('fig.png',bbox_inches='tight')

I got the picture below, with all the cells representing each random number.

enter image description here

However, when the axis is added as the code shown below:

import matplotlib.pyplot as plt
import numpy as np
a=np.random.random((1000,1000))
plt.imshow(a, cmap='Reds', interpolation='nearest')

plt.xlim(0, 10)
plt.xticks(list(range(0, 10)))
plt.ylim(0, 10)
plt.yticks(list(range(0, 10)))

plt.savefig('fig3.png',bbox_inches='tight')

I got the picture with less resolution:

enter image description here

So how can I add axis ticks without affecting the resolution? If this is related to the font size of axis markers, how to automatically adjust them so as to keep the original resolution?

lanselibai
  • 1,203
  • 2
  • 19
  • 35
  • Thanks, but how to label 100 as 1, 200 as 2, ...., 1000 as 10? – lanselibai Dec 16 '17 at 21:38
  • I was making an example. Actually, the raw data is picosecond, and I want to label it as nanosecond. And I would like to manually add some words to the tick makers, e.g. change "1,2,3" to "Atom 1, Atom 2, Atom 3". – lanselibai Dec 16 '17 at 21:48

2 Answers2

1

Application to your problem:

from matplotlib.ticker import FuncFormatter
from matplotlib.pyplot import show
import matplotlib.pyplot as plt
import numpy as np

a=np.random.random((1000,1000))

# create scaled formatters / for Y with Atom prefix
formatterY = FuncFormatter(lambda y, pos: 'Atom {0:g}'.format(y*0.01))
formatterX = FuncFormatter(lambda x, pos: '{0:g}'.format(x*0.01))

# apply formatters 
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(formatterY)
ax.xaxis.set_major_formatter(formatterX)

plt.imshow(a, cmap='Reds', interpolation='nearest')

# create labels
plt.xlabel('nanometer')
plt.ylabel('measure')
plt.xticks(list(range(0, 1001,100)))

plt.yticks(list(range(0, 1001,100)))

plt.show()

Y with Atoms, X with scalen numbers, both with titles

Sources:

A possible solution is to format the ticklabels according to some function as seen in below example code from the matplotlib page.

from matplotlib.ticker import FuncFormatter
import matplotlib.pyplot as plt
import numpy as np

x = np.arange(4)
money = [1.5e5, 2.5e6, 5.5e6, 2.0e7]


def millions(x, pos):
    'The two args are the value and tick position'
    return '$%1.1fM' % (x * 1e-6)


formatter = FuncFormatter(millions)

fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(formatter)
plt.bar(x, money)
plt.xticks(x, ('Bill', 'Fred', 'Mary', 'Sue'))
plt.show()

matplotlib.org Example


A similar solution is shown in this answer, where you can set a function to label the axis for you and scale it down:

ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x*scale))
ax.xaxis.set_major_formatter(ticks)

Here, you would need to do /100 instead of *scale

The easier way for yours would probably be:

ticks = plt.xticks()/100
plt.gca().set_xticklabels(ticks.astype(int))

(adapted from https://stackoverflow.com/a/10171851/7505395)

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • thanks, I got: `AttributeError: module 'matplotlib.pyplot' has no attribute 'get_xticks'` – lanselibai Dec 16 '17 at 21:55
  • I tried, but the labels are squeezed on the left, not evenly distributed. – lanselibai Dec 16 '17 at 22:10
  • Note that there is no difference between pylab and pyplot in the functionality it provides. `pylab` is a wrapper, which essentially consists of two lines: `from numpy import *; from matplotlib.pyplot import *`. Since it is not really good style to import several packages into the same namespace, `pylab` is deprecated. All the solutions in this answer will work simply via `import matplotlib.pyplot as plt`. (I modified your answer and removed the errors in it.) – ImportanceOfBeingErnest Dec 16 '17 at 22:50
  • @ImportanceOfBeingErnest thanks for the tip, adjusted my code example to the Q. – Patrick Artner Dec 17 '17 at 00:35
  • @lanselibai see code for application to your demodata with scaled labels, one adorned with Atom and some axis labels for nanoseconds – Patrick Artner Dec 17 '17 at 00:37
  • thank you Patrick, it works! So now there are 10 tick markers for each axis. When there are more than 20 tick markers for the axis, the markers all overlap with each other. At this time, each marker could still have good resolution when zooming in using `plt.show()`. However, the figure saved by `plt.savefig('fig.png')` would lost its resolution. Can this also be optimised? As I have hundreds of figures to save. – lanselibai Dec 17 '17 at 15:32
  • @lanselibai Best thing to do is open another question and provide sampledata - and screenshots of where your problem is. I am not really getting what you do. Why are there sometimes more then 10 markers? – Patrick Artner Dec 17 '17 at 16:09
  • 1
    Thank you, I posted here [link](https://stackoverflow.com/questions/47871512/how-to-properly-display-sufficient-tick-markers-using-plt-savefig) – lanselibai Dec 18 '17 at 15:27
  • @lanselibai and cleb already provided an answer :) scale the image output size – Patrick Artner Dec 18 '17 at 15:50
1

You would use the extent of the image to bring it into a new coordinate space.

At the moment it ranges in the space between 0 and 999. This means the axis limits are (-0.5, 999.5). You can calculate a new extent from a function, e.g. f = lambda x: x/100. and set the result as new extent of the image.

This would make the image occupy the axis range between (-0.005, 9.995). Now it is straight forward to set the tick(label)s as seen in the question.

import matplotlib.pyplot as plt
import numpy as np
a=np.random.random((1000,1000))

im = plt.imshow(a, cmap='Reds', interpolation='nearest')

f = lambda x: x/100.
(llx,ulx),(lly,uly) = plt.xlim(),plt.ylim()
im.set_extent([f(llx),f(ulx),f(lly),f(uly)])


plt.xticks(list(range(0, 10)))
plt.yticks(list(range(0, 10)))

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712