Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions scripts/_mpl_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Matplotlib compatibility helpers for workbook scripts.

Matplotlib 3.9 renamed the Axes.boxplot keyword argument "labels" to
"tick_labels". The old name is deprecated and scheduled for removal.

These helpers keep our educational scripts working on Matplotlib 3.8+
while avoiding deprecation warnings on newer versions.
"""

from __future__ import annotations

from typing import Any, Sequence


def ax_boxplot(
ax: Any,
*args: Any,
tick_labels: Sequence[str] | None = None,
**kwargs: Any,
):
"""Call ``ax.boxplot`` with a 3.8/3.9+ compatible keyword.

Prefer ``tick_labels`` (Matplotlib >= 3.9). If that keyword is not
supported (Matplotlib <= 3.8), fall back to the legacy ``labels``.
"""

if tick_labels is None:
return ax.boxplot(*args, **kwargs)

try:
return ax.boxplot(*args, tick_labels=tick_labels, **kwargs)
except TypeError:
# Older Matplotlib: the new keyword doesn't exist.
return ax.boxplot(*args, labels=tick_labels, **kwargs)
3 changes: 2 additions & 1 deletion scripts/ch14_tutoring_ab.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from scipy import stats

from scripts._cli import base_parser, apply_seed
from scripts._mpl_compat import ax_boxplot


def cohens_d(x: np.ndarray, y: np.ndarray) -> float:
Expand Down Expand Up @@ -95,7 +96,7 @@ def main() -> None:

# Plot
fig, ax = plt.subplots(figsize=(6, 5))
ax.boxplot([control, tutor], labels=["Control", "Tutor"], patch_artist=True)
ax_boxplot(ax, [control, tutor], tick_labels=["Control", "Tutor"], patch_artist=True)
ax.set_title(f"Test Scores: Control vs Tutor (n={len(control)} per group)")
ax.set_ylabel("Score")
ax.grid(axis="y", linestyle=":", alpha=0.7)
Expand Down
9 changes: 8 additions & 1 deletion scripts/intro_stats_01_descriptives.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,20 @@ def main() -> None:
try:
import matplotlib.pyplot as plt

# Matplotlib 3.9 renamed `labels` -> `tick_labels` for boxplots.
# Use a small compatibility helper to avoid warnings now and breaks later.
try:
from scripts._mpl_compat import ax_boxplot
except ImportError: # pragma: no cover
from _mpl_compat import ax_boxplot # type: ignore

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)

# Keep ordering stable.
order = ["control", "treatment"]
data = [df.loc[df["group"] == g, "score"].to_numpy() for g in order]
ax.boxplot(data, labels=order)
ax_boxplot(ax, data, tick_labels=order)
ax.set_title("Intro Stats: score by group")
ax.set_ylabel("score")

Expand Down
13 changes: 11 additions & 2 deletions scripts/intro_stats_03_distributions_outliers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
import pandas as pd


# Matplotlib 3.9 renamed `labels` -> `tick_labels` for Axes.boxplot.
# This import works when run via `python -m scripts...` (package import),
# and also when run as a plain script from the `scripts/` folder.
try:
from scripts._mpl_compat import ax_boxplot
except ImportError: # pragma: no cover
from _mpl_compat import ax_boxplot # type: ignore


def _iqr_outliers(df: pd.DataFrame, *, group_col: str, value_col: str) -> pd.DataFrame:
"""Return a table of IQR outliers per group (may be empty)."""

Expand Down Expand Up @@ -93,8 +102,8 @@ def main() -> None:
# Boxplot
order = ["control", "treatment"]
data = [df.loc[df["group"] == g, "score"] for g in order if g in set(df["group"])]
labels = [g for g in order if g in set(df["group"])]
axes[1].boxplot(data, labels=labels)
tick_labels = [g for g in order if g in set(df["group"])]
ax_boxplot(axes[1], data, tick_labels=tick_labels)
axes[1].set_title("Boxplot (outliers shown)")
axes[1].set_ylabel("Score")

Expand Down
11 changes: 10 additions & 1 deletion scripts/psych_ch19_problem_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
from scipy.stats import chi2_contingency, chisquare, kruskal, mannwhitneyu


# Matplotlib 3.9 renamed `labels` -> `tick_labels` for Axes.boxplot.
# This import works both when run via `python -m scripts...` and when executed
# as a plain script from the `scripts/` folder.
try:
from scripts._mpl_compat import ax_boxplot
except ImportError: # pragma: no cover
from _mpl_compat import ax_boxplot # type: ignore


PROJECT_ROOT = Path(__file__).resolve().parents[1]
SYNTHETIC_DATA_DIR = PROJECT_ROOT / "data" / "synthetic"
TRACK_C_OUTPUT_DIR = PROJECT_ROOT / "outputs" / "track_c"
Expand Down Expand Up @@ -108,7 +117,7 @@ def _plot_boxplot(df: pd.DataFrame, outfile: Path, title: str) -> None:
fig, ax = plt.subplots(figsize=(7, 4))
groups = list(df["group"].cat.categories)
data = [df.loc[df["group"] == g, "score"].to_numpy() for g in groups]
ax.boxplot(data, labels=[str(g) for g in groups], showfliers=False)
ax_boxplot(ax, data, tick_labels=[str(g) for g in groups], showfliers=False)
ax.set_xlabel("Group")
ax.set_ylabel("Score")
ax.set_title(title)
Expand Down