How to relabel axis ticks for a matplotlib heatmap - python

I am following this example to produce a heat map. Would it be possible to relabel the values over the X-axis, and add a constant to it.
Instead of 0, 1, 2, 3, 4 on the x-axis, I would like to have, for example, 5, 6, 7, 8, 9.

You can label the x and y axes by using the keyword argument extent in the calls to imshow. Here is some documentation,
extent : scalars (left, right, bottom, top), optional, default: None
Data limits for the axes. The default assigns zero-based row,
column indices to the `x`, `y` centers of the pixels.
working off the example you linked, you can do the following,
import matplotlib.pyplot as plt
import numpy as np
A = np.random.rand(5, 5)
plt.figure(1)
plt.imshow(A, interpolation='nearest')
plt.grid(True)
left = 4.5
right = 9.5
bottom = 4.5
top = -0.5
extent = [left, right, bottom, top]
plt.figure(2)
plt.imshow(A, interpolation='nearest', extent=extent)
plt.grid(True)
plt.show()
This will change only the x-axis labels. Note that you have to account for the fact that the default labels the pixels while extent labels the whole axis (hence the factors of 0.5). Also note that the default labelling of the y-axis in imshow increases from top to bottom (going from 0 at the top to 4 at the bottom), this means our bottom will be larger than our top variable.

You can simple add the constant via for-loop or list comprehension and use it as new axis label, for example:
import matplotlib.pyplot as plt
CONST = 10
x = range(10)
y = range(10)
labels = [i+CONST for i in x]
fig, ax = plt.subplots()
plt.plot(x, y)
plt.xlabel('x-value + 10')
# set ticks followed by setting labels
ax.set_xticks(range(10))
ax.set_xticklabels(labels)
plt.show()
I added it to my matplotlib gallery in IPython notebooks here with other examples if it is useful:

If you just want to relabel the current plot (click on it to select it) you use the xticks() function (Note the arange() upper limit needs to be one more than the desired maximum) - e.g. from iPython/Python:
xticks(arange(0,5),arange(5,10))
If you wanted to modify the python script file then you would use:
plt.xticks(arange(0,5),arange(5,10))

Related

Set arrow size based on figure units instead of axis data units?

In matplotlib, is there a way to specify arrow head sizes in figure units rather than in data units?
The use case is: I am making a multi-panel figure in which each panel has a different axis size (e.g., one goes from 0 to 1 on the X-axis, and the next goes from 0 to 10). I'd like the arrows to appear the same in each panel. I'd also like the arrows to appear the same independent of direction.
For axes with an aspect ratio not equal to 1, the width of the tail (and therefore the size of the head) varies with direction.
The closest I've come is, after drawing on the canvas:
dx = ax.get_xlim()[1] - ax.get_xlim()[0]
for arrow in ax.patches:
arrow.set_data(width=dx/50)
but this does not work; it results in images like this:
Just use ax.annotate() instead of ax.arrow():
import matplotlib.pyplot as plt
import numpy as np
xlim, ylim = (-.3, .8), (0, 5.8)
arrow_start, arrow_end = np.asarray([.1, 3]), np.asarray([.5, 5])
fig = plt.figure(figsize=(3, 2))
ax = plt.gca()
ax.set_title('using ax.arrow()')
ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.arrow(*arrow_start, *(arrow_end - arrow_start), width=1/50)
fig = plt.figure(figsize=(3, 2))
ax = plt.gca()
ax.set_title('using ax.annotate()')
ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.annotate('', arrow_end, arrow_start, arrowprops=dict(width=5, headwidth=10, headlength=5))

Polar grid on left hand side of rectangular plot

I am trying to reproduce a plot like this:
So the requirements are actually that the grid (that is to be present just on the left side) behaves just like a grid, that is, if we zoom in and out, it is always there present and not dependent on specific x-y limits for the actual data.
Unfortunately there is no diagonal version of axhline/axvline (open issue here) so I was thinking about using the grid from polar plots.
So for that I have two problems:
This answer shows how to overlay a polar axis on top of a rectangular one, but it does not match the origins and x-y values. How can I do that?
I also tried the suggestion from this answer for having polar plots using ax.set_thetamin/max but I get an AttributeError: 'AxesSubplot' object has no attribute 'set_thetamin' How can I use these functions?
This is the code I used to try to add a polar grid to an already existing rectangular plot on ax axis:
ax_polar = fig.add_axes(ax, polar=True, frameon=False)
ax_polar.set_thetamin(90)
ax_polar.set_thetamax(270)
ax_polar.grid(True)
I was hoping I could get some help from you guys. Thanks!
The mpl_toolkits.axisartist has the option to plot a plot similar to the desired one. The following is a slightly modified version of the example from the mpl_toolkits.axisartist tutorial:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
from mpl_toolkits.axisartist import SubplotHost, ParasiteAxesAuxTrans
from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
import mpl_toolkits.axisartist.angle_helper as angle_helper
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D
# PolarAxes.PolarTransform takes radian. However, we want our coordinate
# system in degree
tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()
# polar projection, which involves cycle, and also has limits in
# its coordinates, needs a special method to find the extremes
# (min, max of the coordinate within the view).
# 20, 20 : number of sampling points along x, y direction
extreme_finder = angle_helper.ExtremeFinderCycle(20, 20,
lon_cycle=360,
lat_cycle=None,
lon_minmax=None,
lat_minmax=(0, np.inf),)
grid_locator1 = angle_helper.LocatorDMS(36)
tick_formatter1 = angle_helper.FormatterDMS()
grid_helper = GridHelperCurveLinear(tr,
extreme_finder=extreme_finder,
grid_locator1=grid_locator1,
tick_formatter1=tick_formatter1
)
fig = plt.figure(1, figsize=(7, 4))
fig.clf()
ax = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)
# make ticklabels of right invisible, and top axis visible.
ax.axis["right"].major_ticklabels.set_visible(False)
ax.axis["right"].major_ticks.set_visible(False)
ax.axis["top"].major_ticklabels.set_visible(True)
# let left axis shows ticklabels for 1st coordinate (angle)
ax.axis["left"].get_helper().nth_coord_ticks = 0
# let bottom axis shows ticklabels for 2nd coordinate (radius)
ax.axis["bottom"].get_helper().nth_coord_ticks = 1
fig.add_subplot(ax)
## A parasite axes with given transform
## This is the axes to plot the data to.
ax2 = ParasiteAxesAuxTrans(ax, tr)
## note that ax2.transData == tr + ax1.transData
## Anything you draw in ax2 will match the ticks and grids of ax1.
ax.parasites.append(ax2)
intp = cbook.simple_linear_interpolation
ax2.plot(intp(np.array([150, 230]), 50),
intp(np.array([9., 3]), 50),
linewidth=2.0)
ax.set_aspect(1.)
ax.set_xlim(-12, 1)
ax.set_ylim(-5, 5)
ax.grid(True, zorder=0)
wp = plt.Rectangle((0,-5),width=1,height=10, facecolor="w", edgecolor="none")
ax.add_patch(wp)
ax.axvline(0, color="grey", lw=1)
plt.show()

Is it possible to test if the legend is covering any data in matplotlib/pyplot

Python beginner so apologies if incorrect terminology at any point.
I am using the legend(loc='best', ...) method and it works 99% of the time. However, when stacking more than 9 plots (i.e. i>9 in example below) on a single figure, with individual labels, it defaults to center and covers the data.
Is there a way to run a test in the script that will give a true/false value if the legend is covering any data points?
Very simplified code:
fig = plt.figure()
for i in data:
plt.plot(i[x, y], label=LABEL)
fig.legend(loc='best')
fig.savefig()
Example of legend covering data
One way is to add some extra space at the bottom/top/left or right side of the axis (in your case I would prefer top or bottom), by changing the limits slightly. Doing so makes the legend fit below the data. Add extra space by setting a different y-limit with ax.set_ylim(-3e-4, 1.5e-4) (the upper limit is approximately what it is in your figure and -3 is a estimate of what you need).
What you also need to do is to add split the legend into more columns, with the keyword ncol=N when creating the legend.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.linspace(0, 1, 100)
y = 3.5 * x - 2
for i in range(9):
ax.plot(x, y + i / 10., label='iiiiiiiiiiii={}'.format(i))
ax.set_ylim(-3, 1.5)
ax.legend(loc='lower center', ncol=3) # ncol=3 looked nice for me, maybe you need to change this
plt.show()
EDIT
Another solution is to put the legend in a separate axis like I do in the code below. The data-plot does not need to care about making space for the legend or anything and you should have enough space in the axis below to put all your line-labels. If you need more space, you can easily change the ratio of the upper axis to the lower axis.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(211)
ax_leg = fig.add_subplot(212)
x = np.linspace(0, 1, 100)
y = 3.5 * x - 2
lines = []
for i in range(9): #for plotting the actual data
li, = ax.plot(x, y + i / 10., label='iiiiiiiiiiii={}'.format(i))
lines.append(li)
for line in lines: # just to make the legend plot
ax_leg.plot([], [], line.get_color(), label=line.get_label())
ax_leg.legend(loc='center', ncol=3, ) # ncol=3 looked nice for me, maybe you need to change this
ax_leg.axis('off')
fig.show()

How to change the orientation of axis to a direction given by tuple in matplotlib

For example the orientation of histogram in the picture below is (2,-2)
Use transformations. Since you did not provide any code that would plot the non-rotated picture, I'm using a simple example:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy
n = numpy.random.normal(size=10000)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.set_aspect(1)
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
base_trans = ax.transData
tr = matplotlib.transforms.Affine2D().rotate_deg(-30) + base_trans
ax.hist(n, normed=True, transform=tr, bins=20)
fig.savefig('t.png')
Notes:
I do not know what you mean by a "direction given by a tuple". In your picture the axes are clearly not just rotated, but moved as well (the (0,0) point is not on the x-axis). I only used rotation in this example; see docs for Affine2D for more transformation properties.
In order for your graph to not look skewed, you must match the plot's aspect ratio, x/y limits, and the transformation's scaling coefficients. In the example I used the aspect 1 and the same scale for x and y axes, so I could just use the rotate_deg() method without any additional corrections.

Multiple y-axis conversion scales

Hi
I'm trying to create plots which incorporate parallel conversion scales for two sets of units on the y-axis; using the two different styles of:
offset ('parasitic') y-axes and
overlaid/shared y-axes
to replicate the style of the left-hand y-axes in the attached example images.
I'd like to find the simplest generic way of producing both of the above example plots, which also allows me to generate the y-axis conversion scales by defining the relationship between the two sets of units as a function (in this example: mmHg = kPa * 7.5).
If it's possible to add the third right-hand y axes (vapour concentration and water content) shown in these examples, which are unrelated to the left hand scales, this would be a bonus.
I've read related stackoverflow.com postings and examples on using multiple x and y axes using the twinx and twiny functions - e.g.
here - as well as the Matplotlib cookbook, but I can't find an example which addresses this particular problem.
I'd be very grateful for any minimal working examples or links.
I'm using Matplotlib in Spyder 2.2.1 / Python 2.7.5
Many thanks in anticipation
Dave
For the first plot, I recommend axisartist. The automatic scaling of the two y-axis on the left-hand-side is achieved through a simple scaling factor that applies to the specified y-limits. This first example is based on the explanations on parasite axes:
import numpy as np
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
# initialize the three axis:
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(left=0.25)
par1 = host.twinx()
par2 = host.twinx()
# secify the offset for the left-most axis:
offset = -60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="left", axes=par2, offset=(offset, 0))
par2.axis["right"].toggle(all=True)
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot data on y1 and y2, respectively:
host.plot(x,y1,'b')
par1.plot(x,y2,'r')
# specify the axis limits:
host.set_xlim(0.0,70.0)
host.set_ylim(YLIM[0],YLIM[1])
par1.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
par2.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
host.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
par1.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
par2.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
plt.show()
You will end up with this plot:
You can try to modify the above example to give you the second plot, too. One idea is, to reduce offset to zero. However, with the axisartist, certain tick functions are not supported. One of them is specifying if the ticks go inside or outside the axis.
Therefore, for the second plot, the following example (based on matplotlib: overlay plots with different scales?) is appropriate.
import numpy as np
import matplotlib.pyplot as plt
# initialize the three axis:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax3 = ax1.twinx()
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot the data
ax1.plot(x,y1,'b')
ax2.plot(x,y2,'r')
# define the axis limits
ax1.set_xlim(0.0,70.0)
ax1.set_ylim(YLIM[0],YLIM[1])
ax2.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
ax3.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# move the 3rd y-axis to the left (0.0):
ax3.spines['right'].set_position(('axes', 0.0))
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
ax1.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
ax2.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
ax3.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
# for both letf-hand y-axis move the ticks to the outside:
ax1.get_yaxis().set_tick_params(direction='out')
ax3.get_yaxis().set_tick_params(direction='out')
plt.show()
This results in this figure:
Again, the set_tick_params(direction='out') does not work with the axisartist from the first example.
Somewhat counter-intuitive, both the y1 and y3 ticks have to be set to 'out'. For y1, this makes sense, and for y3 you have to remember that it started as a right-hand-side axis. Therefore, those ticks would appear outside (with the default 'in' setting) when the axis is moved to the left.

Categories