pythonmatplotlibvectoraxis

Positions of vectors wrong in matplotlib.pyplot.quiver


I am trying to demonstrate that $v_1 + t(v_2 - v_1)$ is a parameterized line using vectors $p_k=v_1 + \frac{k}{8}(v_2 - v_1)$ for $k=0\ldots 8$ with tails at the origin, and then the vector $u=v_2-v_1$ with its tail at $v_1$.

Automatic scaling gives unsatisfactory results, and after setting xlim and ylim and scale, the placement the vectors, and in particular of $u=v_2-v_1$ with its tail at $v_1$, are off.

import numpy as np
import matplotlib.pyplot as plt

VV=[
    [ 4, 12,  5, 6, 7, 8, 9, 10, 11,   8],
    [12, -4, 10, 8, 6, 4, 2,  0, -2, -16]
]
OV=[
    [0,0,0,0,0,0,0,0,0, 4],
    [0,0,0,0,0,0,0,0,0,12]
]

plt.quiver(*OV, VV[0],VV[1],
           color=['b','b']+['k' for j in range(len(VV[0])-3)]+['r'],
          scale=34,
          pivot='tail'
          )
plt.xlim([-10, 24])
plt.ylim([-16, 18])
plt.show()

The expected result would be the blue vectors from $(0,0)$ to $(4,12)$ and from $(0,0)$ to $(12,-4)$, (so at right angles) with a fan of vectors (in black) from the origin to the segment between them, plus a red vector from $(4,12)$ to $(12,-4)$.

The upper blue vector appears to have its tip around $(4,16)$, and the red vector should go from the tip of one blue vector to the tip of the other.


Solution

  • So, a couple of issues. The first is your scale. You should use a scale of 1 so the arrows are the exact length and not some scaled version of the arrows. Along with that you need to set the scale_units to xy so the scale is interpreted to be based on the xy coordinates and not based on axes size.

    Next, you want to use the angles="xy" option so that matplotlib interprets the values as xy-coordinates and not based on the display coordinates.

    Lastly, you should set the plot aspect ratio to 1 so that if your x-axis is 2 units long and your y-axis is 5 units tall your plot doesn't look skewed. This is done using plt.gca() to get the plot axis (gca stands for "get current axis") and use the set_aspect method.

    Final code:

    import numpy as np
    import matplotlib.pyplot as plt
    
    VV=[
        [ 4, 12,  5, 6, 7, 8, 9, 10, 11,   8],
        [12, -4, 10, 8, 6, 4, 2,  0, -2, -16]
    ]
    OV=[
        [0,0,0,0,0,0,0,0,0, 4],
        [0,0,0,0,0,0,0,0,0,12]
    ]
    
    plt.quiver(*OV, VV[0],VV[1],
               color=['b','b']+['k' for j in range(len(VV[0])-3)]+['r'],
              scale=1,
              scale_units="xy",
              pivot="tail",
              angles="xy",
              )
    plt.xlim([-10, 24])
    plt.ylim([-16, 18])
    plt.gca().set_aspect(1)
    plt.grid()
    plt.show()
    

    Resulting plot: