Repository navigation
Description
Per this discussion on Twitter, the matplotlib account suggested we create a feature request for easier arrows. In thinking about how this could be addressed, I think it ties in with another frustration I have with matplotlib: drawing lots of line segments and both could be tackled with the same new function.
Problem
Visually, arrows can be used for a lot of different purposes. plt.annotate() is great for pointing to a feature and labeling it with text, but is not a solid solution for data-to-data pointing because the arrow tips don't fall in the center of the points, e.g.see this example.
plt.arrow() seems like the right solution. However, for general purposes, it has two main shortcomings. First, the scaling of the arrowhead is not automatic, so for certain scales of data the arrowheads look bizarre by default, and the arguments are not vectorized.
# it looks fine if we don't divide by 100, but if we do, the scaling is way off
x0 = np.random.uniform(0, 1, 5) / 100
y0 = np.repeat(0, 5)
dx = np.random.normal(0, 0.1, 5) / 100
dy = np.repeat(1, 5)
for i in range(5):
plt.arrow(x0[i], y0[i], dx[i], dy[i])Second, as the example shows, the arguments x, y, dx, and dy are not vectorized and explicit looping is required. This relates to another section of the API that's undesirably complex in my view: to draw lots of line segments, one needs to use a LineCollection and use ax.add_collection().
Proposed Solution
Matplotlib could solve both of these issues with a new ax.segments() method. This would take xto, xfrom, yto, and yfrom arguments, and ideally a vector of colors or labels too, and then plot the line segments. Then, one could pass an arrowstyle argument too, which would turn this into a collection of line segments with arrow ends. Also, I think in general it would be easier if the API used to and from coordinates rather than from and dx, dy coordinates. Finally, the default should be that the arrows are lines, rather than filled rectangles as with plt.arrow(), which create visually odd thin-width arrow bodies depending on the angle is steep between the two and from points.

Activity
timhoffm commentedon Mar 15, 2021
I agree with you that
plt.arrow()has a lot of shortcomings.That could probably be made configurable.
For vectorized arrows we have to solve two issues:
annotate()usesFancyArrowPatchinternally, which handles all the arrow style. One option would be to useLineCollectionand extend it for arrow functionality. Similar forPathCollection, which would be a little more general. Alternatively one would need a completely new Collection type, e.g.ArrowCollection.ax.add_collection(LineCollection(..., arrowstyle=...)). Having explicit functions likeax.segments()is convenient, but more work to develop and maintain. We don't do this for all functionality. Using*toand*fromonly makes sense for direct connections. That does not work if one wants arrow heads on more generalLineCollectionandPathCollection.Unfortunately, as simple as it sounds, doing this right needs careful consideration and a non-negligable amount of work.
jklymak commentedon Mar 15, 2021
Indeed arrows are non-trivial, and there are lots of attempts already in the library. I'm not sure we should add another until we are sure we know we don't already have something that works. I think its well-understood that
arrowis broken, and maybe needs a new backend.I'm not entirely clear what is being asked above, but it sounds like what
FancyArrowPatchis for? https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.FancyArrowPatch.html#matplotlib.patches.FancyArrowPatchThere is also
quiverwhich takes vectors or matrices. So, for sure, the above uses a loop, but I'm not sure that is worth adding a new Collection for. How many arrows do you need on a plot?TAdeJong commentedon Apr 17, 2021
Seconding
quiver()as a vector of arrows plotter, it does almost everything what @vsbuffalo listed, (if I understand everything correctly):The need of the manual limit scaling is unfortunate, but even plain linesegments can be achieved by setting
headlengthandheadaxislength:However, both these applications of
quiverdo not seem to be documented that well (I had to search quiet a bit on how to disable the arrowheads when I needed it for my own work).An example showcasing these usages and "see also's" in the documentation of
plt.arrowandplt.annotatemight already go a long way in solving this?kmdalton commentedon Feb 10, 2022
For plotting line segments, can't you just use
plt.plot? For example,Maybe there is some nuance I'm missing though.
github-actions commentedon Nov 29, 2023
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!