Horizontal line to infinity on one side only in matplotlib

Viewed 49

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.

2 Answers

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

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

Related