Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arrow direction set by data, but length set by figure size

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:

enter image description hereenter image description here

like image 795
supergra Avatar asked Jul 17 '15 00:07

supergra


People also ask

How do I annotate an arrow in Matplotlib?

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.

How do I add an arrow to a line in Matplotlib?

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.

What is quiver in matplotlib?

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.


1 Answers

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()

enter image description here

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):

enter image description here

like image 176
Joe Kington Avatar answered Oct 19 '22 03:10

Joe Kington