2

I'd like to plot a line that goes to infinity, but starting from a finite point. For simplicity, let's say that the line can be horizontal. I would like to plot a line from (0, 0) to (inf, 0).

Using hlines:

>>> fig, ax = plt.subplots()
>>> ax.hlines(0, 0, np.inf)
.../python3.8/site-packages/matplotlib/axes/_base.py:2480: UserWarning: Warning: converting a masked element to nan.
  xys = np.asarray(xys)

The result is an empty plot.

axhline has a starting parameter, but it is in axis coordinates rather than data. Similar problem for axline. Is there a way to plot a (horizontal) line with one end in data coordinates and the other at infinity?

The motivation behind this is that I'd like to be able to plot some cumulative probabilities without setting data past the last bin to zero, as here: Matplotlib cumulative histogram - vertical line placement bug or misinterpretation?. Rather than simply ending the histogram, I'd like to be able to extend the line from the last bin to infinity at y=1.0.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • 1
    The link you provided tells the following: "Matplotlib is smart enough to not try to draw non-finite values". Is there any specific reason to define the value as infinity? Because you can only see the visible area, `ax.hlines(0, ax.get_xlim()[0], ax.get_xlim()[1])` seems to be sufficient. – J. Choi Aug 22 '22 at 06:42
  • @J.Choi. Xlim will give you a static value that will not work as desired in an interactive plot. I'm therefore looking for hlines on one side, and something like axhline on the other. While I understand that matplotlib *normally* checks for finite values, axhlines and axline clearly indicate that there's a way to go to infinity. If this was trivial, I'd have done my best not to ask. – Mad Physicist Aug 22 '22 at 08:30

2 Answers2

2

There's no built-in function for this, but you can re-draw the line to the axis limit on each change of the x limits.

From Axes:

The events you can connect to are 'xlim_changed' and 'ylim_changed' and the callback will be called with func(ax) where ax is the Axes instance.

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

def hline_to_inf(ax, x, y):
    line = ax.hlines(0, 0, ax.get_xlim()[1])
    ax.callbacks.connect('xlim_changed',
                         lambda ax: line.set_paths([[[x, y], [ax.get_xlim()[1], y]]]))

hline_to_inf(ax, 0, 0)

plt.show() 

enter image description here

Stef
  • 28,728
  • 2
  • 24
  • 52
  • That is indeed very neat. – Mad Physicist Aug 22 '22 at 13:22
  • I've found a built-in that does this. Let me know if you have any thoughts about the two approaches. – Mad Physicist Aug 22 '22 at 17:17
  • 1
    nice approach (+1), although I guess it's computationally more intensive: if I add 100 lines and pan the axes as shown in my gif the plot with ConnectionPatch moves a bit sluggishly/jerkily on my computer whereas in the xlim approach it moves smoothly (which of course for your use case with just one line doesn't play any role) – Stef Aug 22 '22 at 18:18
  • Thanks. If it's just a utility function either way, speed is definitely a good discriminator. – Mad Physicist Aug 22 '22 at 18:19
2

Part of the issue is that normal plotting methods apply the same transform to the input data. What is required here is to apply a data transform to the start point, and a blended transform to the endpoint. It seems that there may be an answer using existing tools with ConnectionPatch, as explained in the Annotations Guide. The idea is to make the left point use data coordinates and the right point have a blended transform with x in axes coordinates and y in data.

from matplotlib import pyplot as plt
from matplotlib.patches import ConnectionPatch

fig, ax = plt.subplots()
line, = ax.plot([1, 2], [1, 2])
ax.add_artist(ConnectionPatch([2, 2], [1, 2], coordsA=ax.transData, coordsB=ax.get_yaxis_transform(), color=line.get_color(), linewidth=line.get_linewidth(), clip_on=True))

enter image description here

Turning on clipping is necessary, otherwise you could end up with artifacts that look like this:

enter image description here

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264