David Folkner
David Folkner

Reputation: 1199

Matplotlib Indicate Point on X and Y Axis

I often want to highlight a point along a curve using matplotlib to make a plot that looks like: Basic Plot

The following code was used to create the plot

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

y  = np.array([0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100])/100.
x = 100. - np.array([
    99.79,98.96,98.65,98.39,98.13,97.88,97.61,97.33,97.01,96.65,96.21,
    95.72,95.16,94.46,93.52,92.31,90.66,88.48,84.04,79.34,19.32])
ax = plt.subplot(111)
line = plt.plot(x,y)


def highlight_point(ax,line,point,linestyle=':'):
    c = line.get_color()
    xmin = ax.get_xlim()[0]
    ymin = ax.get_ylim()[0]

    ax.plot([xmin,point[0]],[point[1],point[1]],color=c,linestyle=linestyle)
    ax.plot([point[0],point[0]],[ymin,point[1]],color=c,linestyle=linestyle)

plt.xlim([0,85])
plt.ylim([0,1])
highlight_point(ax,line[0],[x[10],y[10]])
plt.show()

The above method fails when the xlim and ylim are failed to be entered or if another plot were added to the figure later. I would like some combination of axhline or hlines where I can specify the left/bottom of the plot to a certain mathematical point.

Upvotes: 5

Views: 4870

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339220

A way to go could be to update the lines each time the canvas gets redrawn. To this end we could create a class PointMarkers with an update method that is connected to the draw_event listener. This way the lines will update not only if points are added after the marker lines' creation, but also when the canvas is resized or panned.

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

class PointMarker():
    def __init__(self, ax, point, **kwargs):
        self.ax = ax
        self.point = point
        if "line" in kwargs:
            self.c = kwargs.get("line").get_color()
        else:
            self.c = kwargs.get("color", "b")
        self.ls=kwargs.get("linestyle", ':')
        self.vline, = self.ax.plot([],[],color=self.c,linestyle=self.ls)
        self.hline, = self.ax.plot([],[],color=self.c,linestyle=self.ls)
        self.draw()

    def draw(self):
        xmin = ax.get_xlim()[0]
        ymin = ax.get_ylim()[0]
        self.vline.set_data([self.point[0], self.point[0]], [ymin,self.point[1]])
        self.hline.set_data([xmin, self.point[0]], [self.point[1], self.point[1]])

class PointMarkers():
    pointmarkers = []
    def add(self,ax, point, **kwargs ):
        pm = PointMarker(ax, point, **kwargs)
        self.pointmarkers.append(pm)
    def update(self, event=None):
        for pm in self.pointmarkers:
            pm.draw()

x = np.arange(1,17)
y = np.log(x)
ax = plt.subplot(111)
line = plt.plot(x,y)

# register the markers
p = PointMarkers()
p.add(ax,[x[5],y[5]], line=line[0])
p.add(ax,[x[12],y[12]], color="purple", linestyle="-.")
# connect event listener
cid = plt.gcf().canvas.mpl_connect("draw_event", p.update)

#testing: draw some new points or change axis limits
plt.plot([5,11],[-0.5,0.6])
#plt.xlim([0,85])
#plt.ylim([0,1])

plt.show()

enter image description here

For saving, the redrawing would need to be performed manually directly before the save command, like

plt.gcf().canvas.draw()
plt.savefig(...)

Upvotes: 5

Related Questions