Matplotlib quiver plotting with constant arrow size - python

I'm trying to plot a simple quiver plot (e.g. as in the matplotlib gallery: http://matplotlib.org/examples/pylab_examples/quiver_demo.html), although I don't want the autoscaling feature enabled.
I only want to show the field direction, not the magnitude.
Is there a way to set the arrow size as constant please? I tried playing with the scale and scale units but this just seems to change all arrows by some common factor.
Thanks

Changing scale won't work for this. You need to normalize the vectors, e.g.
X, Y = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2))
U = np.cos(X)
V = np.sin(Y)
# Normalize the arrows:
U = U / np.sqrt(U**2 + V**2);
V = V / np.sqrt(U**2 + V**2);
plt.figure()
plt.title('Normalized arrows')
Q = plt.quiver(X, Y, U, V, units='width')
qk = plt.quiverkey(Q, 0.9, 0.9, 2, r'$2 \frac{m}{s}$', labelpos='E',
coordinates='figure')

Related

Is there a matplotlib method that allows to plot a vector field on a surface so that the surface is opaque to the vectors?

I wrote a matplotlib program that plots a 2d surface embedded in three dimensional space using plot_trisurf() and then plots a vector field defined on the surface using quiver(). I'd like the surface to be opaque to the vector field but instead the program plots both the vectors that are in front of the surface and those that are behind the surface with respect to the camera, despite the surface's alpha value being 1.0.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
fig = plt.figure(figsize=plt.figaspect(1.0) * 1.7)
# Make a mesh in the space of parameterisation variables u and v
u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=30) # u: theta
v = np.linspace(0, 2.0 * np.pi, endpoint=True, num=60) # v: phi
u, v = np.meshgrid(u, v)
u, v = u.flatten(), v.flatten()
x, y, z = F(u, v)
# Triangulate parameter space to determine the triangles
tri = mtri.Triangulation(u, v)
# Plot the surface. The triangles in parameter space determine which x, y, z
# points are connected by an edge.
ax = fig.add_subplot(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.magma, alpha = 1.0)
xl = ax.get_xlim()
yl = ax.get_ylim()
ax.set_zlim(xl[0], xl[1])
plt.show()
Here's where the vector field gets plotted:
alpha = 1.0
lenght = 0.25
ax.quiver(xf, yf, zf, ox, oy, oz, color='red', alpha=alpha, length=lenght, normalize=True)
Here's an example that shows how the full vector field gets plotted.
I've also tried adding zorder parameters in the plotting functions but with no success: ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.magma, alpha = 1.0, zorder = 2)
Set antialiased=False in plot_trisurf

Ticks in colorbar of matplotlib (Python) are not positioned correctly when I add an arbitrary value

I have plotted a contourf (and contour to plot lines on top) graph with colorbar and equispaced data levels and it is ok. However, when I add an arbitrary level value(1.1 in the example), it is not representd correctly in the colorbar.
In the reproducible example below, besides the levels with steps of 0.5, a new value with a step of 0.1 is added. In the colorbar, the distances are the same.
How can I correct this?
Thanks in advance
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2.0, 3.0, 0.025)
y = np.arange(-2.0, 3.0, 0.025)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
levels = np.arange(-2,3,1)
levels=np.append(levels,1.1)
levels=np.sort(levels)
fig, ax = plt.subplots()
c = plt.contourf(X, Y, Z, levels=levels,alpha=0.15)
cs = plt.contour(X, Y, Z, levels=levels)
ax.clabel(cs, inline=1, fontsize=10)
cbar=plt.colorbar(c)
plt.show()
You should pass spacing parameter to plt.colorbar:
spacing: uniform spacing gives each discrete color the same space; proportional makes the space proportional to the data interval.
cbar=plt.colorbar(c, spacing = 'proportional')

Pointing all arrows towards the origin in matplotlib 3D scatter

I have been trying to figure an easy way to define u,v,w of mpl_toolkits.mplot3d.Axes3D.quiver such that all the quivers (from all over the 3D scatter plot) are all pointing towards the origin. Many thanks for all help rendered!!
You can do this fairly easily by starting with unit-length quivers:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
n = 25
# Generate some random data
x = (np.random.random(n) - 0.5) * 10
y = (np.random.random(n) - 0.5) * 10
z = (np.random.random(n) - 0.5) * 10
r = np.sqrt(np.power(x,2) + np.power(y,2) + np.power(z,2))
# Create unit-length quivers by dividing out the magnitude
u = - x / r
v = - y / r
w = - z / r
ax.quiver(x, y, z, u, v, w)
plt.show()
And then if you want quivers of a certain size or if you have an array of sizes you can simply multiply this in when creating u, v, and w:
sizes = np.random.random(n) * 4
u = -x / r * sizes
v = -y / r * sizes
w = -z / r * sizes
ax.quiver(x, y, z, u, v, w)
plt.show()
This should also support other methods of analytical sizing - provided you calculate the r vectors correctly.

Python/Matplotlib - How to plot a vector sum on top of a circular histogram?

I am trying to plot both a circular histogram and a vector (overlapping) on the same polar plot, but cannot get the vector to show up.
Basically, my data set consists of the times at which unitary events occur during a repeating cycle. This data is in the array "all_phases", which is just a list of degree values for each of these events. I want to show (1) the overall distribution of events w/ a circular histogram (bins corresponding to degree ranges) and (2) a vector sum as a measure of the coherence of all of these values (treating each event as a unit vector).
I can plot either one of these things individually on the subplot titled "histo", but when I try to plot both, only the histogram shows up. I have tried playing with the z-indexes of both objects to no use. The code is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import math
array = np.array
all_phases = [array([-38.24240218]), array([-120.51570738]), array([-23.70224663]),
array([114.9540152]), array([ 2.94523445]), array([-2.16112692]), array([-18.72274284]),
array([13.2292216]), array([-95.5659992]), array([15.69046269]), array([ 51.12022047]),
array([-89.10567276]), array([ 41.77283949]), array([-9.92584921]), array([-7.59680678]),
array([166.71824996]), array([-178.94642752]), array([-23.75819463]), array([38.69481261]),
array([-52.26651244]), array([-57.40976514]), array([33.68226762]), array([-122.1818295]),
array([ 10.17007425]), array([-38.03726335]),array([44.9504975]), array([ 134.63972923]),
array([ 63.02516932]),array([-106.54049292]), array([-25.6527599])]
number_bins = 60
bin_size = 360/number_bins
cluster_num = 1
counts, theta = np.histogram(all_phases, np.arange(-180, 180 + bin_size, bin_size), density=True)
theta = theta[:-1]+ bin_size/2.
theta = theta * np.pi / 180
a_deg = map(lambda x: np.ndarray.item(x), all_phases)
a_rad = map(lambda x: math.radians(x), a_deg)
a_cos = map(lambda x: math.cos(x), a_rad)
a_sin = map(lambda x: math.sin(x), a_rad)
uv_x = sum(a_cos)/len(a_cos)
uv_y = sum(a_sin)/len(a_sin)
uv_radius = np.sqrt((uv_x*uv_x) + (uv_y*uv_y))
uv_phase = np.angle(complex(uv_x, uv_y))
"""
plot histogram and vector sum
"""
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.16, 0.05, 0.56])
histo = fig.add_subplot(111, polar=True)
histo.yaxis.set_ticks(())
histo.arrow(0,0,0.11, 1, head_width=.01, zorder=2)
plt.suptitle("Phase distribution for Neuron #" + str(cluster_num), fontsize=15, y=.94)
plt.subplots_adjust(bottom=0.12, right=0.95, top=0.78, wspace=0.4)
width = (2*np.pi) / number_bins
bars = histo.bar(theta, counts, width = width, bottom=0.002)
for r, bar in zip(counts, bars):
bar.set_facecolor(plt.cm.jet(r / max(counts)))
bar.set_alpha(0.7)
bar.set_zorder(1)
norm = matplotlib.colors.Normalize(vmin (counts.min())*len(all_phases)*bin_size, vmax=(counts.max())*len(all_phases)*bin_size)
cb1 = matplotlib.colorbar.ColorbarBase(ax1, cmap=plt.cm.jet,
orientation='vertical', norm=norm, alpha=0.4,
ticks=np.arange(0, (counts.max())*len(all_phases)*bin_size)+1, )
cb1.ax.tick_params(labelsize=9)
cb1.solids.set_rasterized(True)
cb1.set_label("# spikes")
cb1.ax.yaxis.set_label_position('left')
plt.show()
cluster_num = cluster_num + 1
vs_radius and vs_phase are the parameters for the vector sum arrow I want to put on the polar plot, which I end up calling w/ histo.arrow().
My suspicion is that it might have something to do with trying to put two things on a subplot object?
Any help or thoughts would be very much appreciated!!
The problem is that the FancyArrow that is used by Axes.arrow() does not play well with polar plots.
Instead, you could use the annotate() function to draw a simple arrow that works better in the case of polar plots.
for example:
# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)
ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
v_angle = 0.275*np.pi
v_length = 4
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))
plt.show()
As a general rule, when you deal with polar plot, you have to work just as if you were working with a linear plot. That is to say, you shouldn't try to draw your arrow from (0,0) but rather from (uv_phase, 0)
fig, ax = plt.subplots()
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))

how to use matplotlib quiver scale

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:
Nice to use quiverkey to add the key
Sometimes it can be helpful to set 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/s
Thus 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.

Categories

Resources