Scatter Plot with Linear Regression — lets-plot

A scatter plot that displays the relationship between two numeric variables with a fitted linear regression line and confidence interval band. This visualization extends the basic scatter plot by adding statistical modeling elements, making it ideal for understanding linear relationships, assessing model fit, and communicating the strength of correlations with visual uncertainty quantification.

Scatter Plot with Linear Regression rendered with lets-plot

Renders

Python source (lets-plot)

""" anyplot.ai
scatter-regression-linear: Scatter Plot with Linear Regression
Library: letsplot 4.11.0 | Python 3.13.14
Quality: 85/100 | Updated: 2026-08-05
"""

import os

import numpy as np
import pandas as pd
from lets_plot import *
from lets_plot.export import ggsave as export_ggsave


LetsPlot.setup_html()

# Theme tokens (Imprint)
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
GRID_COLOR = "rgba(26,26,23,0.15)" if THEME == "light" else "rgba(240,239,232,0.15)"
BRAND = "#009E73"  # Imprint palette position 1
SECONDARY = "#C475FD"  # Imprint palette position 2 — regression line + CI band

# Data - Advertising spend vs sales revenue relationship
np.random.seed(42)
n = 100
advertising_spend = np.random.uniform(10, 100, n)
sales_revenue = 2.5 * advertising_spend + 50 + np.random.randn(n) * 25

df = pd.DataFrame({"advertising_spend": advertising_spend, "sales_revenue": sales_revenue})

# Regression statistics (closed-form OLS, so the annotation matches geom_smooth exactly)
x_mean = np.mean(advertising_spend)
y_mean = np.mean(sales_revenue)
slope = np.sum((advertising_spend - x_mean) * (sales_revenue - y_mean)) / np.sum((advertising_spend - x_mean) ** 2)
intercept = y_mean - slope * x_mean

y_pred = slope * advertising_spend + intercept
ss_res = np.sum((sales_revenue - y_pred) ** 2)
ss_tot = np.sum((sales_revenue - y_mean) ** 2)
r_squared = 1 - (ss_res / ss_tot)

equation_text = f"y = {slope:.2f}x + {intercept:.1f}"
r2_text = f"R² = {r_squared:.3f}"
annotation_text = f"{equation_text}\n{r2_text}"

annotation_x = 15
annotation_y = sales_revenue.max() - 8

# Plot
plot = (
    ggplot(df, aes(x="advertising_spend", y="sales_revenue"))
    + geom_point(
        shape=21,
        fill=BRAND,
        color=PAGE_BG,
        stroke=0.6,
        size=3,
        alpha=0.65,
        tooltips=layer_tooltips()
        .line("Ad Spend|$@advertising_spend{.1f}K")
        .line("Sales|$@sales_revenue{.1f}K"),
    )
    + geom_smooth(
        method="lm",
        color=SECONDARY,
        size=1.6,
        se=True,
        level=0.95,
        fill=SECONDARY,
        fill_alpha=0.15,
        tooltips=layer_tooltips()
        .line("Fitted|@..y..")
        .line("95% CI|[@..ymin.., @..ymax..]"),
    )
    + geom_label(
        x=annotation_x,
        y=annotation_y,
        label=annotation_text,
        size=4.5,
        color=INK,
        fill=ELEVATED_BG,
        label_size=0,
        family="sans-serif",
        hjust=0,
        vjust=1,
        alpha=0.92,
    )
    + labs(
        x="Advertising Spend ($K)", y="Sales Revenue ($K)", title="scatter-regression-linear · letsplot · anyplot.ai"
    )
    + scale_x_continuous(expand=(0.03, 0))
    + scale_y_continuous(expand=(0.05, 0))
    + ggmarginal(
        "tr",
        size=0.09,
        layer=geom_density(color=BRAND, fill=BRAND, alpha=0.25, size=0.8),
    )
    + ggsize(800, 450)
    + theme_minimal()
    + theme(
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        panel_background=element_rect(fill=PAGE_BG),
        panel_border=element_blank(),
        panel_grid_major=element_line(color=GRID_COLOR, size=0.6),
        panel_grid_minor=element_blank(),
        axis_text=element_text(size=10, color=INK_SOFT),
        axis_title=element_text(size=12, color=INK),
        plot_title=element_text(size=16, color=INK),
        axis_line_x=element_line(color=INK_SOFT, size=0.6),
        axis_line_y=element_line(color=INK_SOFT, size=0.6),
        axis_ticks=element_blank(),
    )
)

# Save outputs
export_ggsave(plot, filename=f"plot-{THEME}.png", path=".", scale=4)
export_ggsave(plot, filename=f"plot-{THEME}.html", path=".")

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/scatter-regression-linear/letsplot/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "scatter-regression-linear",
  "language": "python",
  "library": "letsplot",
  "page": "https://anyplot.ai/scatter-regression-linear/python/letsplot",
  "hub": "https://anyplot.ai/scatter-regression-linear",
  "code_json": "https://api.anyplot.ai/specs/scatter-regression-linear/letsplot/code",
  "spec_json": "https://api.anyplot.ai/specs/scatter-regression-linear",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-linear/python/letsplot/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-linear/python/letsplot/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-linear/python/letsplot/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/scatter-regression-linear/python/letsplot/plot-dark.html",
  "quality_score": 85.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Scatter Plot with Linear Regression on anyplot.ai.

Other implementations