Course project template
First run
uv sync # Python env (uv fetches Python 3.12 itself)
uv sync --group ml # add scikit-learn, xgboost, shap (~2-3 GB)
uv sync --group llm # add the Claude API client
claude # start Claude Code in this folder
Inside Claude Code: /permissions shows what it may touch here, /usage shows
your remaining weekly allowance.
Layout
data/raw/ original files, never edited
data/restricted/ confidential microdata — Claude cannot read this folder
data/processed/ cleaned, de-identified files your scripts produce
src/analysis/ reusable cleaning, modelling and LLM-coding functions
scripts/ numbered runnable steps (01_clean.py, 02_model.py, ...)
notebooks/ exploration; import from src/, don't redefine logic there
report/ the HTML/JS report — index.html + data.json
outputs/ figures and tables, regenerated by scripts
The report
report/index.html is a self-contained animated chart: no build step, no CDN, no
framework. It reads report/data.json, which scripts/03_export_report_data.py
writes from your analysis. That one-way flow is the point — the figure cannot
drift from the numbers, because it has no other source for them.
Preview it (HTML needs a server; opening the file directly blocks fetch):
npm run dev # serves report/ on port 8080
Working on the course server? Forward the port from your own machine:
ssh -L 8080:localhost:8080 yourlogin@investigatingwithai.com
then open http://localhost:8080.
The chart already does the things that are easy to get wrong: a zero-based axis,
a legend plus direct labels so identity is never colour alone, a hover tooltip, a
data-table toggle, a colourblind-safe palette, dark mode, and animation that
respects prefers-reduced-motion. Change the data, keep the structure.
Machine learning
uv sync --group ml. Two rules that matter more here than the code:
- Split before you look. Fit on train, tune on validation, report on test, once.
- A model that predicts well is not an explanation. If the write-up says a variable "drives" an outcome, it needs an identification strategy, not a SHAP plot.
LLM calls in your own code
src/analysis/llm.py shows the standard pattern: coding open-ended survey
answers into categories with a versioned codebook and a validated output schema.
This is billed separately from your Claude Code seat. It needs an
ANTHROPIC_API_KEY from platform.claude.com. Ask your instructor before running
anything at scale.
Treat the model as a coder, not an oracle: hand-code a sample first, compare, report the agreement rate in the write-up.
Git and GitHub
git init && git add -A && git commit -m "first commit"
gh auth login # once per account, on the server
gh repo create my-analysis --private --source=. --push
.gitignore already excludes data/ and outputs/. Keep it that way — survey
microdata in a git history is very hard to remove, and a private repo is one
setting away from being public.
Custom commands
/explore-data <path>— first-pass description of a dataset/check-repro— verify the project runs end to end/explain— plain-language reading of the last result
Rules
Read CLAUDE.md. The same rules apply to you and to Claude.