Analysis code for the manuscript "Premotor cortex uses a compositional neural geometry to plan words".
This repository contains five Python scripts — one per main-text figure — that reproduce analyses of intracortical recordings from human ventral premotor cortex during preparation and production of variable-length syllable sequences from publically available data.
git clone <this repo>
cd speech_preparation
uv syncThis installs the exact versions pinned in uv.lock into a project-local .venv/. Activate it with source .venv/bin/activate or prefix commands with uv run.
git clone <this repo>
cd speech_preparation
python -m venv .venv && source .venv/bin/activate
pip install -e .Typical install time on a normal desktop: ~2 min with uv sync over a fast connection; ~5 min with pip install -e . (slower dependency resolver).
Download the data bundle from Dryad: <DRYAD LINK TBD>.
Unzip it into a folder named data/ at the repository root. The resulting tree must look exactly like this (folder names are case-sensitive):
speech_preparation/
└── data/
├── cvcvc/
│ └── t15.2024.06.30_binnedTX_spikePow_20ms.mat
├── multiseq/
│ ├── t12.2022.09.27_binnedTX_spikePow_20ms_speech_onset_audio.mat
│ └── t15.2024.09.13_binnedTX_spikePow_20ms.mat
├── nonsense_one_two_word/
│ └── t12.2022.10.25_binnedTX_spikePow_20ms.mat
├── real_words/
│ └── t12.2025.05.06_binnedTX_spikePow_20ms.mat
└── derivatives/
├── figure1/ # CSV files
├── figure2/ # CSV + NPZ + TXT
├── figure3/ # CSV
├── figure4/ # CSV
└── figure5/ # CSV
If you prefer to keep the data elsewhere, pass --data-dir /path/to/your/data to any script.
Each figureN.py is authored as a percent-format script (# %% cell markers). Two ways to run it cell-by-cell either directly in modern IDEs or as an interactive jupyter notebook.
-
Cursor / VS Code / PyCharm / Spyder: open the
.pyfile directly — these editors recognize# %%cells natively. -
Classic Jupyter / JupyterLab: convert with
jupytext(already pinned inpyproject.toml):# convert a single figure jupytext --to notebook figure3.py # → figure3.ipynb # convert all five at once for f in figure*.py; do jupytext --to notebook "$f"; done jupyter lab # then open the generated .ipynb files
Generated
.ipynbfiles are git-ignored — regenerate them locally as needed.
Each figure can be generated from derivative data (--mode deriv) or from raw neural timeseries from each trial (--mode raw). Note: Running from raw can take a long time
depending on the number of cores available. With ≥64 cores regenerating from raw can take very little time as the computationally expensive analyses are parallelized.
python figure1.py --mode raw # or --mode derivs
python figure2.py --mode raw
python figure3.py --mode raw
python figure4.py --mode raw
python figure5.py --mode rawAll scripts accept the same flags:
| Flag | Default | Purpose |
|---|---|---|
--mode |
raw |
raw recomputes from .mat; derivs re-renders from cached CSVs. |
--data-dir |
./data (next to the script) |
Root of the data tree. |
--deriv-dir |
<data-dir>/derivatives/figureN |
Where derivatives are written / loaded. |
--fig-dir |
./figures (next to the script) |
Where the final figure PDFs are written. |
Runtimes are wall-clock on a 12-core desktop, --mode raw. --mode derivs is < 1 min per figure.
| Figure | Runtime (--mode deriv) |
Runtime (--mode raw, 12 core laptop) |
Runtime (--mode raw, 64 core cluster) |
|---|---|---|---|
| 1 | <5 min | 2 – 8 h | 20 min |
| 2 | <5 min | 30 min – 2 h | 10 min |
| 3 | <5 min | 30 min – 2 h | 10 min |
| 4 | <5 min | 6 – 8 h | 30 min |
| 5 | <5 min | 30 min – 2 h | 30 min |
To recompute every derivative from scratch and check against the shipped CSVs:
for f in figure*.py; do python "$f" --mode raw --deriv-dir ./repro_derivs/$(basename "$f" .py); done
diff -r ./repro_derivs ./data/derivativesRe-render Figure 3 from the cached derivative CSVs shipped in data/derivatives/figure3/:
python figure3.py --mode derivs- Expected output:
figures/figure3_python.pdf— the rendered figure panel.- Console prints of compositionality R² statistics + permutation p-values.
- Expected runtime: < 1 min on a typical laptop.
Run Figure 3 end-to-end from the raw .mat files, writing derivatives to a fresh directory so the shipped CSVs are not overwritten:
python figure3.py --mode raw --deriv-dir ./demo_derivs --fig-dir ./demo_figs- Expected output:
demo_derivs/populated with the same CSVs asdata/derivatives/figure3/.demo_figs/figure3_python.pdfmatching the paper's figure 3.
- Expected runtime: 30 min – 2 h on a 12-core desktop.
- macOS 13+ (Apple Silicon) — primary development platform.
- Linux, RHEL 7-family (Stanford Sherlock HPC cluster) — production runs.
- Other Linux distributions and Windows 10/11 are expected to work but have not been tested.
- Python ≥ 3.10 (tested on 3.10 and 3.11).
| Package | Tested version |
|---|---|
| numpy | 2.2.6 |
| pandas | 2.3.3 |
| scipy | 1.15.3 |
| scikit-learn | 1.7.2 |
| matplotlib | 3.10.9 |
| seaborn | 0.13.2 |
| plotly | 6.7.0 |
| joblib | 1.5.3 |
| tqdm | 4.67.3 |
| jupytext | 1.19.2 |
Looser version ranges are declared in pyproject.toml; exact pins live in uv.lock.
- No GPU required. All computation is CPU-only (NumPy / scikit-learn / joblib).
- Recommended for
--mode raw: ≥ 12-core CPU and ≥ 32 GB RAM. The raw.matneural recordings are multi-GB and the decoding sweeps use all available cores viajoblib.Parallel(n_jobs=-1). - Minimum for
--mode derivs: any modern 4-core laptop with 8 GB RAM. - Disk: ≥ 12 GB free for the Dryad data bundle.