I've been spending the past couple of hours trying to customize matplotlib.quiver
without much luck. The documentation is quite confusing and I haven't been able to parse out how to set each parameter. In my axes, one vertical pixel is much less physical distance than one horizontal pixel, and I want quiver to autoscale the arrows to this aspect ratio. What I'm currently getting are horizontal arrows with the following code:
Tlevs = np.arange(-1.,8.5,.5) + 0.
yy, zz = np.meshgrid(ds.YC, ds.Z)
fig, ax = plt.subplots(figsize=(8,5))
fig.set_tight_layout(True)
im = ax.contourf(T_clim.YC, T_clim.Z, T_clim, levels=Tlevs)
ax.quiver(yy[::3,::10], zz[::3,::10],
vpFep_b[::3,::10], wpFep_b[::3,::10],
pivot='mid', angles='xy', units='xy')
ax.set_xlabel('Y [m]', fontsize=13)
ax.set_ylabel('Depth [m]', fontsize=13)
cbar = fig.colorbar(im, ax=ax)
cbar.set_label(r"[$^\circ$C]")
Setting the quiver options to the following only gives dots for what the arrows should be:
ax.quiver(yy[::3,::10], zz[::3,::10],
vpFep_b[::3,::10], wpFep_b[::3,::10],
pivot='mid', angles='xy', scale_units='xy', scales=1.)
and:
ax.quiver(yy[::3,::10], zz[::3,::10],
vpFep_b[::3,::10], wpFep_b[::3,::10],
pivot='mid', angles='xy', scale_units='xy')
gives the following error:
/home/takaya/miniconda3/envs/uptodate/lib/python3.6/site-packages/matplotlib/quiver.py:666: RuntimeWarning: divide by zero encountered in double_scalars
length = a * (widthu_per_lenu / (self.scale * self.width))
/home/takaya/miniconda3/envs/uptodate/lib/python3.6/site-packages/matplotlib/quiver.py:666: RuntimeWarning: invalid value encountered in multiply
length = a * (widthu_per_lenu / (self.scale * self.width))
/home/takaya/miniconda3/envs/uptodate/lib/python3.6/site-packages/matplotlib/quiver.py:719: RuntimeWarning: invalid value encountered in less
short = np.repeat(length < minsh, 8, axis=1)
/home/takaya/miniconda3/envs/uptodate/lib/python3.6/site-packages/matplotlib/quiver.py:733: RuntimeWarning: invalid value encountered in less
tooshort = length < self.minlength
Any help would be appreciated. Thanks!
The quiver
documentation says:
To plot vectors in the x-y plane, with u and v having the same units as x and y, use
angles='xy'
,scale_units='xy'
,scale=1
Thus, using this arguments your problem will be solved. That is because the keyword units
affects the arrow dimensions except for length, and scale_units
affects only the lenght.
It is true that the documentation is not completely clear, and there are many parameters with similar names which in addition to that, are unique to quiver.
Below there are many examples of the behaviour of different parameters, in particular the ones of interest to this question: angles
, units
and scale_units
. Each example has an image that can be expanded by clicking on it.
The data for all the plots is the same, and can be reproduced using this code:
x = np.linspace(0,50,5)
y = np.linspace(-150,150,7)
X,Y = np.meshgrid(x,y)
U = 3.5*np.ones_like(X)
V = 3.5*np.ones_like(Y)
angles
The entry in the documenatation is:
angles : [ ‘uv’ | ‘xy’ ], array, optional
Method for determining the angle of the arrows. Default is ‘uv’.
‘uv’: the arrow axis aspect ratio is 1 so that if U*==*V the orientation of the arrow on the plot is 45 degrees counter-clockwise from the horizontal axis (positive to the right).
‘xy’: arrows point from (x,y) to (x+u, y+v). Use this for plotting a gradient field, for example.
In our case, the ratio between U and V is one, therefore the arrows will point in 45 degrees in the 'uv' case, however, in the 'xy' case, as the range of the y and x axis is different, the arrows won't point in 45 degrees in order to preserve the displacement from (x,y) to (x+u, y+v). This is important to take into account, since depending on the axis aspect ratio, a gradient (u,v)=(1,1) won't have a 45 degrees angle.
units
The entry of the documentation is:
units : [ ‘width’ | ‘height’ | ‘dots’ | ‘inches’ | ‘x’ | ‘y’ | ‘xy’ ]
The arrow dimensions (except for length) are measured in multiples of this unit.
‘width’ or ‘height’: the width or height of the axis
‘dots’ or ‘inches’: pixels or inches, based on the figure dpi
‘x’, ‘y’, or ‘xy’: respectively X, Y, or in data units
The arrows scale differently depending on the units. For ‘x’ or ‘y’, the arrows get larger as one zooms in; for other units, the arrow size is independent of the zoom state. For ‘width or ‘height’, the arrow size increases with the width and height of the axes, respectively, when the window is resized; for ‘dots’ or ‘inches’, resizing does not change the arrows.
As explained above, this parameters defines the units in which all dimensions except the length of the arrow are measured. However, the parameter width (which is the most rellevant since the arrow headwidth and headlength and so on are defined as multiples of it) has a default values which depends on what units are used.
Below there is an example of different units, fixing the width to comparable cases. The parameter dots or inches is nearly equivalent, and therefore only one of them is considered in the example. Thus, width is set to 0.01 for 'width' and 'height', 3 for 'dots' and 2 for 'x','y' and 'xy'. Not fixing the same width yields different result depending on the backend: i.e. with matplotlib inline in jupyter there is no difference in any case and with Qt5 there are some, but its hard to interpret as the width is unknown.
It can be seen that as the plot axes are wider than taller, setting units to 'width' yields thicker arrows compared to 'length', because the width is the same. The same goes when comparing 'x', 'y' and 'xy', a distance of 1 measured according to the x axis is much larger than a distance of 1 according to the y axis.
scale_units
The entry in the documentation is:
scale_units : [ ‘width’ | ‘height’ | ‘dots’ | ‘inches’ | ‘x’ | ‘y’ | ‘xy’ ], None, optional
If the scale kwarg is None, the arrow length unit. Default is None.
e.g. scale_units is ‘inches’, scale is 2.0, and (u,v) = (1,0), then the vector will be 0.5 inches long.
If scale_units is ‘width’/’height’, then the vector will be half the width/height of the axes.
If scale_units is ‘x’ then the vector will be 0.5 x-axis units. To plot vectors in the x-y plane, with u and v having the same units as x and y, use angles='xy', scale_units='xy', scale=1.
The explanation about scaling when resizing the plot is the same as in units. However, the "If the scale kwarg is None" statment is completely unclear and leads to error.
If scale is None, then the lenght of the arrows will be set to a default value depending on scale_units in order to keep a reasonable ratio between width and height and to keep the arrows in good shape (i.e. a reasonable head). Then, scale_units won't be propperly appreciated until the plot is resized (due to the differences in scaling depending on scale_units).
If scale is different than None, then the arrow lenght is not set to its default anymore, it follows the examples in the documentation.
Below there is a plot comparing different values of scale_units, with scale=1 and units set to its default value 'width'.
It can be seen that the 'y' case, makes the arros look like dots, due to the difference in size between the U,V vectors which is around 5 and the y scale which goes between -150 and 150, whereas in the case of 'width' and 'lenght', 5 times the size of the plot axes makes the arrows huge.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With