skills/jupytext/SKILL.md
Use when working with jupytext — converting notebooks to/from text formats, syncing paired .ipynb/.py files, multi-kernel projects (Python/R/Stata/SAS), or executing notebooks via papermill.
npx skillsauth add edwinhu/workflows jupytextInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Jupytext converts Jupyter notebooks to/from text formats (.py, .R, .md), enabling version control and multi-kernel workflows.
Before claiming ANY jupytext script executed successfully, follow this sequence:
jupytext --to notebook --output - script.py | papermill - output.ipynbThis is non-negotiable. Skipping papermill execution is NOT HELPFUL — the user gets a notebook that fails on first run.
jupyter nbconvert --execute — papermill has better error handling, parameter injection, and logging, and the pipe form needs no intermediate .ipynb files.Before EVERY "notebook works" claim:
Conversion:
Execution (MANDATORY):
jupytext --to notebook --output - script.py | papermill - output.ipynbOutput Verification:
Multi-Kernel Projects (if applicable):
Only after ALL checks pass:
Follow this sequence for EVERY jupytext task involving execution:
1. CONVERT → jupytext --to notebook --output -
2. EXECUTE → papermill - output.ipynb (with params if needed)
3. CHECK → Verify exit code and stderr
4. INSPECT → Use notebook-debug verification
5. VERIFY → Outputs match expectations
6. CLAIM → "Notebook works" only after all gates passed
NEVER skip execution gate. Converting without executing proves nothing about correctness.
Use percent format (py:percent) for all projects:
# %% [markdown]
# # Analysis Title
# %%
import pandas as pd
df = pd.read_csv("data.csv")
# %% tags=["parameters"]
input_file = "data.csv"
Cell markers: # %% for code, # %% [markdown] for markdown.
Markdown dollar signs: Always wrap $ in backticks to prevent LaTeX rendering - # Cost: $50`` not # Cost: $50
Create jupytext.toml in project root:
formats = "ipynb,py:percent"
notebook_metadata_filter = "-all"
cell_metadata_filter = "-all"
# Convert notebook to percent-format Python file
jupytext --to py:percent notebook.ipynb
# Convert Python script to Jupyter notebook format
jupytext --to notebook script.py
# Enable bidirectional pairing to keep formats synchronized
jupytext --set-formats ipynb,py:percent notebook.ipynb
# Synchronize paired notebook and text file
jupytext --sync notebook.ipynb
Always pipe to papermill for execution - no intermediate files:
# Convert script to notebook and execute in atomic operation
jupytext --to notebook --output - script.py | papermill - output.ipynb
# Convert and execute with parameter injection
jupytext --to notebook --output - script.py | papermill - output.ipynb -p start_date "2024-01-01" -p n_samples 1000
# Convert and execute with detailed logging output
jupytext --to notebook --output - script.py | papermill - output.ipynb --log-output
# Convert and execute in memory without saving intermediate files
jupytext --to notebook --output - script.py | papermill - -
Key flags:
--output - tells jupytext to write to stdoutpapermill - output.ipynb reads from stdin, writes to filepapermill - - reads from stdin, writes to stdout (for inspection)Why this pattern:
.ipynb files cluttering the workspaceAfter execution, use notebook-debug skill to inspect tracebacks in the output ipynb.
Share data between Python/R/Stata/SAS via files:
| Route | Format | Write | Read |
|-------|--------|-------|------|
| Python -> R | Parquet | df.to_parquet() | arrow::read_parquet() |
| Python -> Stata | DTA | df.to_stata() | use "file.dta" |
| Any -> Any | CSV | Native | Native |
| SQL queries | DuckDB | Query parquet directly | Query parquet directly |
Python (prep) -> Parquet -> R (stats) -> Parquet -> Python (report)
|
v
Stata (.dta) -> Econometrics
Add the following to .pre-commit-config.yaml:
repos:
- repo: https://github.com/mwouts/jupytext
rev: v1.16.0
hooks:
- id: jupytext
args: [--sync] # Synchronize paired formats before commit
Choose one approach:
*.ipynb to .gitignore) for minimal repository sizeConfigure editors for automatic synchronization:
Standard multi-kernel project layout:
project/
├── jupytext.toml # Project-wide settings
├── environment.yml # Conda env with all kernels
├── notebooks/
│ ├── 01_python_prep.py # Python percent format
│ ├── 02_r_analysis.R # R percent format
│ └── 03_stata_models.do # Stata script
├── data/
│ ├── raw/
│ └── processed/ # Parquet/DTA interchange files
└── results/
Specify kernel in file header:
# ---
# jupyter:
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# %% [markdown]
# # Python Analysis
| Issue | Solution |
|-------|----------|
| Sync conflict | Delete .ipynb, regenerate from .py |
| Wrong kernel | Add kernelspec header to .py file |
| Metadata noise | Set notebook_metadata_filter = "-all" |
| Cell order lost | Use percent format (preserves structure) |
Detailed patterns and configurations:
references/formats.md - All format specifications (percent, light, sphinx, myst, rmd, quarto), cell metadata, configuration optionsreferences/kernels.md - Kernel setup (IRkernel, xeus-r, stata_kernel, pystata, saspy), environment configuration, troubleshootingreferences/data-sharing.md - Cross-kernel data sharing patterns (parquet, dta, csv, duckdb), full pipeline examples, validation patternsWorking code in examples/:
examples/python_analysis.py - Python percent-format template with common patternsexamples/r_analysis.R - R percent-format template for statistical analysisexamples/cross_kernel_pipeline.py - Multi-kernel data sharing exampleUtility scripts in scripts/:
scripts/init_project.sh - Initialize jupytext project with standard structurescripts/sync_all.sh - Sync all paired notebooks in projectdevelopment
Build the meeting-level proxy-voting × ownership panel on the WRDS SGE grid — ISS N-PX fund votes reduced to (item × block) direction cells, joined to institutional and mutual-fund ownership. Use when working with risk.voteanalysis_npx, N-PX fund-level votes, ISS→CRSP fund linking, index/passive/active voting blocks, or a proxy-voting panel that needs ownership attached.
development
Use when "CRSP CIZ", "CRSP v2", "CRSP flat file format 2.0", "crsp.dsf_v2 / msf_v2", "StkDlySecurityData", "StkMthSecurityData", "StkSecurityInfoHist", "stocknames_v2", "DlyRet / MthRet / DlyPrc / MthPrc", "SHRCD or EXCHCD equivalent in new CRSP", "SIZ to CIZ migration", "CRSP data after 2024", "CRSP delisting returns", "CRSP cumulative adjustment factors", "CRSP index INDNO / INDFAM", or any CRSP stock/index query where the legacy SIZ column names no longer exist.
development
Use when linking or deduping datasets by entity name rather than a shared key — 'fuzzy match', 'fuzzy name matching', 'entity resolution', 'record linkage', 'match company/person names', 'dedupe entity names', 'name-based join', 'bridge identifiers' (CIK ↔ permno ↔ gvkey ↔ wficn ↔ EIN ↔ personid), or any use of char n-gram TF-IDF, cosine similarity on names, `sparse_dot_topn`, or RapidFuzz at scale.
development
Use when building a publication-quality table in Python — 'regression table', 'results table', 'summary statistics table', 'etable', 'coefplot', 'great_tables', 'GT', 'gt table', 'format a table for the paper', 'export table to LaTeX/HTML', significance stars, spanners, or column formatting for a table headed into a paper, slide deck, or notebook.