I would like to draw an arrow indicating the gradient of a function at a point, by pointing in the direction tangent to the function. I would like the length of this arrow to be proportional to the axis size, so that it is visible at any zoom level.
Say we want to draw the derivative of x^2
at x=1
(the derivative is 2
). Here are two things I tried:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.linspace(0, 2, 1000)
y = x**2
ax.plot(x, y)
x, y = (1.0, 1.0)
grad = 2.0
# Fixed size, wrong direction
len_pts = 40
end_xy = (len_pts, len_pts*grad)
ax.annotate("", xy=(x, y), xycoords='data',
xytext=end_xy, textcoords='offset points',
arrowprops=dict(arrowstyle='<-', connectionstyle="arc3"))
# Fixed direction, wrong size
len_units = 0.2
end_xy = (x+len_units, y+len_units*grad)
ax.annotate("", xy=(x, y), xycoords='data',
xytext=end_xy, textcoords='data',
arrowprops=dict(arrowstyle='<-', connectionstyle="arc3"))
ax.axis((0,2,0,2))
plt.show()
Here's what they look like at two zoom levels. To be clear, I want the red line's length and the black line's direction:
Optionally, you can enable drawing of an arrow from the text to the annotated point by giving a dictionary of arrow properties in the optional keyword argument arrowprops. In the example below, the xy point is in native coordinates (xycoords defaults to 'data'). For a polar axes, this is in (theta, radius) space.
Plot x and y with color=red and linewidth = 1. Use arrow method to add an arrow to the axes. The first two values in the arguments are the coordinates of the arrow base and the next two values are for the length of the arrow along X and Y direction. To display the figure, use show() method.
Quiver plot is basically a type of 2D plot which shows vector lines as arrows. This type of plots are useful in Electrical engineers to visualize electrical potential and show stress gradients in Mechanical engineering.
In your case, it sounds like you want quiver
. The scaling options are a bit confusing at first, and the default behavior is different than what you want. However, the entire point of quiver is to give you control over how size, angles, and scaling interact as you resize the plot.
For example:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0, 2, 1000)
y = x**2
ax.plot(x, y)
x0, y0 = 1.0, 1.0
dx, dy = 1, 2
length = 1.25 # in inches
dx, dy = length * np.array([dx, dy]) / np.hypot(dx, dy)
ax.quiver(x0, y0, dx, dy, units='inches', angles='xy', scale=1,
scale_units='inches', color='red')
ax.axis((0, 2, 0, 2))
plt.show()
The key part here is
units='inches', angles='xy', scale=1
angles='xy'
specifies that we want the rotation/angle of the arrow to be in data units (i.e. to match the gradient of the plotted curve, in this case).
scale=1
tells it not to autoscale the length of the arrow, and instead draw it the size that we give it with the units we've specified.
units='inches'
tells quiver
to interpret our dx, dy
as being in inches.
I'm not sure scale_units
is actually needed in this case (it should default to the same as units
), but it allows the arrow to have a different length unit than width unit.
And as I resize the plot, the angle stays in data units, but the length stays in inches (i.e. constant length on-screen):
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