Reputation: 22467
I have a single row of data that I want to heat-map it without the use of twiny(), because it will have alignment problems. I have searched a lot in this site and thats what I've reached till far:
import numpy as np
import matplotlib.pyplot as plt
x = "0.2, 0.3, 0.4, 0.5, 0.6".split(",")
y = "180, 175, 170, 169, 150".split(",")
z = [[5000, 4800, 4500, 4450, 4300]]
fig, ax1 = plt.subplots()
image = z
im = ax1.imshow(image, cmap=plt.cm.Blues, interpolation='nearest')
plt.colorbar(im)
ax1.set_xticks(np.arange(len(x)), minor=False)
ax1.set_xticklabels(x, minor=False)
#ax1.set_yticklabels(y, minor=False)
ax1.tick_params(labelbottom='on',labeltop='on', labelleft="off")
plt.show()
As you can see, the top axis has exactly the same text as the lower axes.
What I want is to put y
in the above axis.
Thanks in advance :)
Upvotes: 1
Views: 2271
Reputation: 23540
It seems that twiny
and axes with aspect ratio set to equal
do not want to live on the same axes. To me it seems like a bug, but there may be an explanation.
So, let us get around this problem by plotting two axes on top of each other. This is not as trivial as it sounds, because if two subplots are at the same position, matplotlib
interpretes them to be the same one. However, using add_plot
there is no such problem.
import numpy as np
import matplotlib.pyplot as plt
x = "0.2, 0.3, 0.4, 0.5, 0.6".split(",")
y = "180, 175, 170, 169, 150".split(",")
z = [[5000, 4800, 4500, 4450, 4300]]
fig = plt.figure()
ax1 = fig.add_subplot(111)
image = z
im = ax1.imshow(image, cmap=plt.cm.Blues, interpolation='nearest')
plt.colorbar(im)
ax1.set_xticks(np.arange(len(x)), minor=False)
ax1.set_xticklabels(x, minor=False)
ax1.tick_params(labelbottom='on',labeltop='off', labelleft="off",
top='off', left='off', right='off')
# create another axes on the same position:
# - create second axes on top of the first one without background
# - make the background invisible
# - set the x scale according to that of `ax1`
# - set the top ticks on and everything else off
# - set the size according to the size of `ax1`
ax2 = fig.add_axes(ax1.get_position(), frameon=False)
ax2.tick_params(labelbottom='off',labeltop='on', labelleft="off", labelright='off',
bottom='off', left='off', right='off')
ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(np.arange(len(y)))
ax2.set_xticklabels(y, minor=False)
plt.draw()
ax2.set_position(ax1.get_position())
plt.draw()
plt.show()
The plt.draw()
is needed before the set_position
as otherwise the get_position
will return wrong position for ax1
due to the use of equal
aspect. (This may be the very reason why twiny
fails.)
If you need to have several rows, the solution is not that different:
import numpy as np
import matplotlib.pyplot as plt
x = "0.2, 0.3, 0.4, 0.5, 0.6".split(",")
y = "180, 175, 170, 169, 150".split(",")
z = [[5000, 4800, 4500, 4450, 4300]]
numRows = 8
fig, subaxes = plt.subplots(nrows=numRows, ncols=1)
axeslist = subaxes.flatten()
for ax in axeslist:
im = ax.imshow(z, cmap=plt.cm.Blues, interpolation='nearest')
ax.tick_params(labelbottom='off',labeltop='off', labelleft="off", labelright='off',
bottom='off', top='off', left='off', right='off')
if ax == axeslist[0]:
ax.set_title('Avg. (s)\n', size=13)
elif ax == axeslist[-1]:
ax.tick_params(bottom='on', labelbottom='on')
ax.set_xticks(range(len(x)))
ax.set_xticklabels(x)
# reserve some space between the subplots
fig.subplots_adjust(hspace=0.07*(numRows-1))
# create the overlay images, add them as extra properties of the original images
for ax in axeslist:
axnew = fig.add_axes(ax.get_position(), frameon=False)
axnew.tick_params(labelbottom='off',labeltop='on', labelleft="off", labelright='off',
bottom='off', top='on', left='off', right='off')
axnew.set_xlim(ax.get_xlim())
axnew.set_xticks(range(len(y)))
axnew.set_xticklabels(y)
ax.extra_axes = axnew
# update the secondary axes positions
# draw() only if there was something changed (important!)
def update_secondary(event=None):
position_changed = False
for ax in axeslist:
if ax.extra_axes.get_position().bounds == ax.get_position().bounds:
continue
position_changed = True
ax.extra_axes.set_position(ax.get_position())
if position_changed:
plt.draw()
# register the secondary axes updater as a callback
fig.canvas.mpl_connect('draw_event', update_secondary)
# make sure everything is drawn
plt.draw()
As the overlay update has to be carried out after everything else has been drawn, it is here done by the draw_event
from the backend. The result is that after the image is redrawn for some reason, the overlays are readjusted, and if any positions were changed, the scene is redrawn.
This works but is not beautiful.
Upvotes: 4
Reputation: 22467
Use twin()
and from mpl_toolkits.axes_grid1 import host_subplot
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import numpy as np
x = "0.2, 0.3, 0.4, 0.5, 0.6".split(",")
y = "180, 175, 170, 169, 150".split(",")
z = [[5000, 4800, 4500, 4450, 4300]]
#fig, ax1 = plt.subplots()
ax1 = host_subplot(111, axes_class=AA.Axes)
ax2 = ax1.twin()
image = z
im = ax1.imshow(image, cmap=plt.cm.Blues, interpolation='nearest')
plt.colorbar(im)
ax1.set_xticks(np.arange(len(x)), minor=False)
ax2.set_xticks(np.arange(len(y)), minor=False)
#ax1.tick_params(labelbottom='on',labeltop='on', labelleft="off")
#ax2.tick_params(labelbottom='on',labeltop='on', labelleft="off")
ax1.set_yticklabels([])
ax2.set_yticklabels([])
ax1.tick_params(labelbottom='on',labeltop='on', labelleft="off")
plt.show()
Upvotes: 1
Reputation: 22467
Well, I found an alternative solution by using text instead of changing the axes values.
import numpy as np
import matplotlib.pyplot as plt
font = {'family' : 'sans-serif',
'color' : 'k',
'weight' : 'normal',
'size' : 12,
}
x = "0.2, 0.3, 0.4, 0.5, 0.6".split(",")
y = "180, 175, 170, 169, 150".split(",")
z = [[5000, 4800, 4500, 4450, 4300]]
fig, ax1 = plt.subplots()
#fig, ax1 = plt.subplots()
image = z
im = ax1.imshow(z, cmap=plt.cm.Blues, interpolation='nearest')
xticks = ax1.get_xticks()
top_lables_width_spacings = 0.83
top_lables_hight_spacings = -.53
for i in range(len(y)):
ax1.text(xticks[i] + top_lables_width_spacings, top_lables_hight_spacings, y[i], fontdict=font)
#ax1.set_aspect('auto')
fig.colorbar(im, orientation='vertical')
ax1.set_xticks(np.arange(len(x)), minor=False)
ax1.set_xticklabels(x, minor=False)
ax1.tick_params(labelbottom='on',labeltop='off', labelleft="off")
ax1.set_title('$\eta$\n', size=17) # represents the top axes label
plt.xlabel(r'$\theta$', size=17) # represents the bottom axes label
plt.show()
Upvotes: 1
Reputation: 2710
If I got this right, I guess you just need to change x to y:
ax1.set_xticklabels(y, minor=False)
If I do that, I see labels in list y on the top and bottom of the plot.
Upvotes: 0