Skip to content

Add add_abline for drawing a line from a slope and an intercept - #5668

Open
kirthi-b wants to merge 1 commit into
plotly:mainfrom
kirthi-b:feat/add-abline
Open

Add add_abline for drawing a line from a slope and an intercept#5668
kirthi-b wants to merge 1 commit into
plotly:mainfrom
kirthi-b:feat/add-abline

Conversation

@kirthi-b

@kirthi-b kirthi-b commented Jul 19, 2026

Copy link
Copy Markdown

Link to issue

Closes #3166

Description of change

Adds Figure.add_abline(slope=..., intercept=...), which draws a straight line from a slope and an intercept the way matplotlib's axline or R's abline do. The signature and behavior follow add_hline and add_vline, including row, col, exclude_empty_subplots, annotation, and the annotation_* keywords, and extra kwargs are forwarded to add_shape.

Demo

add_abline demo: a 1:1 reference line and a least-squares fit line drawn with add_abline

Left panel is a 1:1 reference line on a predicted vs actual plot, right panel is a least squares fit drawn from its own slope and intercept. Both use the annotation keywords, and both are targeted at a specific subplot with row and col.

import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

rng = np.random.default_rng(7)
x = np.linspace(0, 10, 60)
y = 0.8 * x + 1.2 + rng.normal(0, 1.0, x.size)
slope, intercept = np.polyfit(x, y, 1)

fig = make_subplots(rows=1, cols=2, subplot_titles=("Predicted vs actual", "Least-squares fit"))
fig.add_trace(go.Scatter(x=x, y=y, mode="markers"), row=1, col=1)
fig.add_abline(slope=1, intercept=0, row=1, col=1,
               line_color="#d1495b", line_dash="dash",
               annotation_text="y = x", annotation_position="top left")

fig.add_trace(go.Scatter(x=x, y=y, mode="markers"), row=1, col=2)
fig.add_abline(slope=slope, intercept=intercept, row=1, col=2,
               line_color="#118a7e", line_width=3,
               annotation_text=f"y = {slope:.2f}x + {intercept:.2f}",
               annotation_position="bottom right")

fig.show()

Testing strategy

New tests in tests/test_optional/test_autoshapes/test_add_abline.py cover basic usage, a custom slope and intercept, the priority of an explicit axis range over a data derived range, the fallback when there is no data and no range, subplot targeting with row and col, exclude_empty_subplots, and the annotation keywords. To try it by hand, check out the branch and run the snippet above, then run pytest tests/test_optional/test_autoshapes/test_add_abline.py.

Additional information (optional)

One behavior is worth calling out, since it came up in the issue discussion about "infinite" lines. add_hline and add_vline stay pinned to the plot edges through pan and zoom because they use plotly's axis domain reference for one dimension, which plotly.js recalculates against the visible range on every render. A line with an arbitrary slope cannot use that trick, because domain references only work when one axis is held constant, not when both move together.

So add_abline computes its endpoints once, at the moment it is called, from the axis's current range, or from the data already plotted if no range has been set. Those endpoints are then ordinary fixed data coordinates like any shape added with add_shape. If you pan or zoom afterwards, or add data outside the original range, the line will not move or extend to follow. This is documented in the docstring and on the shapes doc page, and calling add_abline again picks up the new range.

Annotation placement reuses the existing vline positioning code in shapeannotation.py, which was already written generically enough to handle a slanted line's endpoints (its own comment says as much).

Guidelines

Figure.add_abline(slope=1, intercept=0, ...) draws a straight line the
way matplotlib's axline or R's abline do, computing the line's
endpoints from the current x-axis range (or, if that range hasn't been
set explicitly, from the data already plotted on that axis) at the
time it is called.

The method mirrors the signature style of add_hline/add_vline: slope
and intercept play the role of y/x, and row, col,
exclude_empty_subplots, annotation and annotation_* all behave the
same way. Annotation placement reuses the existing vline positioning
logic in shapeannotation.py, which was already written generically
enough to place text relative to a slanted line's endpoints.

Unlike add_hline/add_vline, the new line is not anchored to the plot
edges: its endpoints are ordinary data coordinates fixed at add time,
so panning or zooming afterwards won't move or extend it. This is
called out explicitly in the docstring and in the shapes doc page.

Closes plotly#3166
@camdecoster

Copy link
Copy Markdown
Contributor

Thanks for the PR! Could you please update the PR description to use the template? We need to see screenshots/videos along with testing instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement the equivalent of abline (plot line using slope/intercept only) to add line to graph

2 participants