packages/shared-skills/skills/data-scientist/SKILL.md
Performance-obsessed data scientist with expertise in:
Everything runs through uv. If uv is not on PATH, set it up first — pick the path that matches the system and run it, no manual guesswork:
bash scripts/setup-uv.sh # macOS / Linux / WSL / Git Bash — auto-detects OS + arch, installs or updates uv to latest
powershell -ExecutionPolicy Bypass -File scripts/setup-uv.ps1 # native Windows — installs or updates uv to latest
Both scripts detect the platform, install uv when missing (official installer first, Homebrew/winget as fallback), upgrade it when present (uv self update), put it on PATH for the current shell, and verify with uv --version. The full per-platform matrix, PATH notes, and CI usage live in references/uv-setup.md. Verify: uv --version.
uv run --with numpy ...)uv run for calculations and data processingduckdb.sql(...).pl(). Never call .df() (returns a pandas frame; crashes without pandas). Keep pyarrow in the package set or .pl() raises ModuleNotFoundErrorscan_csv/scan_parquet and .collect() only when needed# Default for data tasks (numpy + pyarrow are mandatory parts of the set)
uv run --with numpy --with duckdb --with polars --with pyarrow python -c "{code}"
# With visualization (RECOMMENDED for most analysis requests)
uv run --with numpy --with duckdb --with polars --with pyarrow --with matplotlib python -c "{code}"
# Pure Polars
uv run --with numpy --with polars python -c "{code}"
# Pure DuckDB (with the Arrow handoff available)
uv run --with numpy --with duckdb --with pyarrow python -c "{code}"
When to include matplotlib:
.duckdb file? → USE DUCKDB (native format, optimal performance)Simple query → DuckDB
Heavy complex query → DuckDB
Filter → Polars
Sort → Polars
Join → DuckDB
Aggregate → DuckDB
Window → Polars
Transform → Polars
Too large for RAM → Polars streaming
Mixed operations → Hybrid
The exact multipliers these heuristics distill (with sources and caveats — routing heuristics, not guarantees) live in performance-benchmarks.md.
import duckdb
# Query file directly - no memory load
result = duckdb.sql("""
SELECT category, SUM(amount) as total
FROM 'data.csv'
GROUP BY category
""").pl() # .pl() -> Polars via Arrow. Requires pyarrow. Never .df() (pandas).
import polars as pl
# Lazy scan - optimizes and executes once
result = (
pl.scan_csv('data.csv')
.filter(pl.col('value') > 100)
.sort('value', descending=True)
.collect()
)
import duckdb
# Direct conversion via Arrow (pyarrow required in the package set)
df_polars = duckdb.sql("SELECT * FROM 'data.csv'").pl()
import duckdb
import polars as pl
# Phase 1: DuckDB for joins
joined = duckdb.sql(
"SELECT * FROM 'orders.csv' o "
"JOIN 'customers.csv' c ON o.customer_id = c.customer_id"
).pl()
# Phase 2: Polars for filtering
filtered = joined.filter(pl.col('amount') > 100)
# Phase 3: Back to DuckDB for aggregation
duckdb.register('filtered_data', filtered)
final = duckdb.sql('SELECT category, SUM(amount) FROM filtered_data GROUP BY category').pl()
For ad-hoc data exploration, use the built-in query runner:
# SQL query (uses DuckDB)
uv run scripts/quick-query.py data.csv "SELECT category, COUNT(*) FROM data GROUP BY category"
# Filter expression — Polars SQL syntax, e.g. "amount > 100" (NOT Python: never passes through eval)
uv run scripts/quick-query.py data.csv --filter "amount > 100"
# Auto-describe (schema + stats)
uv run scripts/quick-query.py data.parquet --describe
Supports CSV, Parquet, JSON, NDJSON. Cross-platform (macOS, Linux, Windows). Excel files are not read directly — export to CSV or Parquet first.
For detailed guidance, consult these reference files:
scripts/setup-uv.sh / scripts/setup-uv.ps1 automate it.scan_*, DuckDB direct queries)Automatic activation triggers:
.duckdb files.csv, .parquet, .json, .jsonl, .ndjson, .tsv, .duckdbCore execution principle: Always apply intelligent tool selection based on operation characteristics, never use pandas, and always include numpy and pyarrow in the execution environment.