# Source data — long-read RNA sequencing in primary lung cell types

Everything the analysis code for Leshem et al. reads and cannot produce for itself, plus two optional
extras: the model's full DeepSHAP attribution arrays and a packaged copy of the software the analyses
ran in. Download the data files, get the code, and you can rebuild the paper's results.

**Analysis code:** https://github.com/peter4244/nmd_lung_longread_reproduction
([10.5281/zenodo.21897099](https://doi.org/10.5281/zenodo.21897099))
**Raw sequencing reads:** NCBI GEO **GSE329233** — not needed for anything here.

**What the record holds.**

| Part | Files | Size | Needed to reproduce the paper |
|---|---|---|---|
| Data | Twelve: counts, annotation, isoform-pair inputs, the trained model and its outputs, reused data tables | 695 MB | Yes. Every input the analysis code reads |
| DeepSHAP attribution arrays | Twenty `deepshap_*.npz` | 27.3 GB | No. The model's full per-isoform attributions. The DeepSHAP tables in `model.zip`, which the analysis code reads, were computed from them |
| Runtime environment | `nmd_1.3.sif` and `nmd_docker_1.0_2026-08-13_verified.tar` | 8.1 GB and 8.8 GB | No. One software environment in two formats |
| Checksums | `MANIFEST.sha256`, `UPLOAD_MANIFEST.sha256`, `deepshap_npz.sha256` | Under 10 kB each | For checking downloads |

The steps below fetch the data, its two checksum files and this README. *File reference* describes
every file, including how to fetch the DeepSHAP arrays.

---

# Start here

## 1. Download the twelve data files — 695 MB

That is every input the code reads.

The code below fetches the twelve data files and both checksum files, resolving the record to its
current version automatically:

```bash
curl -sL "https://zenodo.org/api/records/21544336" \
| python3 -c '
import json, sys
# leave out the two container images and the optional deepshap_* files
skip = {"nmd_1.3.sif", "nmd_docker_1.0_2026-08-13_verified.tar"}
for f in json.load(sys.stdin)["files"]:
    if f["key"] not in skip and not f["key"].startswith("deepshap_"):
        print(f["links"]["self"], f["key"])
' \
| while read url name; do curl -L -o "$name" "$url"; done
```

For packaged environments, there is a Docker and a .sif file that are also available.

## 2. Arrange the files

The code looks for its input in a folder named `source_data`, with the two checksum files one level
above it. Five of the twelve downloads are zipped folders:

```bash
mkdir -p nmd_deposit_2026/source_data
mv *.csv *.gtf.gz *.zip                    nmd_deposit_2026/source_data/
mv MANIFEST.sha256 UPLOAD_MANIFEST.sha256  nmd_deposit_2026/
cd nmd_deposit_2026/source_data && for z in *.zip; do unzip -q "$z" && rm "$z"; done
```

You should end up with this:

```
nmd_deposit_2026/
├── MANIFEST.sha256
├── UPLOAD_MANIFEST.sha256
└── source_data/
    ├── nmd_isocall_counts_4ct.csv            ├── sqanti/                  (4 files)
    ├── nmd_lungcells_counts_4ct.csv          ├── annotation/              (6 files)
    ├── salmon_gene_counts_4ct.csv            ├── model/                   (32 files)
    ├── nmd_isocall_4ct.gtf.gz                ├── isopair/                 (5 files)
    ├── pheno_4ct.csv                         └── tan_2025_supplementary/  (4 files)
    ├── gerstberger_2014_rbp_census.csv
    └── encode_rbp_roster_vannostrand2020.csv
```

Each of the five unzipped folders also holds its own `MANIFEST.sha256`, which the counts above
leave out — so `annotation/` is seven files on disk, not six.

Two things to get right:

- **Do not unzip everything into one flat folder.** The code then looks for its input in folders
  that do not exist. It prints a warning but keeps running, so a job can get a long way before
  anything looks wrong.
- **The checksum files belong above `source_data/`**, because the paths written inside them are
  relative to it.

## 3. Check the download

```bash
cd nmd_deposit_2026/source_data && shasum -a 256 -c ../MANIFEST.sha256
```

58 lines, every one `OK`. If instead you see `58 listed files could not be read`, you are in the
wrong directory rather than looking at a bad download.

`UPLOAD_MANIFEST.sha256` is an optional earlier check you can run before unzipping, on the twelve
files exactly as you downloaded them.

Unzipped, the data comes to 4.6 GB. Nearly all of that growth is `sqanti.zip`.

## 4. Get the analysis code

Clone the repository linked at the top. Its `README.md` takes over from here: what to install, how
to point the code at this folder, and the order to run things in.

---

# What's in here

**The experiment.** Paired short-read and PacBio Iso-Seq long-read RNA sequencing of four primary
human lung cell types — alveolar type 2 (**AT2**), large airway epithelial (**LAE**), fibroblast
(**FB**) and microvascular endothelial (**MV**) — treated with an SMG1 inhibitor (`SMG1i`) or
vehicle (`DMSO`). **26 samples: 13 donor-matched pairs.** Every count matrix carries exactly those
26 samples.

The twelve files fall into five groups.

**Counts — what was measured.** Three matrices of how much of each transcript or gene was seen in
each sample, plus a small table saying which sample is which. Start here for any
differential-expression analysis.

**Annotation — where transcripts are and what they are called.** The isoform models, the SQANTI3
structural classification of each one, Ensembl identifier maps, and the gene sets used for pathway
work. These describe the transcripts the counts refer to.

**Isoform-pair inputs.** Two things the isoform-pair and PTC analyses cannot begin without: the
list of isoforms that passed expression filtering, and the per-cell-type differential-expression
tables that feed the NMD classification.

**The model.** The trained neural network, its predictions, and every interpretability output.
Training needs a GPU cluster, so these are deposited — you do not need to retrain anything to
reproduce the paper's model results.

**Reused data tables.** Two published catalogues of RNA-binding proteins, and supplementary tables
from Tan et al. 2025 that one section reanalyses. These carry their own citation requirements, given
with the tables under *Reused data tables*.

Outside the twelve are the model's full **DeepSHAP attribution arrays**, from which the DeepSHAP
tables in `model.zip` were computed, and the **runtime environment**, the two large container files.
Neither is needed to reproduce the paper.

---

# File reference

## Counts

| File | Size | Shape | What it is |
|---|---|---|---|
| `nmd_isocall_counts_4ct.csv` | 48.9 MB | 614,993 × 26 | Long-read isoform counts from the Isocall pipeline, one column per sample. The unfiltered isoform set |
| `nmd_lungcells_counts_4ct.csv` | 48.9 MB | 614,992 × 26 | The same counts over the SQANTI3-filtered isoform set |
| `salmon_gene_counts_4ct.csv` | 15.5 MB | 78,899 × 26 | Short-read gene-level counts (Salmon, via nf-core/rnaseq). 46,571 genes have a non-zero count in at least one sample |
| `pheno_4ct.csv` | 2.3 kB | 26 × 5 | Which sample is which: `sample_name`, `donor`, `treatment`, `cell_type`, `bam`. The join key for every matrix |

Sample columns are named `<donor>_<treatment>_<cell type>`, for example `001V_DMSO_AT2`.

## Annotation

| File | Size | What it is |
|---|---|---|
| `nmd_isocall_4ct.gtf.gz` | 42.8 MB | Isoform models for the long-read set — where each transcript's exons sit, and the transcript-to-gene map |
| `sqanti.zip` | 348.6 MB → 4.2 GB | SQANTI3 output: structural classification per isoform, corrected models, coding sequences, and transcript sequence. The analysis starts from these calls and does not re-run SQANTI3 |
| `annotation.zip` | 7.3 MB | Ensembl v115 gene and transcript identifier maps; the human paralog table behind the train/test leakage screen; one reference transcript captured from the Ensembl API so the RBP section runs without network access; and the MSigDB hallmark gene sets, captured for the same reason |

Inside `sqanti.zip`: `nmd_lungcells_classification.txt` (structural category per isoform),
`nmd_lungcells_filtered.gtf` (filtered models), `nmd_lungcells_corrected.cds.gff3` (coding
sequences), `nmd_lungcells_corrected.fasta` (sequence).

## Isoform-pair inputs

`isopair.zip`, 21.2 MB, 5 files.

| File | What it is |
|---|---|
| `expressed_isoforms_4ct.txt` | The 95,623 isoforms that passed the isoform-pair analyses' expression filter: the universe for the isoform-pair and PTC analyses. See *Things that may trip you up* |
| `nmd_mashr_die_{at2,lae,fb,mv}_2026.3.10.csv` | Per-cell-type differential-isoform-expression tables (mashr), which feed the NMD classification |

## The model

`model.zip`, 7.1 MB, 32 files. Filenames are long because they carry the window size, the seed and
the evaluation split — two files differing only in those are different results, so the names are
left unshortened.

| File(s) | What it is |
|---|---|
| `best_model_atg1000_stop1000_seed42.pt` | Trained weights: 1000 nt windows around the start and stop codons, seed 42 |
| `metrics_atg1000_stop1000_seed42_test_clean.json` | Held-out performance — AUC 0.9257, AUPRC 0.8175 over 10,522 isoforms, 2,405 of them NMD susceptible. Evaluated once |
| `predictions_atg1000_stop1000_seed42_all.tsv` | Per-isoform predictions for all 41,776 isoforms, with split, label and probability |
| `predictions_atg1000_stop1000_seed42_test_clean.tsv` | The held-out split alone, 10,522 rows — a subset of the file above |
| `kernel_shap_branch_*.tsv` | Per-branch attributions (start codon window, stop codon window, ORF structure). Exact Shapley values by enumeration, not the sampling estimator |
| `deepshap_summary_*_{joint,structural,atg-stop}_run{1..5}.tsv` | Feature attributions: three separate decompositions, five replicates each |
| `shap_profile_{atg,stop}_joint_*.tsv` | Per-nucleotide attribution around the start and stop codons |
| `motif_logo_atg_*.tsv`, `motif_logos_stop_*.tsv` | Per-position mean attribution and base frequency, NMD susceptible against control, behind the sequence-logo panels |
| `gc_content_across_{atg,stop}_window_refaug_only_*.tsv` | GC content along the start and stop codon windows, NMD susceptible against control, counting only NMD isoforms whose ORF is the reference coding sequence. Computed from the inputs stored in the joint DeepSHAP arrays |
| `stop_codon_freq_by_class_sf37_all.tsv`, `stop_codon_test_sf37_all.tsv` | Stop codon usage (UGA, UAA, UAG) in the model's priority ORF, for all isoforms and for NMD susceptible and control isoforms separately, and the NMD-against-control comparison for each codon (odds ratio and p-value). The `sf37` in the names is a supplementary figure number from an earlier numbering |
| `uorf_attention_*.tsv`, `uorf_features_in_priority_slots.tsv` | Model attention over candidate upstream ORFs, and the counts behind the uORF panels |

### DeepSHAP attribution arrays (optional)

Twenty `deepshap_*.npz` files, 27.3 GB, not zipped, with checksums in `deepshap_npz.sha256`. They
hold the full per-isoform DeepSHAP attributions for the model above: all 41,776 isoforms of
`predictions_atg1000_stop1000_seed42_all.tsv`, in that file's row order. There are four
decompositions, each run five times; the five runs explain the same isoforms and differ in the
background sample the attributions are measured against.

| File(s) | Size each | What is explained |
|---|---|---|
| `deepshap_atg_atg1000_stop1000_seed42_run{1..5}.npz` | 1.4 GB | The 1,000 nt window around the start codon, 9 channels × 1,000 positions |
| `deepshap_stop_atg1000_stop1000_seed42_run{1..5}.npz` | 1.4 GB | The 1,000 nt window around the stop codon, the same shape |
| `deepshap_structural_atg1000_stop1000_seed42_run{1..5}.npz` | 1.4 MB | The model's 5 structural features |
| `deepshap_joint_atg1000_stop1000_seed42_run{1..5}.npz` | 2.7 GB | All of these at once: 18,005 inputs, the start codon window (9,000), then the stop codon window (9,000), then the 5 structural features |

Every file holds `shap_values` (isoforms × inputs × 1, the trailing 1 being the model's single
output), `inputs` (the values that were explained), `labels`, `explain_indices` (each isoform's row
in the predictions file) and `seed`. The atg, stop and joint files name their channels in
`channel_names`: `A`, `C`, `G`, `T`, `junction`, `rolling_gc`, `frame_0`, `frame_1`, `frame_2`. The
structural and joint files name their features in `feature_names`: `frac_start`, `frac_stop`,
`is_ref_cds`, `is_sqanti_cds`, `n_downstream_ejc`. `joint` also records its layout in `n_channels`,
`w_atg`, `w_stop` and `n_features`.

They are not part of the download in *Start here*. To fetch them and check them:

```bash
curl -sL "https://zenodo.org/api/records/21544336" \
| python3 -c '
import json, sys
for f in json.load(sys.stdin)["files"]:
    if f["key"].startswith("deepshap_"):
        print(f["links"]["self"], f["key"])
' \
| while read url name; do curl -L -o "$name" "$url"; done
shasum -a 256 -c deepshap_npz.sha256
```

The analysis code reads the DeepSHAP tables in `model.zip`, not these arrays. The arrays are here so
those tables can be recomputed, or the attributions taken further, without rerunning DeepSHAP, which
needs a GPU. The scripts that compute the tables are in the model's own repository,
`NMD_orf_model_v5_4ct`, and find the arrays by filename, so keep the names as downloaded, with
`deepshap_npz.sha256` beside them.

## Reused data tables

| File | Size | What it is |
|---|---|---|
| `gerstberger_2014_rbp_census.csv` | 78 kB | 1,542 human RNA-binding proteins with gene id, RNA target and domains |
| `encode_rbp_roster_vannostrand2020.csv` | 56 kB | 356 experimentally characterised RBPs with functional class. This is where the functional classes used in the paper come from |
| `tan_2025_supplementary.zip` | 154.4 MB → 156 MB | Tan et al. 2025 Supplementary Tables S1, S2, S4 and S6, read by one comparative reanalysis |

If you use these files, cite the original publications:

> Gerstberger S, Hafner M, Tuschl T. *A census of human RNA-binding proteins.* **Nature Reviews
> Genetics** 2014;15:829–845. doi:[10.1038/nrg3813](https://doi.org/10.1038/nrg3813) —
> `gerstberger_2014_rbp_census.csv`, Supplementary Table S3.

> Van Nostrand EL, Freese P, Pratt GA, et al. *A large-scale binding and functional map of human
> RNA-binding proteins.* **Nature** 2020;583:711–719.
> doi:[10.1038/s41586-020-2077-3](https://doi.org/10.1038/s41586-020-2077-3) —
> `encode_rbp_roster_vannostrand2020.csv`.

> Tan K, Sebat J, Wilkinson MF. *Cell type- and factor-specific nonsense-mediated RNA decay.*
> **Nucleic Acids Research** 2025;53. doi:[10.1093/nar/gkaf395](https://doi.org/10.1093/nar/gkaf395)
> — `tan_2025_supplementary.zip`, Supplementary Tables S1, S2, S4 and S6.

The two RBP tables are unmodified apart from conversion to CSV. The Tan et al. tables are the
publisher's `.xlsx` files byte-for-byte, renamed only to tidy the filenames.

**Which Tan et al. sheet holds each condition**, for the comparative reanalysis:

| Condition | Table | Sheet | | Condition | Table | Sheet |
|---|---|---|---|---|---|---|
| ESC_UPF2_KD | S1 | Sheet4 | | NPC_UPF2_KD | S4 | Sheet4 |
| ESC_UPF2_iKO | S1 | Sheet5 | | NPC_UPF2_iKO | S4 | Sheet5 |
| ESC_UPF2_iKO_KD | S1 | Sheet6 | | NPC_UPF2_iKO_KD | S4 | Sheet6 |
| ESC_UPF3B_KO | S2 | Sheet2 | | NPC_UPF3B_KO | S6 | Sheet2 |

## Runtime environment

`nmd_1.3.sif` (8.1 GB) and `nmd_docker_1.0_2026-08-13_verified.tar` (8.8 GB) — the same environment
as an Apptainer image and as the Docker archive it was converted from. It carries the R and Python
versions the analyses ran under: the 189 R packages pinned by the code's `renv.lock`, plus both
conda environments.

```bash
apptainer exec nmd_1.3.sif Rscript <script>
```

**`nmd_1.3.sif` is a corrected rebuild, not the original image.** The analyses ran under
`nmd_1.2.sif`, which turned out to be missing two R packages the model-section report needs, and in
which a bare `Rscript` found the wrong R installation. Version 1.3 is built from the same pinned
package list with both problems fixed. So it is the environment the paper describes, corrected —
not a byte-for-byte copy of the container the results came out of. `nmd_1.2.sif` is kept in an
earlier version of this record, [10.5281/zenodo.21878996](https://doi.org/10.5281/zenodo.21878996),
whose files are restricted: it cannot be downloaded without access being granted.

You do not need it to read the data. Everything else here is plain text or a standard archive. The
code repository also ships a `Dockerfile` that builds an equivalent environment, which is the route
to take if downloading 8 GB is impractical.

---

# Things that may trip you up

**Use the right count matrix with the right annotation.** The two long-read matrices cover slightly
different isoform sets, and pairing them wrongly changes results without any error:

| Matrix | Annotation to use with it | For |
|---|---|---|
| `nmd_lungcells_counts_4ct.csv` | `sqanti/nmd_lungcells_filtered.gtf` | Gene- and isoform-level differential expression |
| `nmd_isocall_counts_4ct.csv` | `nmd_isocall_4ct.gtf.gz` | Isoform-pair and PTC analyses |

They differ by exactly one isoform, `ENST00000441168.6` (*ANKMY1*), which SQANTI3 removes for
carrying a 1-bp exon. Counts for the 614,992 shared isoforms are identical in both.

**Use `expressed_isoforms_4ct.txt` rather than recalculating it.** The expression filter's first step
keeps an isoform whose share of its gene reaches 5%, and because a share is a ratio, 2,567 isoforms land
exactly on that cutoff. Whether each is kept comes down to rounding that differs between operating
systems: with identical code and data, macOS keeps 95,623 isoforms and Linux keeps 95,639. Neither
is wrong, but only one matches the paper. If the file is absent the code recalculates the list in a
way that is consistent across machines, and says so — that route gives 95,817 isoforms, a 0.2%
difference that changes no conclusion.

**The paper counts isoforms over three different sets.** The long-read count matrices here are
unfiltered (614,993 and 614,992 isoforms). The paper's expressed-isoform set, which its isoform
landscape and isoform-level differential-expression results are stated over, is the 162,800 that
pass an expression filter. The isoform-pair and PTC analyses use the narrower 95,623 in
`expressed_isoforms_4ct.txt`. Some published numbers are stated over the unfiltered matrices. When
comparing against a number in the paper, check which set it refers to.

**Two prediction files overlap.** `predictions_..._test_clean.tsv` is the held-out subset of
`predictions_..._all.tsv`. Both ship because published values were computed from each.

**Cell-type codes in the code differ from the labels in the paper.** `AT2` appears as `AT` and
`LAE` as `DD` in some scripts, mapped when the data is loaded.
