How to add line based on slope and intercept
Asked Answered
P

12

95

In R, there is a function called abline in which a line can be drawn on a plot based on the specification of the intercept (first argument) and the slope (second argument). For instance,

plot(1:10, 1:10)
abline(0, 1)

where the line with an intercept of 0 and the slope of 1 spans the entire range of the plot. Is there such a function in Matplotlib?

Pyrophyllite answered 29/10, 2011 at 19:39 Comment(2)
No, there's not. It would be a handy function to have. There's axvline, axvspan, axhline, and axhspan, which are similar vertical and horizontal functions, but the usual way in matplotlib is to just plot a line at the given slope (which means that you'll eventually zoom beyond it, if you're working interactively.). The "correct" way of doing it (i.e. so that it's always spans the axis no matter where you zoom) is actually a bit complicated, though the framework (matplotlib.transforms) is there.Ihram
Yes, that's unfortunate... Matlab does not have this function either. On the other hand, R's plots are static (the base graphics system for which abline exists) so less to worry about there (it's a good and bad thing I suppose).Pyrophyllite
C
125

A lot of these solutions are focusing on adding a line to the plot that fits the data. Here's a simple solution for adding an arbitrary line to the plot based on a slope and intercept.

import matplotlib.pyplot as plt 
import numpy as np    

def abline(slope, intercept):
    """Plot a line from slope and intercept"""
    axes = plt.gca()
    x_vals = np.array(axes.get_xlim())
    y_vals = intercept + slope * x_vals
    plt.plot(x_vals, y_vals, '--')
Cob answered 5/5, 2017 at 18:38 Comment(4)
I can't believe this functionality isn't included directly in matplotlib. It seems like the very, very first thing one might implement if making such a package.Dissimilation
@Dissimilation Welcome to the world of sharing and caring! "Every good work of software starts by scratching a developer's personal itch" - Eric S. Raymond. Go and send pull request to the Matplotlib project!Staceestacey
just a minor point for completeness it needs import matplotlib.pyplot as plt import numpy as np at the start of the script.Kellam
Thanks for this nice answer. I took the liberty to add an answer which modifies your code to ensures that the sloped line does not expand the original plotting area.Empoison
H
46

As of 2021, in matplotlib 3.3.4, it supports drawing lines with slope value and a point.

fig, ax = plt.subplots()

ax.axline((0, 4), slope=3., color='C0', label='by slope')
ax.set_xlim(0, 1)
ax.set_ylim(3, 5) 
ax.legend()

enter image description here

Hylo answered 18/2, 2021 at 2:59 Comment(0)
M
39

I know this question is a couple years old, but since there is no accepted answer, I'll add what works for me.

You could just plot the values in your graph, and then generate another set of values for the coordinates of the best fit line and plot that over your original graph. For example, see the following code:

import matplotlib.pyplot as plt
import numpy as np

# Some dummy data
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 3, 2, 5, 7, 9]

# Find the slope and intercept of the best fit line
slope, intercept = np.polyfit(x, y, 1)

# Create a list of values in the best fit line
abline_values = [slope * i + intercept for i in x]

# Plot the best fit line over the actual values
plt.plot(x, y, '--')
plt.plot(x, abline_values, 'b')
plt.title(slope)
plt.show()
Musketry answered 9/12, 2013 at 18:6 Comment(0)
I
15

It looks like this feature will be part of version 3.3.0:

matplotlib.axes.Axes.axline

You'll be, for example, able to draw a red line through points (0, 0) and (1, 1) using

axline((0, 0), (1, 1), linewidth=4, color='r')
Inadmissible answered 6/2, 2020 at 9:12 Comment(0)
A
12
X = np.array([1, 2, 3, 4, 5, 6, 7])
Y = np.array([1.1,1.9,3.0,4.1,5.2,5.8,7])

scatter (X,Y)
slope, intercept = np.polyfit(X, Y, 1)
plot(X, X*slope + intercept, 'r')
Aun answered 7/5, 2014 at 18:19 Comment(0)
L
10

I couldn't figure a way to do it without resorting to callbacks, but this seems to work fairly well.

import numpy as np
from matplotlib import pyplot as plt


class ABLine2D(plt.Line2D):

    """
    Draw a line based on its slope and y-intercept. Additional arguments are
    passed to the <matplotlib.lines.Line2D> constructor.
    """

    def __init__(self, slope, intercept, *args, **kwargs):

        # get current axes if user has not specified them
        if not 'axes' in kwargs:
            kwargs.update({'axes':plt.gca()})
        ax = kwargs['axes']

        # if unspecified, get the current line color from the axes
        if not ('color' in kwargs or 'c' in kwargs):
            kwargs.update({'color':ax._get_lines.color_cycle.next()})

        # init the line, add it to the axes
        super(ABLine2D, self).__init__([], [], *args, **kwargs)
        self._slope = slope
        self._intercept = intercept
        ax.add_line(self)

        # cache the renderer, draw the line for the first time
        ax.figure.canvas.draw()
        self._update_lim(None)

        # connect to axis callbacks
        self.axes.callbacks.connect('xlim_changed', self._update_lim)
        self.axes.callbacks.connect('ylim_changed', self._update_lim)

    def _update_lim(self, event):
        """ called whenever axis x/y limits change """
        x = np.array(self.axes.get_xbound())
        y = (self._slope * x) + self._intercept
        self.set_data(x, y)
        self.axes.draw_artist(self)
Latish answered 15/1, 2013 at 22:56 Comment(2)
small improvement: swap the lines: ax.figure.canvas.draw() and self._update_lim(None) so the plot is actually updated without having to click the windowPouched
@Pouched At last on my version of matplotlib (1.4.3) it's necessary to render the parent axes at least once before calling self.axes.draw_artist(self), otherwise I get an AssertionError on the line assert self._cachedRenderer is not None in Axes.draw_artist. You could always insert an additional draw after _update_lim has been called. I usually initialize the ABLine from inside a convenience function that does this for me, rather than instantiating it directly.Latish
P
6

I suppose for the case of (intercept, slope) of (0, 1) the following function could be used and extended to accommodate other slopes and intercepts, but won't readjust if axis limits are changed or autoscale is turned back on.

def abline():
    gca = plt.gca()
    gca.set_autoscale_on(False)
    gca.plot(gca.get_xlim(),gca.get_ylim())

import matplotlib.pyplot as plt
plt.scatter(range(10),range(10))
abline()
plt.draw()
Pyrophyllite answered 29/10, 2011 at 21:36 Comment(2)
Well, if you just want a line that goes from the lower-left corner to the upper-right corner, no matter how you zoom, then you can just do plt.plot([0,1],[0,1], transform=plt.gca().transAxes). This won't represent a 1 to 1 slope in data coordinates, though, and it will always go from the lower left corner to the upper right, wherever you zoom to... Like you said, though, a more general abline replacement is more difficult for interactive use...Ihram
Ah, this is quite interesting the transAxes. I can imagine that I will be making use of it at some point... (I often have many plots where xlim=ylim, or should be).Pyrophyllite
E
6

I'd like to expand on the answer from David Marx, where we are making sure that the sloped line does not expand over the original plotting area. Since the x-axis limits are used to calculate the y-data for the sloped line, we need to make sure, that the calculated y-data does not extend the given ymin - ymax range. If it does crop the displayed data.

def abline(slope, intercept,**styles):
    """Plot a line from slope and intercept"""

    axes = plt.gca()
    xmin,xmax = np.array(axes.get_xlim())
    ymin,ymax = np.array(axes.get_ylim()) # get also y limits
    x_vals = np.linspace(xmin,xmax,num=1000) #increased sampling (only actually needed for large slopes)
    y_vals = intercept + slope * x_vals
    locpos = np.where(y_vals<ymax)[0] # if data extends above ymax
    locneg = np.where(y_vals>ymin)[0] # if data extends below ymin
    # select most restricitive condition 
    if len(locpos) >= len(locneg):
        loc = locneg
    else: 
        loc = locpos
    plt.plot(x_vals[loc], y_vals[loc], '--',**styles)
    return y_vals
Empoison answered 28/8, 2020 at 16:33 Comment(0)
V
2

Here's a possible workaround I came up with: suppose I have my intercept coordinates stored as x_intercept and y_intercept, and the slope (m) saved as my_slope which was found through the renowned equation m = (y2-y1)/(x2-x1), or in whichever way you managed to find it.

Using the other famous general equation for a line y = mx + q, I define the function find_second_point that first computes the q (since m, x and y are known) and then computes another random point that belongs to that line.

Once I have the two points (the initial x_intercept,y_intercept and the newly found new_x,new_y), I simply plot the segment through those two points. Here's the code:

import numpy as np
import matplotlib.pyplot as plt

x_intercept = 3  # invented x coordinate
y_intercept = 2  # invented y coordinate
my_slope = 1  # invented slope value

def find_second_point(slope,x0,y0):
    # this function returns a point which belongs to the line that has the slope 
    # inserted by the user and that intercepts the point (x0,y0) inserted by the user
    q = y0 - (slope*x0)  # calculate q
    new_x = x0 + 10  # generate random x adding 10 to the intersect x coordinate
    new_y = (slope*new_x) + q  # calculate new y corresponding to random new_x created

    return new_x, new_y  # return x and y of new point that belongs to the line

# invoke function to calculate the new point
new_x, new_y = find_second_point(my_slope , x_intercept, y_intercept)

plt.figure(1)  # create new figure
plt.plot((x_intercept, new_x),(y_intercept, new_y), c='r', label='Segment')
plt.scatter(x_intercept, y_intercept, c='b', linewidths=3, label='Intercept')
plt.scatter(new_x, new_y, c='g', linewidths=3, label='New Point')
plt.legend()  # add legend to image

plt.show()

here is the image generated by the code:

result image

Validity answered 23/10, 2018 at 14:44 Comment(0)
S
1

Short answer inspired by kite.com:

plt.plot(x, s*x + i) 

Reproducible code:

import numpy as np
import matplotlib.pyplot as plt
i=3        # intercept
s=2        # slope
x=np.linspace(1,10,50)      # from 1 to 10, by 50
plt.plot(x, s*x + i)        # abline
plt.show()

2x+3

Shebashebang answered 24/6, 2020 at 5:29 Comment(0)
A
1

You can write a simple function by converting Slope-Intercept form to 2-Point Form.

def mxline(slope, intercept, start, end):
    y1 = slope*start + intercept
    y2 = slope*end + intercept
    plt.plot([start, end], [y1, y2])

Calling the function

mxline(m,c, 0, 20)

OUTPUT

Graph of a Line

Assert answered 23/1, 2023 at 10:18 Comment(0)
R
0

One can simply create a list with the line's equation obtained from a particular intercept and slope. Put those values in a list and plot it against any set of numbers you would like. For example- (Lr being the Linear regression model)

te= []
for i in range(11):
    te.append(Lr.intercept_ + Lr.coef_*i)
plt.plot(te, '--')

Gets the job done.

Rorqual answered 5/2, 2021 at 15:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.