[tools] Jupytext

安装 bash uv add dev jupytext 一次性转换 单独转换 bash jupytext to py:percent nbs/01 Preprocess dataset.ipynb 单独转换并指定输出路径 bash jupytext to py:percent nbs/01 Preprocess dataset.ipynb \ o scrip

安装

uv add --dev jupytext

一次性转换

单独转换

jupytext --to py:percent nbs/01_Preprocess_dataset.ipynb

单独转换并指定输出路径

jupytext --to py:percent nbs/01_Preprocess_dataset.ipynb \
  -o scripts/01_preprocess_dataset.py

反向转换

jupytext --to ipynb scripts/01_preprocess_dataset.py \
  -o nbs/01_Preprocess_dataset.ipynb

Pair 和 Sync

先 pair:

jupytext --set-formats ipynb,py:percent nbs/01_Preprocess_dataset.ipynb

然后 sync:

jupytext --sync nbs/01_Preprocess_dataset.ipynb

也可以从 .py 文件反向 sync:

jupytext --sync nbs/01_Preprocess_dataset.py

Repo 批量配置

在 repo 根目录放 jupytext.toml:

formats = "ipynb,py:percent"

示例结构:

katlas-raw/
  jupytext.toml
  nbs/
    01_Preprocess_dataset.ipynb
    02_Analyze_dataset.ipynb
  src/
    katlas_raw/
      preprocess.py

批量转换和 pair:

find nbs -name "*.ipynb" -not -path "*/.ipynb_checkpoints/*" \
  -exec jupytext --set-formats ipynb,py:percent {} \;

从 .py 同步回 .ipynb:

jupytext --sync nbs/01_Preprocess_dataset.py

批量同步:

find nbs -name "*.py" -exec jupytext --sync {} \;

可以变成一个 Skill

Work with the paired percent-format script, not the ipynb JSON.

Target file:
nbs/01_Preprocess_dataset.py

Rules:
- Edit nbs/01_Preprocess_dataset.py only.
- Do not directly edit nbs/01_Preprocess_dataset.ipynb.
- Keep # %% cell boundaries.
- Move reusable logic into src/katlas_raw/preprocess.py when appropriate.
- After editing, run:
  jupytext --sync nbs/01_Preprocess_dataset.py
- If possible, validate with:
  jupyter nbconvert --execute nbs/01_Preprocess_dataset.ipynb --to notebook --inplace

nbdev 的 .gitignore

如果不想把同步出来的 notebook script 提交到 Git,可以在 nbdev repo 的 .gitignore 里添加:

nbs/*.py

可以放进repo里的 AGENTS.md

## Notebook workflow

- This repo uses Jupytext paired notebooks.
- Treat `nbs/*.py` files in `py:percent` format as the editable source for notebooks.
- Do not directly edit `nbs/*.ipynb` unless explicitly asked.
- Preserve `# %%` cell boundaries when editing notebook scripts.
- After editing a paired notebook script, run:

  ```bash
  jupytext --sync nbs/<notebook_name>.py
  ```

- Reusable logic should be moved to `src/katlas_raw/*.py`.
- Keep notebooks thin: parameter setup, function calls, quick display, and plots.
- Do not modify files under `data/` unless explicitly asked.

Jupytext Skill

Use Jupytext to work with paired py:percent scripts instead of editing .ipynb JSON directly.

Workflow

  1. Check Pairing: If the notebook is paired, edit the .py percent-format file. If not paired, ask the user whether to pair only the current notebook or the entire repository.

  2. Edit Safely: Keep # %% cell boundaries intact. Do not directly edit the .ipynb file.

  3. Refactor: Move reusable logic into the repo package under src/ when appropriate.

  4. Sync: After editing the paired .py, sync the changes:

    Bash

    jupytext --sync <target.py>
    
  5. Validate: When practical, validate by executing the synced notebook:

    Bash

    jupyter nbconvert --execute <target.ipynb> --to notebook --inplace
    

Note: If the repo uses uv, install Jupytext as a dev dependency (uv add --dev jupytext) and prefix commands with uv run jupytext.

Commands & Syncing

One-Off Conversions:

Bash

# Notebook to script
jupytext --to py:percent nbs/01_Preprocess_dataset.ipynb [-o path/to/output.py]

# Script to notebook
jupytext --to ipynb scripts/01_preprocess_dataset.py [-o path/to/output.ipynb]

Pair and Sync:

Bash

# Pair an existing notebook with a py:percent script
jupytext --set-formats ipynb,py:percent nbs/01_Preprocess_dataset.ipynb

# Sync changes from either side
jupytext --sync nbs/01_Preprocess_dataset.ipynb

Repo-Wide Setup

To consistently pair notebooks and scripts across a project, add a jupytext.toml file to the repo root:

Ini, TOML

formats = "ipynb,py:percent"

Batch Operations:

Bash

# Batch pair all notebooks (ignoring checkpoints)
find nbs -name "*.ipynb" -not -path "*/.ipynb_checkpoints/*" -exec jupytext --set-formats ipynb,py:percent {} \;

# Batch sync all scripts
find nbs -name "*.py" -exec jupytext --sync {} \;

Tip: For nbdev repos, consider adding nbs/\*.py to .gitignore if the .py files are only local sync artifacts.

Notebook & Function Style

Use the repo's existing style if consistent. Otherwise, follow these rules:

Notebooks:

  • Structure: # Title (cell 1) > Notebook Goal (cell 2) > Imports > Setup/Config > Main sections (## Section Title) > Reusable defs > Runnable examples.
  • Rules: Keep code cells focused on one step. Separate imports/defs/examples into different cells. Do not redundantly reload datasets.

Functions:

  • Add type annotations for arguments and returns.
  • Prefer a one-line docstring explanation if sufficient.
  • For multiple/complex arguments, use a multiline signature with inline comments:

Python

def summarize_counts(
    df: pd.DataFrame,          # source dataframe
    group_col: str,            # categorical column to count
    top_k: int | None = None,  # optional limit
) -> pd.DataFrame:
    "Returns count of categorical values, optionally limited to top_k."
    counts = df[group_col].value_counts().rename_axis(group_col).reset_index(name="count")
    return counts.head(top_k) if top_k is not None else counts