I need to do a series of vector plots. I can get any number of plots with matplotlib's quiver routine. The thing is, quiver autoscales each plot, but I need the vectors in each plot to all represent the same scale. For instance, if 10 km/hr is represented by a vector of 1cm in one plot, then 10km/hr should be represented by a 1cm vector in all plots. (I don't really care if the vector is specifically 1cm. That's just an example.) I thought I could make this happen by adjusting the scale argument separately for each plot. But it doesn't seem to work.
For example, I find the maximum speed in the first plot, mxs1
, and then for each plot I do something like
mxspd = np.max(speed[n])
pylab.quiver(x,y,vx[n],vy[n],scale=mxs1/mxspd)
But this does not adjust the lengths of the vectors enough. For instance, in the case I was trying, mxspd
is about one half of mxs1
, so the vectors in plot n
should be about half as long as the ones in the first plot. But the vectors in the two plots have pretty much the same lengths.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
ax_r.quiver(x, y, u, v, scale=5, scale_units='inches')
ax_l.quiver(x, y, 2*u, 2*v, scale=5, scale_units='inches')
ax_l.set_title('2x')
ax_r.set_title('1x')
See the documentation for explainations of the scale
and scale_units
kwargs.
The answer above matches the scales of the two plots a priori.
The solution below matches the scales a posteri by taking the automatically determined scale from the first plot and applying it to the second.
This may not always work as it uses private calls, but solved my problem.
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
Q = ax_r.quiver(x, y, u, v, scale=None, scale_units='inches')
Q._init()
assert isinstance(Q.scale, float)
ax_l.quiver(x, y, 2*u, 2*v, scale=Q.scale, scale_units='inches')
ax_l.set_title('2x')
ax_r.set_title('1x')
This issue of scaling confused me for ages. In general, I want to have a key that says, "this length of arrow is equivalent to this velocity" , and this means:
angles='xy', scale_units='xy'
, to plot the arrows scaling with x and y units, rather than fixed units. For example, if the axes are in meters and you want m/sThus if we assume the fake u,v data has units of m/s, I adapted the answer of tacaswell as follows (here the scale is fixed to 1).
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x, y = np.mgrid[0:20, 0:25]
u = np.sin(2 *x * np.pi / 20)
v = np.cos(2 * y * np.pi / 25)
fig, (ax_l, ax_r) = plt.subplots(1, 2, figsize=(8, 4))
# set the key length
lkey=1
#set the scale factor
scalef=1
q_l=ax_l.quiver(x, y, 2*u, 2*v, angles='xy', scale_units='xy' , scale=scalef)
ax_l.quiverkey(q_l, X=0.3, Y=1.1, U=lkey,
label='Quiver key, length = '+str(lkey)+' m/s', labelpos='E')
q_r=ax_r.quiver(x, y, u, v, angles='xy', scale_units='xy', scale=scalef )
ax_r.quiverkey(q_r, X=0.3, Y=1.1, U=lkey,
label='Quiver key, length = '+str(lkey)+' m/s', labelpos='E')
ax_l.set_title('2x')
ax_r.set_title('1x')
giving:
That said, in general you might want to use the automatic scale factor and then adjust the key as this prevents overlap of arrows and manual fiddling with the scale factor. To illustrate this further, I'll scale the data by a factor of 5:
u = 5*np.sin(2 *x * np.pi / 20)
v = 5*np.cos(2 * y * np.pi / 25)
Now the left panel will have max velocity of 10 m/s (it is doubled)
So if we set the following options:
lkey=10
#set the scale factor to None for autoscaling
scalef=None
Then we get the following:
so here the plots look the same but the arrow on the left key is correctly half the length of that on the right.
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