The same plot in 14 other libraries — Python: Altair, Bokeh, lets-plot, Matplotlib, Plotly, Pygal, Seaborn; R: ggplot2; Julia: Makie.jl; JavaScript: Chart.js, D3.js, Apache ECharts, Highcharts, MUI X Charts. Compare all 15 side by side: Logistic Regression Curve Plot in Python, R, Julia and JavaScript.
A logistic regression visualization showing the characteristic S-shaped (sigmoid) probability curve for binary classification. The plot displays data points colored by their binary class, the fitted logistic curve representing predicted probabilities, confidence intervals around the curve, and a dashed decision threshold line at probability 0.5. This visualization is essential for understanding how a logistic model maps continuous input features to class probabilities.

""" anyplot.ai
logistic-regression: Logistic Regression Curve Plot
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 94/100 | Updated: 2026-05-18
"""
import os
import numpy as np
import pandas as pd
import statsmodels.api as sm
from plotnine import (
aes,
element_line,
element_rect,
element_text,
geom_hline,
geom_line,
geom_point,
geom_ribbon,
ggplot,
labs,
position_jitter,
scale_color_manual,
theme,
theme_minimal,
)
# Theme tokens
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"
# imprint semantic anchors
FAIL_COLOR = "#AE3030" # imprint red - fail
PASS_COLOR = "#009E73" # imprint green - pass
CURVE_COLOR = "#4467A3" # imprint blue - regression curve
# Data - Exam score vs Pass/Fail outcome
np.random.seed(42)
n_samples = 150
# Generate exam scores with different distributions for pass/fail
scores_fail = np.random.normal(45, 12, 60) # Lower scores tend to fail
scores_pass = np.random.normal(70, 10, 90) # Higher scores tend to pass
scores = np.concatenate([scores_fail, scores_pass])
outcomes = np.concatenate([np.zeros(60), np.ones(90)])
# Add some noise to outcomes for realism
flip_indices = np.random.choice(n_samples, size=15, replace=False)
outcomes[flip_indices] = 1 - outcomes[flip_indices]
# Clip scores to reasonable range
scores = np.clip(scores, 20, 100)
# Fit logistic regression using statsmodels
X = sm.add_constant(scores)
model = sm.Logit(outcomes, X).fit(disp=0)
# Create smooth curve for predictions with confidence intervals
x_curve = np.linspace(20, 100, 200)
X_curve = sm.add_constant(x_curve)
predictions = model.get_prediction(X_curve)
y_pred = predictions.predicted
conf_int = predictions.conf_int(alpha=0.05)
y_lower = conf_int[:, 0]
y_upper = conf_int[:, 1]
# Create dataframes
df_points = pd.DataFrame(
{"score": scores, "outcome": outcomes, "class": ["Fail" if o == 0 else "Pass" for o in outcomes]}
)
df_curve = pd.DataFrame({"score": x_curve, "probability": y_pred, "lower": y_lower, "upper": y_upper})
# Theme
anyplot_theme = theme(
figure_size=(16, 9),
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
panel_background=element_rect(fill=PAGE_BG),
panel_grid_major=element_line(color=INK, size=0.3, alpha=0.08),
panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.04),
panel_border=element_rect(color=INK_SOFT, fill=None, size=0.5),
axis_title=element_text(size=20, color=INK),
axis_text=element_text(size=16, color=INK_SOFT),
axis_line=element_line(color=INK_SOFT, size=0.5),
plot_title=element_text(size=24, color=INK),
legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
legend_text=element_text(size=16, color=INK_SOFT),
legend_title=element_text(size=18, color=INK),
)
# Create plot
plot = (
ggplot()
# Confidence interval ribbon
+ geom_ribbon(data=df_curve, mapping=aes(x="score", ymin="lower", ymax="upper"), alpha=0.25, fill=CURVE_COLOR)
# Fitted logistic curve
+ geom_line(data=df_curve, mapping=aes(x="score", y="probability"), color=CURVE_COLOR, size=2)
# Decision threshold line at p=0.5
+ geom_hline(yintercept=0.5, linetype="dashed", color=INK_SOFT, size=1, alpha=0.6)
# Data points with jitter
+ geom_point(
data=df_points,
mapping=aes(x="score", y="outcome", color="class"),
size=4,
alpha=0.6,
position=position_jitter(width=0, height=0.03),
)
# Colors
+ scale_color_manual(values={"Fail": FAIL_COLOR, "Pass": PASS_COLOR})
# Labels
+ labs(
title="logistic-regression · python · plotnine · anyplot.ai",
x="Exam Score (points)",
y="Probability of Passing",
color="Outcome",
)
+ theme_minimal()
+ anyplot_theme
)
# Save
plot.save(f"plot-{THEME}.png", dpi=300)
Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/logistic-regression/plotnine/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": "logistic-regression",
"language": "python",
"library": "plotnine",
"page": "https://anyplot.ai/logistic-regression/python/plotnine",
"hub": "https://anyplot.ai/logistic-regression",
"code_json": "https://api.anyplot.ai/specs/logistic-regression/plotnine/code",
"spec_json": "https://api.anyplot.ai/specs/logistic-regression",
"render_light_png": "https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/plotnine/plot-light.png",
"render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/logistic-regression/python/plotnine/plot-dark.png",
"quality_score": 94.0,
"license": "MIT",
"guide": "https://anyplot.ai/llms.txt"
}Part of Logistic Regression Curve Plot on anyplot.ai.