CLAUDE LABJP
TEACHERS — Anthropic launches Claude for Teachers, giving verified US K-12 educators free premium access, teaching skills, and curriculum links aligned to standards in all 50 statesADMIN — The Admin API is now in beta for every Claude Enterprise organization: member and invite calls need no beta header, while group and custom-role calls doM365 — The Microsoft 365 connector gains write tools — draft, send, and organize email, manage calendar events and mailbox settings, and create or update files in OneDrive and SharePointMCP — Fixed servers from --mcp-config or .mcp.json ignoring a per-server request_timeout_ms, which left long-running tool calls timing out at the 60s default in fresh sessionsSUBAGENT — A new --forward-subagent-text flag and CLAUDE_CODE_FORWARD_SUBAGENT_TEXT variable include subagent text and thinking in stream-json outputDEADLINE — Opus 4.7 fast mode is removed on July 24, seven days out. speed: "fast" will error, so confirm your move to Opus 4.8TEACHERS — Anthropic launches Claude for Teachers, giving verified US K-12 educators free premium access, teaching skills, and curriculum links aligned to standards in all 50 statesADMIN — The Admin API is now in beta for every Claude Enterprise organization: member and invite calls need no beta header, while group and custom-role calls doM365 — The Microsoft 365 connector gains write tools — draft, send, and organize email, manage calendar events and mailbox settings, and create or update files in OneDrive and SharePointMCP — Fixed servers from --mcp-config or .mcp.json ignoring a per-server request_timeout_ms, which left long-running tool calls timing out at the 60s default in fresh sessionsSUBAGENT — A new --forward-subagent-text flag and CLAUDE_CODE_FORWARD_SUBAGENT_TEXT variable include subagent text and thinking in stream-json outputDEADLINE — Opus 4.7 fast mode is removed on July 24, seven days out. speed: "fast" will error, so confirm your move to Opus 4.8
Articles/Claude Code
Claude Code/2026-05-03Advanced

Claude Code for Data Science — pandas, scikit-learn, and ML Workflows with AI Pair Programming

A hands-on guide to using Claude Code across the full data science and machine learning workflow. From EDA and feature engineering to model evaluation, hyperparameter tuning with Optuna, SHAP analysis, and MLOps basics — with working Python code throughout.

claude-code129data-sciencemachine-learningpython22pandas2scikit-learnmlops

Premium Article

Most data scientists I know use Claude Code for quick scripts and code completions. Far fewer have tried letting it drive the full pipeline — from raw data through EDA, feature engineering, model training, hyperparameter tuning, and into MLOps tooling.

I was in the first camp for a while. My data analysis lived in Jupyter notebooks, edited by hand, with Claude Code used occasionally for specific snippets. It took actually committing to the full workflow to realize how well-suited data science is for AI pair programming.

The reason is structural. Data science is fundamentally a loop: hypothesis → code → observe → revise. Claude Code excels at compressing that loop. The faster you can move from "I wonder if this feature helps" to "here's the validation curve showing it does," the better your work gets.

This article walks through the patterns that work in practice, with complete working code for each phase.

1. Project Setup: Using CLAUDE.md to Encode Data Science Constraints

Before writing a single line of analysis code, invest a few minutes in a CLAUDE.md that tells Claude Code how this project works. The payoff is substantial: Claude will automatically apply constraints you'd otherwise have to re-state in every prompt.

Installing the Stack

# Prompt to Claude Code:
"Create a requirements.txt for a data science project and install it
in a virtual environment. We need: pandas, numpy, scikit-learn,
matplotlib, seaborn, jupyterlab, optuna, shap, joblib"

Generated requirements.txt:

pandas==2.2.0
numpy==1.26.3
scikit-learn==1.4.0
matplotlib==3.8.2
seaborn==0.13.1
jupyterlab==4.1.0
optuna==3.5.0
shap==0.44.0
joblib==1.3.2
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt

The CLAUDE.md That Changes Everything

The constraints below aren't obvious to Claude Code without being told. Once they're in CLAUDE.md, you stop having to re-state them:

# Data Science Project Guidelines
 
## Stack
- Python 3.12, pandas 2.2, scikit-learn 1.4, matplotlib/seaborn
- Data: data/raw/ (source), data/processed/ (transformed)
- Models: models/ (pkl via joblib)
 
## Rules — Always Apply
- Always .copy() DataFrames before mutation
- Use RANDOM_STATE = 42 everywhere, consistently
- Split data BEFORE fitting any preprocessor (prevents data leakage)
- Save models with joblib.dump, load with joblib.load
- For datasets >500k rows, use chunked reading

With this in place, Claude Code will include .copy() on every DataFrame operation, set seeds across every library, and structure preprocessing to avoid leakage — without being asked each time.

2. EDA: Getting Claude Code to Do the Boring Parts

Exploratory data analysis is where projects start, and where a lot of time gets lost to boilerplate. The goal here is to get to insights fast, not to hand-write histogram loops.

A Complete EDA Report Generator

# Prompt to Claude Code:
# "Write src/eda_report.py that performs basic EDA on data/raw/sales.csv:
# - dtypes, missing values, summary statistics for numeric columns
# - histograms and boxplots for numeric features
# - value counts (top 10) for categorical columns
# - correlation heatmap
# Save all figures to reports/eda/ as PNG files."

Generated script:

# src/eda_report.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
 
RANDOM_STATE = 42
REPORTS_DIR = Path("reports/eda")
REPORTS_DIR.mkdir(parents=True, exist_ok=True)
 
def generate_eda_report(filepath: str) -> dict:
    """Run EDA and save figures. Returns summary dict."""
    df = pd.read_csv(filepath)
    report = {}
 
    report["shape"] = df.shape
    report["missing_pct"] = (df.isnull().sum() / len(df) * 100).to_dict()
 
    numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
    report["stats"] = df[numeric_cols].describe().to_dict()
 
    # Correlation heatmap
    if len(numeric_cols) > 1:
        fig, ax = plt.subplots(figsize=(12, 10))
        sns.heatmap(
            df[numeric_cols].corr(),
            annot=True, fmt=".2f",
            cmap="coolwarm", center=0, ax=ax
        )
        ax.set_title("Feature Correlation Matrix")
        fig.savefig(REPORTS_DIR / "correlation_heatmap.png",
                    dpi=150, bbox_inches="tight")
        plt.close(fig)
        print("[OK] Correlation heatmap saved.")
 
    # Histograms
    n_cols = 3
    n_rows = (len(numeric_cols) + n_cols - 1) // n_cols
    fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 5 * n_rows))
    axes = axes.flatten()
 
    for i, col in enumerate(numeric_cols):
        axes[i].hist(df[col].dropna(), bins=30, edgecolor="black", alpha=0.7)
        axes[i].set_title(
            f"{col} (missing: {report['missing_pct'][col]:.1f}%)"
        )
        axes[i].set_xlabel(col)
        axes[i].set_ylabel("Count")
 
    for j in range(i + 1, len(axes)):
        axes[j].set_visible(False)
 
    plt.tight_layout()
    fig.savefig(REPORTS_DIR / "histograms.png", dpi=150, bbox_inches="tight")
    plt.close(fig)
    print(f"[OK] Histograms saved ({len(numeric_cols)} features)")
 
    return report
 
if __name__ == "__main__":
    report = generate_eda_report("data/raw/sales.csv")
    print(f"\nShape: {report['shape']}")
    print("\nMissing values (%):")
    for col, pct in report["missing_pct"].items():
        if pct > 0:
            print(f"  {col}: {pct:.1f}%")

Outlier Detection: Comparing Three Strategies

Rather than picking an outlier strategy upfront, generate a comparison script that shows all three options side by side:

# src/outlier_handling.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
 
def detect_outliers_iqr(series: pd.Series, multiplier: float = 1.5) -> pd.Series:
    """Return Boolean mask of IQR-based outliers."""
    Q1, Q3 = series.quantile(0.25), series.quantile(0.75)
    IQR = Q3 - Q1
    return (series < Q1 - multiplier * IQR) | (series > Q3 + multiplier * IQR)
 
def compare_outlier_strategies(
    df: pd.DataFrame, col: str, output_dir: str = "reports/eda"
) -> dict:
    """Compare drop / clip / log-transform and save a side-by-side plot."""
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    mask = detect_outliers_iqr(df[col])
    print(f"Outliers detected: {mask.sum()} / {len(df)} "
          f"({mask.sum()/len(df)*100:.1f}%)")
 
    Q1, Q3 = df[col].quantile(0.25), df[col].quantile(0.75)
    IQR = Q3 - Q1
    lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
 
    df_drop = df[~mask].copy()
    df_clip = df.copy()
    df_clip[col] = df_clip[col].clip(lower=lower, upper=upper)
    df_log = None
    if df[col].min() > 0:
        df_log = df.copy()
        df_log[f"{col}_log"] = np.log1p(df_log[col])
 
    fig, axes = plt.subplots(1, 3, figsize=(18, 5))
    axes[0].hist(df_drop[col], bins=30, alpha=0.7, color="steelblue")
    axes[0].set_title(f"① Drop (n={len(df_drop)})")
    axes[1].hist(df_clip[col], bins=30, alpha=0.7, color="orange")
    axes[1].set_title(f"② Clip [{lower:.1f}, {upper:.1f}]")
    if df_log is not None:
        axes[2].hist(df_log[f"{col}_log"], bins=30, alpha=0.7, color="green")
        axes[2].set_title("③ Log Transform (log1p)")
    else:
        axes[2].text(0.5, 0.5, "Log transform unavailable\n(non-positive values)",
                     ha="center", va="center", transform=axes[2].transAxes)
    plt.suptitle(f"Outlier Strategy Comparison: '{col}'", fontsize=14)
    plt.tight_layout()
    fig.savefig(f"{output_dir}/outlier_comparison_{col}.png",
                dpi=150, bbox_inches="tight")
    plt.close(fig)
 
    return {"drop_n": len(df_drop), "lower": lower, "upper": upper}

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A reproducible layout that turns an exploratory notebook into six tested modules in ~25 minutes
Wiring data-leakage detection into CLAUDE.md — a real case that caught leakage in 3 of 11 features
A full production workflow: Optuna 100 trials (RMSE 0.0421->0.0388), pytest, and a single run script
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Claude Code2026-04-25
Claude Code × uv: Blazing-Fast Python Environment Setup — 10x Faster Than pip in Practice
Learn how to combine uv — Astral's Rust-powered Python package manager — with Claude Code for dramatically faster environment setup. Covers project initialization, dependency management automation, CI integration, and migrating from pip.
Claude Code2026-04-21
Claude Code × Python Hybrid Development Patterns: A Production Guide to 50% Token Reduction
Seven production-tested patterns for hybrid Claude Code × Python workflows, each with working code and real-world token reduction data.
Claude Code2026-07-17
The Morning My Table Ended in "… 2,847 more rows" — Separating Render Caps from Token Cost in Tool Output
Claude Code 2.1.209 caps markdown tables at 200 rows plus a remainder count. Only the rendering is capped — the model still receives every row. Here is how to measure the gap and redesign tool output around aggregates.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →