How to get unit vectors using `quiver()` in matplotlib? - python

I'm trying to wrap my head around the quiver function to plot vector fields. Here's a test case:
import numpy as np
import matplotlib.pyplot as plt
X, Y = np.mgrid[1:1.5:0.5, 1:1.5:0.5]
print(X)
print(Y)
u = np.ones_like(X)
v = np.zeros_like(Y)
plt.quiver(X,Y, u, v)
plt.axis([0, 3, 0, 3], units='xy', scale=1.)
plt.show()
I am trying to get a vector of length 1, point from (1,0) to (2,0), but here is what I get:
I have tried adding the scale='xy' option, but the behaviour doesn't change. So how does this work?

First funny mistake is that you put the quiver arguments to the axis call. ;-)
Next, looking at the documentation, it says
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.
So let's do as the documentation tells us,
import numpy as np
import matplotlib.pyplot as plt
X, Y = np.mgrid[1:1.5:0.5, 1:1.5:0.5]
u = np.ones_like(X)
v = np.zeros_like(Y)
plt.quiver(X,Y, u, v, units='xy', angles='xy', scale_units='xy', scale=1.)
plt.axis([0, 3, 0, 3])
plt.show()
and indeed we get a one unit long arrow:

Related

How to generate a array with points of this curve?

I want to code a program to generate an array with coordinates to follow for drawing a shape like the white here, given are the blue points. Does anyone know how to do something like that or at least can give me a tip?
You could use e.g. InterpolatedUnivariateSpline to interpolate the points. As these spline functions are usually 1D, you could calculate x and y positions separately, depending on a new variable t going from 0 to 1.
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# positions of the given points
px = [1, 4, 3, 2, 5]
py = [1, 3, 4, 3, 1]
# 5 t-values, at t=0 in point 1, at t=1 reaching point 5
pt = np.linspace(0, 1, len(px))
# sx and sy are functions that interpolate the points at the given t-values
sx = interpolate.InterpolatedUnivariateSpline(pt, px)
sy = interpolate.InterpolatedUnivariateSpline(pt, py)
# calculate many intermediate values
t = np.linspace(0, 1, 500)
x = sx(t)
y = sy(t)
# show the original points together with the spline
fig, ax = plt.subplots(facecolor='black')
ax.axis('off')
plt.scatter(px, py, s=80, color='skyblue')
plt.plot(x, y, color='white')
for i, (xi, yi) in enumerate(zip(px, py), start=1):
ax.text(xi, yi, f'\n {i}', ha='left', va='center', size=30, color='yellow')
plt.show()

How to draw slope triangles in matplotlib?

What I would like to have are the triangles as shown in the image:
Here is my code:
import matplotlib.pyplot as plt
data= [0.2855,0.3030,0.4995]
x = [1,2,3]
plt.plot(x, data)
plt.show
Is there a simple way of inserting these slope triangles as shown in the image in an automatic fashion? I would like to have the triangle in the middle between two points and with the slope written next to it.
Depending on your idea of "automatic fashion", this might be a suitable solution:
import matplotlib.pyplot as plt
import numpy as np
# Data
x = np.array([1, 2, 3])
y = np.array([0.2855, 0.3030, 0.4995])
# Calculate triangle coordinates values
x_mid = np.convolve(x, [0.5, 0.5], mode='valid')
x_tri = np.vstack((x_mid, x_mid + 0.3))
y_tri = np.interp(x_tri, x, y)
# Calculate slopes
slopes = np.diff(y) / np.diff(x)
# Plot
plt.plot(x, y)
plt.plot(x_tri, np.tile(y_tri[0, :], [2, 1]), 'r') # red horizontal line
plt.plot(np.tile(x_tri[1, :], [2, 1]), y_tri, 'r') # red vertical line
for i, slope in enumerate(slopes): # slope values
plt.text(x_tri[1, i] + 0.05, np.mean(y_tri[:, i]), r'{0:.3f}'.format(slope))
plt.show()
Output:
Put all the triangle stuff in a separate function, and it won't affect your main code too much.
Hope that helps!
The triangle construction can also be done using mpltools:
import matplotlib.pyplot as plt
from mpltools import annotation
import numpy as np
data = [0.2855, 0.3030, 0.4995]
x = [1, 2, 3]
# get midpoint coordinates
x_mid = np.convolve(x, [0.5, 0.5], mode='valid')
y_mid = np.interp(x_mid, x, data)
# compute the gradient of each segment
gradients = np.diff(data)/np.diff(x)
# plot
plt.plot(x, data)
axes = plt.gca()
for xm, ym, g in zip(x_mid, y_mid, gradients):
annotation.slope_marker((xm, ym), g)
plt.show()
The first argument of annotation.slope_marker is a tuple containing the coordinates for the left-hand corner of the triangle and the second argument is the gradient. So here we loop over the midpoints of the line segments and their gradients and annotate with a triangular slope marker for that gradient at those coordinates.
Expected Output:

How to add a point inside a matplotlib contour plot?

I would like to draw a phase portrait of a system of ordinary differential equations in a 2D phase space, and used the streamplot function of matplotlib to achieve it. And I would like to mark some points (the fixed points) in the figure, but I don't know how to do it on the same diagram, for using scatter only plots points on another figure. How can I do it? The following is what I have for now:
import numpy as np
import matplotlib.pyplot as plt
"""
The system of ODE is given by:
dx/dt = alpha*(1-x)*x - c*x*y
dy/dt = -b*y + d*x*y
"""
# Set the parameters
alpha, b, c, d = 4, 3, 2, 1
# The fixed points
fp = [(0, 0), (1,0), (b/d, alpha/c*(1-b/d))]
# The phase portrait lies in {(x,y)|-l<x<l, -l<y<l} with n arrows in each dimension
l, n = 2, 41
# The 'velocity' of a point in phase space = (dx/dt, dy/dt)
def velocity(x, y):
return alpha*(1-x)*x - c*x*y, -b*y + d*x*y
# Mesh the grids in x-y space
x, y = np.linspace(-l, l, n), np.linspace(-1, 1, n)
X, Y = np.meshgrid(x, y)
U, V = velocity(X, Y)
plt.figure()
plt.streamplot(X, Y, U, V)
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.show()

How to plot vectors in python using matplotlib

I am taking a course on linear algebra and I want to visualize the vectors in action, such as vector addition, normal vector, so on.
For instance:
V = np.array([[1,1],[-2,2],[4,-7]])
In this case I want to plot 3 vectors V1 = (1,1), M2 = (-2,2), M3 = (4,-7).
Then I should be able to add V1,V2 to plot a new vector V12(all together in one figure).
when I use the following code, the plot is not as intended
import numpy as np
import matplotlib.pyplot as plt
M = np.array([[1,1],[-2,2],[4,-7]])
print("vector:1")
print(M[0,:])
# print("vector:2")
# print(M[1,:])
rows,cols = M.T.shape
print(cols)
for i,l in enumerate(range(0,cols)):
print("Iteration: {}-{}".format(i,l))
print("vector:{}".format(i))
print(M[i,:])
v1 = [0,0],[M[i,0],M[i,1]]
# v1 = [M[i,0]],[M[i,1]]
print(v1)
plt.figure(i)
plt.plot(v1)
plt.show()
How about something like
import numpy as np
import matplotlib.pyplot as plt
V = np.array([[1,1], [-2,2], [4,-7]])
origin = np.array([[0, 0, 0],[0, 0, 0]]) # origin point
plt.quiver(*origin, V[:,0], V[:,1], color=['r','b','g'], scale=21)
plt.show()
Then to add up any two vectors and plot them to the same figure, do so before you call plt.show(). Something like:
plt.quiver(*origin, V[:,0], V[:,1], color=['r','b','g'], scale=21)
v12 = V[0] + V[1] # adding up the 1st (red) and 2nd (blue) vectors
plt.quiver(*origin, v12[0], v12[1])
plt.show()
NOTE: in Python2 use origin[0], origin[1] instead of *origin
This may also be achieved using matplotlib.pyplot.quiver, as noted in the linked answer;
plt.quiver([0, 0, 0], [0, 0, 0], [1, -2, 4], [1, 2, -7], angles='xy', scale_units='xy', scale=1)
plt.xlim(-10, 10)
plt.ylim(-10, 10)
plt.show()
Your main problem is you create new figures in your loop, so each vector gets drawn on a different figure. Here's what I came up with, let me know if it's still not what you expect:
CODE:
import numpy as np
import matplotlib.pyplot as plt
M = np.array([[1,1],[-2,2],[4,-7]])
rows,cols = M.T.shape
#Get absolute maxes for axis ranges to center origin
#This is optional
maxes = 1.1*np.amax(abs(M), axis = 0)
for i,l in enumerate(range(0,cols)):
xs = [0,M[i,0]]
ys = [0,M[i,1]]
plt.plot(xs,ys)
plt.plot(0,0,'ok') #<-- plot a black point at the origin
plt.axis('equal') #<-- set the axes to the same scale
plt.xlim([-maxes[0],maxes[0]]) #<-- set the x axis limits
plt.ylim([-maxes[1],maxes[1]]) #<-- set the y axis limits
plt.legend(['V'+str(i+1) for i in range(cols)]) #<-- give a legend
plt.grid(b=True, which='major') #<-- plot grid lines
plt.show()
OUTPUT:
EDIT CODE:
import numpy as np
import matplotlib.pyplot as plt
M = np.array([[1,1],[-2,2],[4,-7]])
rows,cols = M.T.shape
#Get absolute maxes for axis ranges to center origin
#This is optional
maxes = 1.1*np.amax(abs(M), axis = 0)
colors = ['b','r','k']
for i,l in enumerate(range(0,cols)):
plt.axes().arrow(0,0,M[i,0],M[i,1],head_width=0.05,head_length=0.1,color = colors[i])
plt.plot(0,0,'ok') #<-- plot a black point at the origin
plt.axis('equal') #<-- set the axes to the same scale
plt.xlim([-maxes[0],maxes[0]]) #<-- set the x axis limits
plt.ylim([-maxes[1],maxes[1]]) #<-- set the y axis limits
plt.grid(b=True, which='major') #<-- plot grid lines
plt.show()
EDIT OUTPUT:
What did you expect the following to do?
v1 = [0,0],[M[i,0],M[i,1]]
v1 = [M[i,0]],[M[i,1]]
This is making two different tuples, and you overwrite what you did the first time... Anyway, matplotlib does not understand what a "vector" is in the sense you are using. You have to be explicit, and plot "arrows":
In [5]: ax = plt.axes()
In [6]: ax.arrow(0, 0, *v1, head_width=0.05, head_length=0.1)
Out[6]: <matplotlib.patches.FancyArrow at 0x114fc8358>
In [7]: ax.arrow(0, 0, *v2, head_width=0.05, head_length=0.1)
Out[7]: <matplotlib.patches.FancyArrow at 0x115bb1470>
In [8]: plt.ylim(-5,5)
Out[8]: (-5, 5)
In [9]: plt.xlim(-5,5)
Out[9]: (-5, 5)
In [10]: plt.show()
Result:
Thanks to everyone, each of your posts helped me a lot.
rbierman code was pretty straight for my question, I have modified a bit and created a function to plot vectors from given arrays. I'd love to see any suggestions to improve it further.
import numpy as np
import matplotlib.pyplot as plt
def plotv(M):
rows,cols = M.T.shape
print(rows,cols)
#Get absolute maxes for axis ranges to center origin
#This is optional
maxes = 1.1*np.amax(abs(M), axis = 0)
colors = ['b','r','k']
fig = plt.figure()
fig.suptitle('Vectors', fontsize=10, fontweight='bold')
ax = fig.add_subplot(111)
fig.subplots_adjust(top=0.85)
ax.set_title('Vector operations')
ax.set_xlabel('x')
ax.set_ylabel('y')
for i,l in enumerate(range(0,cols)):
# print(i)
plt.axes().arrow(0,0,M[i,0],M[i,1],head_width=0.2,head_length=0.1,zorder=3)
ax.text(M[i,0],M[i,1], str(M[i]), style='italic',
bbox={'facecolor':'red', 'alpha':0.5, 'pad':0.5})
plt.plot(0,0,'ok') #<-- plot a black point at the origin
# plt.axis('equal') #<-- set the axes to the same scale
plt.xlim([-maxes[0],maxes[0]]) #<-- set the x axis limits
plt.ylim([-maxes[1],maxes[1]]) #<-- set the y axis limits
plt.grid(b=True, which='major') #<-- plot grid lines
plt.show()
r = np.random.randint(4,size=[2,2])
print(r[0,:])
print(r[1,:])
r12 = np.add(r[0,:],r[1,:])
print(r12)
plotv(np.vstack((r,r12)))
Vector addition performed on random vectors
All nice solutions, borrowing and improvising for special case -> If you want to add a label near the arrowhead:
arr = [2,3]
txt = “Vector X”
ax.annotate(txt, arr)
ax.arrow(0, 0, *arr, head_width=0.05, head_length=0.1)
In order to match the vector lenght and angle with the x,y coordinates of the plot, you can use to following options to plt.quiver:
plt.figure(figsize=(5,2), dpi=100)
plt.quiver(0,0,250,100, angles='xy', scale_units='xy', scale=1)
plt.xlim(0,250)
plt.ylim(0,100)
Quiver is a good method once you figure out its annoying nuances, like not plotting vectors in their original scales. To do as far as I can tell you must pass these params to quiver call as many have pointed out: angles='xy', scale_units='xy', scale=1 AND you should set your plt.xlim and plt.ylim such that you get a square or near square grid. That is the only way I have gotten it to consistently plot the way I want. For instance passing a origin as *[0,0] and U, V as *[5,3] means the resulting plot should be a vector centered at 0,0 origin that goes over 5 units to the right on the x-axis and 3 units up on the y-axis.

How to use matplotlib quiver using an external file

I am trying to write a very simple code (should be):
I want to plot arrows from an external data file which gives me the vector dimensions in x and y, given in two columns sx and sy.
For example, I have two columns of 36 numbers each but they are the dimension of a vector having 6x6 grid. however when I do the following it gives me an error, I suppose I need an extra step to convert this data from two columns to a grid?!
But I have no idea what this step could be. Any insights?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
data=np.loadtxt(r'text.dat')
x,y = np.meshgrid(np.arange(0, 6, 1), np.arange(0, 6, 1))
u = data[:,1]
v = data[:,2]
plt.quiver(x, y, u, v, angles='xy', scale_units='xy', scale=1)
You would benefit from a quick sanity check.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
data=np.loadtxt(r'text.dat')
x,y = np.meshgrid(np.arange(0, 6, 1), np.arange(0, 6, 1))
print x # 6x6 array, 2D!
print y # 6x6 array, 2D!
u = data[:,1] # 36 array, 1D!
v = data[:,2] # 36 array, 1D!
plt.quiver(x, y, u, v, angles='xy', scale_units='xy', scale=1)
Presumably your text.dat file holds records for points [(0,0), (1,0), (2,0), ... (0,1), (1,1), (2,1)... (4,5), (5,5)] in that order.
In such a case, you just need to flatten x and y to make them 1-dimensional. You can't mix 1D and 2D arrays in quiver.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
data=np.loadtxt(r'text.dat')
x,y = np.meshgrid(np.arange(0, 6, 1), np.arange(0, 6, 1))
x = x.flatten()
y = y.flatten()
print x # 36 array, 1D!
print y # 36 array, 1D!
u = data[:,1] # 36 array, 1D!
v = data[:,2] # 36 array, 1D!
plt.quiver(x, y, u, v, angles='xy', scale_units='xy', scale=1)
EDIT: If you continue having problems, run your commands in an interactive terminal or command prompt. Check each variable and its dimensions (array.shape) to make sure the variables are what you think they are. Are each of the dimensions actually 36?

Categories

Resources