@datagrok/proteomics 1.0.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +52 -3
  2. package/CLAUDE.md +178 -0
  3. package/README.md +195 -3
  4. package/detectors.js +152 -9
  5. package/dist/package-test.js +1 -1744
  6. package/dist/package-test.js.map +1 -1
  7. package/dist/package.js +1 -146
  8. package/dist/package.js.map +1 -1
  9. package/docs/personas-and-capabilities.md +165 -0
  10. package/files/demo/README.md +264 -0
  11. package/files/demo/cptac-spike-in.txt +1571 -0
  12. package/files/demo/enrichment-demo.csv +120 -0
  13. package/files/demo/fragpipe-smoke-test.tsv +11 -0
  14. package/files/demo/proteinGroups.txt +28 -0
  15. package/files/demo/spectronaut-hye-candidates.tsv +94 -0
  16. package/files/demo/spectronaut-hye-demo.tsv +8761 -0
  17. package/files/demo/spectronaut-hye-mix.tsv +8761 -0
  18. package/files/demo/spectronaut-hye-precursor-golden.json +938 -0
  19. package/files/demo/spectronaut-hye-precursor-golden.tsv +235 -0
  20. package/files/demo/spectronaut-hye-precursor.tsv +493 -0
  21. package/images/enrichment-crosslink.png +0 -0
  22. package/images/enrichment-term-selected.png +0 -0
  23. package/images/hero.png +0 -0
  24. package/images/pipeline.svg +80 -0
  25. package/package.json +88 -63
  26. package/scripts/deqms_de.R +60 -0
  27. package/scripts/limma_de.R +42 -0
  28. package/scripts/vsn_normalize.R +19 -0
  29. package/src/analysis/differential-expression.ts +450 -0
  30. package/src/analysis/enrichment-export.ts +101 -0
  31. package/src/analysis/enrichment.ts +602 -0
  32. package/src/analysis/experiment-setup.ts +199 -0
  33. package/src/analysis/imputation.ts +407 -0
  34. package/src/analysis/log2-scale.ts +139 -0
  35. package/src/analysis/normalization.ts +255 -0
  36. package/src/analysis/pca.ts +254 -0
  37. package/src/analysis/spc-storage.ts +515 -0
  38. package/src/analysis/spc.ts +544 -0
  39. package/src/analysis/subcellular-location.ts +431 -0
  40. package/src/demo/enrichment-demo.ts +94 -0
  41. package/src/demo/proteomics-demo.ts +123 -0
  42. package/src/global.d.ts +15 -0
  43. package/src/menu.ts +133 -0
  44. package/src/package-api.ts +136 -14
  45. package/src/package-test.ts +45 -20
  46. package/src/package.g.ts +161 -0
  47. package/src/package.ts +1029 -17
  48. package/src/panels/protein-focus.ts +63 -0
  49. package/src/panels/published-analysis-panel.ts +151 -0
  50. package/src/panels/uniprot-panel.ts +349 -0
  51. package/src/parsers/fragpipe-parser.ts +200 -0
  52. package/src/parsers/generic-parser.ts +197 -0
  53. package/src/parsers/maxquant-parser.ts +162 -0
  54. package/src/parsers/shared-utils.ts +163 -0
  55. package/src/parsers/spectronaut-candidates-parser.ts +307 -0
  56. package/src/parsers/spectronaut-parser.ts +604 -0
  57. package/src/publishing/assert-published-shape.ts +260 -0
  58. package/src/publishing/post-open-recovery.ts +104 -0
  59. package/src/publishing/publish-project.ts +515 -0
  60. package/src/publishing/publish-settings.ts +59 -0
  61. package/src/publishing/publish-state.ts +316 -0
  62. package/src/publishing/share-dialog.ts +171 -0
  63. package/src/publishing/trim-dataframe.ts +247 -0
  64. package/src/tests/analysis.ts +658 -0
  65. package/src/tests/enrichment-export.ts +61 -0
  66. package/src/tests/enrichment-visualization.ts +340 -0
  67. package/src/tests/enrichment.ts +224 -0
  68. package/src/tests/fragpipe-e2e.ts +74 -0
  69. package/src/tests/fragpipe-parser.ts +147 -0
  70. package/src/tests/gene-label-resolver.ts +387 -0
  71. package/src/tests/generic-parser.ts +152 -0
  72. package/src/tests/group-mean-correlation.ts +139 -0
  73. package/src/tests/log2-scale.ts +93 -0
  74. package/src/tests/organisms.ts +56 -0
  75. package/src/tests/parsers.ts +182 -0
  76. package/src/tests/publish-roundtrip.ts +584 -0
  77. package/src/tests/publish-spike.ts +377 -0
  78. package/src/tests/qc-dashboard.ts +210 -0
  79. package/src/tests/smart-pathway-filter.ts +193 -0
  80. package/src/tests/spc-formula-lines-spike.ts +129 -0
  81. package/src/tests/spc.ts +640 -0
  82. package/src/tests/spectronaut-candidates-e2e.ts +140 -0
  83. package/src/tests/spectronaut-candidates-parser.ts +398 -0
  84. package/src/tests/spectronaut-parser.ts +668 -0
  85. package/src/tests/subcellular-location.ts +361 -0
  86. package/src/tests/uniprot-panel.ts +202 -0
  87. package/src/tests/volcano.ts +603 -0
  88. package/src/utils/column-detection.ts +28 -0
  89. package/src/utils/gene-label-resolver.ts +443 -0
  90. package/src/utils/organisms.ts +82 -0
  91. package/src/utils/proteomics-types.ts +30 -0
  92. package/src/viewers/enrichment-viewers.ts +274 -0
  93. package/src/viewers/group-mean-correlation.ts +218 -0
  94. package/src/viewers/heatmap.ts +168 -0
  95. package/src/viewers/pca-plot.ts +169 -0
  96. package/src/viewers/qc-computations.ts +266 -0
  97. package/src/viewers/qc-dashboard.ts +176 -0
  98. package/src/viewers/spc-dashboard.ts +755 -0
  99. package/src/viewers/volcano.ts +690 -0
  100. package/test-console-output-1.log +2073 -0
  101. package/test-record-1.mp4 +0 -0
  102. package/tools/derive-precursor-golden-sidecar.mjs +81 -0
  103. package/tools/generate-enrichment-fixture.sh +160 -0
  104. package/tools/generate-spectronaut-candidates-fixture.mjs +212 -0
  105. package/tools/generate-spectronaut-precursor-fixture.mjs +128 -0
  106. package/tools/spectronaut-aggregate.sh +46 -0
  107. package/tools/spectronaut-aggregate.sql +80 -0
  108. package/tsconfig.json +18 -71
  109. package/webpack.config.js +86 -45
  110. package/.eslintignore +0 -1
  111. package/.eslintrc.json +0 -56
  112. package/LICENSE +0 -674
  113. package/package.png +0 -0
  114. package/scripts/number_antibody.py +0 -190
  115. package/scripts/number_antibody_abnumber.py +0 -177
  116. package/scripts/number_antibody_anarci.py +0 -200
package/CHANGELOG.md CHANGED
@@ -1,3 +1,52 @@
1
- # monomerDB changelog
2
-
3
- ## 0.0.1 (2025-10-28)
1
+ # Proteomics changelog
2
+
3
+ ## 1.2.0
4
+
5
+ - **Shared enrichment charts now survive reopen** — a published/shared analysis's enrichment
6
+ dot and bar charts no longer lose their value column (the "Column negLog10FDR does not
7
+ exist" error) when the project is reopened, and the Up/Down term tables no longer leak
8
+ into the project as extra tables. The charts are now self-contained on the enrichment
9
+ frame and split by direction via a per-viewer filter.
10
+ - **Volcano opens automatically on Candidates import** — importing a Spectronaut Candidates
11
+ file now lands you on the volcano immediately, matching the Report → Differential
12
+ Expression path (Candidates arrive with the result already computed, so the pipeline's DE
13
+ step is skipped).
14
+ - **Personas & capabilities documentation** — a reference describing what the proteomics
15
+ analyst vs. the biology scientist can do and how that boundary is enforced.
16
+
17
+ ## 0.2.0
18
+
19
+ - **Organism-aware subcellular-location coloring** — the volcano's "color by subcellular
20
+ location" now fetches UniProt annotations for the experiment's organism (persisted as a
21
+ `proteomics.organism` tag from the Enrichment dialog), so non-human data is no longer
22
+ mis-colored by human entries. The taxonomy filter applies only to the reviewed-by-gene
23
+ fallback; accession lookups stay unfiltered, and all 9 supported organisms are covered.
24
+ - **Internal consistency** — significance thresholds, the direction palette, and column-name
25
+ constants are now single-sourced instead of duplicated across files; the differential-
26
+ expression FDR cutoff is wired through to the BH correction.
27
+ - **README** — illustrated with the main analysis view, an authored pipeline diagram, and an
28
+ enrichment→volcano cross-link walkthrough.
29
+
30
+ ## 0.1.0
31
+
32
+ Initial release — mass spectrometry-based proteomics analysis, carrying a study from
33
+ search-engine output to biological interpretation inside Datagrok.
34
+
35
+ - **Import** — MaxQuant (`proteinGroups.txt`), Spectronaut Report (PG-level, including
36
+ streaming aggregation of long-form precursor exports), Spectronaut Candidates
37
+ (pre-computed differential expression), FragPipe (`combined_protein.tsv`), and a generic
38
+ CSV/TSV column-mapping importer. Contaminant/decoy filtering, log2 transformation, and
39
+ semantic-type tagging are applied on import.
40
+ - **Pipeline** — experiment annotation (group assignment), normalization
41
+ (median / quantile / VSN), missing-value imputation (kNN / MinProb / mean / median /
42
+ zero), and differential expression cascading DEqMS → limma → client-side Welch's t-test.
43
+ - **Visualization** — volcano, clustered expression heatmap, sample-level PCA, group-mean
44
+ correlation, QC dashboard (MA / CV / missingness / intensity trend), and an SPC dashboard
45
+ with Nelson-rule instrument monitoring.
46
+ - **Biological interpretation** — g:Profiler over-representation analysis (GO, KEGG,
47
+ Reactome, WikiPathways across 9 organisms) with dot/bar charts cross-linked to the
48
+ volcano, plus a UniProt context panel for per-protein metadata.
49
+ - **Sharing** — publish read-only, access-verified, versioned review snapshots
50
+ (table + volcano + enrichment) to a reviewer group.
51
+ - R scripts (limma, DEqMS, VSN) run server-side with client-side fallbacks throughout, so
52
+ the package is fully functional without a configured R environment.
package/CLAUDE.md ADDED
@@ -0,0 +1,178 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Purpose
6
+
7
+ Datagrok extension for mass-spectrometry proteomics analysis. Imports search-engine output
8
+ (MaxQuant, Spectronaut, FragPipe, generic CSV/TSV), runs the analysis pipeline
9
+ (annotate groups -> normalize -> impute -> differential expression -> enrichment),
10
+ and adds the matching viewers (volcano, heatmap, PCA, QC dashboard, enrichment charts).
11
+ Exposed in the platform under the top-menu **Proteomics**.
12
+
13
+ Auth / credentials: none. The package only calls public REST APIs (UniProt, g:Profiler).
14
+ R scripts (`scripts/limma_de.R`, `scripts/deqms_de.R`, `scripts/vsn_normalize.R`) run server-side
15
+ via `grok.functions.call(...)` and need a configured Datagrok R compute environment; every
16
+ R path has a client-side TypeScript fallback.
17
+
18
+ ## Architecture
19
+
20
+ Functional pipeline. Each step mutates a shared `DG.DataFrame` in-place and records its
21
+ completion as a `proteomics.*` tag on the DataFrame. Downstream functions read those tags
22
+ as preconditions. There is no separate state store.
23
+
24
+ - **`src/package.ts`** — entry point. `PackageFunctions` class with `@grok.decorators.func`
25
+ / `init` / `panel` methods. Each handler validates that a table is open, checks any
26
+ required `proteomics.*` precondition tag, then delegates to `analysis/` or `viewers/`.
27
+ The **Proteomics** menu is built two ways (see "Menu architecture" below).
28
+ - **`src/menu.ts`** — builds the full **Proteomics** ribbon menu (Import + Annotate /
29
+ Analyze / Visualize / Share) on a table view's `ribbonMenu`, with the `isEnabled` grey-out:
30
+ sample-level items disable themselves on a Spectronaut Candidates table (no per-sample
31
+ intensities). The `buildProteomicsTopMenu` autostart in `package.ts` calls
32
+ `buildProteomicsRibbonMenu` once per Proteomics table view.
33
+
34
+ ### Menu architecture (read before touching the menu — non-obvious, verified live)
35
+
36
+ The **Proteomics** menu lives in two independent hosts. A decorator `top-menu` populates
37
+ `grok.shell.topMenu` (the **left-bar main menu**) ONLY; it does NOT populate a table view's
38
+ ribbon. So:
39
+
40
+ - **Left-bar main menu** = decorator `top-menu` strings on the `importX` handlers in
41
+ `package.ts`, e.g. `Proteomics | Import | Spectronaut Candidates...`. This is the
42
+ always-available entry point — reachable with **no table open** (the ribbon shows no
43
+ package menu on the Home/start view). Declaration order = menu order (Candidates first,
44
+ then Report). Only Import is here, because import is the only thing you do without a table.
45
+ - **Ribbon (table view)** = built programmatically in `menu.ts` onto `view.ribbonMenu`, and
46
+ only when a Proteomics table (`proteomics.source` tag) is active. It builds the WHOLE menu
47
+ including its own Import group — the decorator Import does not reach the ribbon, and a
48
+ view's own `ribbonMenu` 'Proteomics' group is what the ribbon shows. Built once per view;
49
+ do NOT rebuild on view activation (`isEnabled` re-evaluates on open — rebuilding the menu
50
+ duplicates it, which was a real bug).
51
+
52
+ To add a menu item: if it's an importer, add a decorator `top-menu` handler in `package.ts`
53
+ (left-bar menu) AND an `imp.item(...)` line in `menu.ts` (ribbon). Otherwise add the handler
54
+ in `package.ts` and an item in the right group in `menu.ts` (pass `sampleOnly` as the
55
+ `isEnabled` option if it needs per-sample data). Don't give the analysis groups decorator
56
+ `top-menu` strings — they'd show in the left-bar menu with no table context and can't grey
57
+ out.
58
+ - **`src/analysis/`** — pure computation that mutates the DataFrame and sets a completion
59
+ tag. One file per step: `experiment-setup.ts` (groups + `getGroups`/`setGroups`),
60
+ `normalization.ts`, `imputation.ts`, `differential-expression.ts`, `pca.ts`, `enrichment.ts`.
61
+ `log2-scale.ts` is the exception — a post-import corrective (Analyze | Set Log2 Scale...) that
62
+ rebuilds the `log2(...)` columns from the pristine originals and *clears* the downstream completion
63
+ tags (base data changed → pipeline must re-run).
64
+ - **`src/parsers/`** — vendor file -> filtered, log2-transformed, semantic-typed DataFrame.
65
+ `maxquant-parser.ts`, `spectronaut-parser.ts` (PG report — per-sample quant),
66
+ `spectronaut-candidates-parser.ts` (Candidates report — pre-computed DE; emits the
67
+ canonical `log2FC` / `p-value` / `adj.p-value` / `significant` shape and sets
68
+ `proteomics.de_complete`, so the pipeline skips Annotate/Normalize/Impute/DE),
69
+ `fragpipe-parser.ts`, `generic-parser.ts`, plus `shared-utils.ts` (log2 transform,
70
+ log2-status detection, delimiter detection, primary-id column extraction). Parsers return
71
+ unnamed DataFrames; the import handler in `package.ts` sets `df.name` from the filename.
72
+ - **`src/viewers/`** — `DG.Viewer` factories that read an already-analysed DataFrame.
73
+ `volcano.ts`, `heatmap.ts`, `pca-plot.ts`, `qc-dashboard.ts` (+ `qc-computations.ts` for
74
+ MA / CV / missingness math), `enrichment-viewers.ts`.
75
+ - **`src/panels/uniprot-panel.ts`** — context-panel widget that fetches per-protein metadata
76
+ on cell click. Registered via `@grok.decorators.panel` with a `semType` filter.
77
+ - **`src/utils/proteomics-types.ts`** — `SEMTYPE` constants. **Single source of truth** for
78
+ every `Proteomics-*` semantic type string. Use these; never inline the strings.
79
+ - **`src/utils/column-detection.ts`** — `findColumn(df, semType, nameHints)` and
80
+ `findProteomicsColumns(df)`. **Use these instead of `df.col('Gene names')`** so the
81
+ package keeps working on tables from new vendors that haven't been semantically tagged.
82
+ - **`detectors.js`** (package root, plain JS — not under `src/`) — semantic-type detectors
83
+ loaded by the platform before the webpack bundle. Mirrors the `SEMTYPE` constants. If you
84
+ add a new semantic type, add a detector here too.
85
+
86
+ ### Pipeline shape (read before editing)
87
+
88
+ ```
89
+ parse* (parsers/)
90
+ -> grok.shell.addTableView(df)
91
+ -> showAnnotationDialog (analysis/experiment-setup) sets proteomics.groups
92
+ -> showNormalizationDialog (analysis/normalization) sets proteomics.normalized
93
+ -> showImputationDialog (analysis/imputation) sets proteomics.imputed
94
+ -> showDEDialog (analysis/differential-expression) sets proteomics.de_complete (+ proteomics.de_method)
95
+ -> createVolcanoPlot / createExpressionHeatmap / showEnrichmentDialog
96
+ ```
97
+
98
+ Workflow state is **only** on the DataFrame as tags (see "Tags" below). Group assignments
99
+ are JSON-encoded in `proteomics.groups` — always go through `getGroups(df)` / `setGroups(df, g)`
100
+ in `analysis/experiment-setup.ts`; do not parse the tag yourself.
101
+
102
+ PCA is the one viewer that produces a **new** sample-level DataFrame (rows = samples, not
103
+ proteins). It must open in its own table view — never add the PCA viewer to the protein
104
+ table view. See the `showPcaPlot` handler in `package.ts` for the pattern.
105
+
106
+ ### Function-naming convention (load-bearing)
107
+
108
+ The package splits the common shapes by prefix. Match the prefix when adding new code:
109
+
110
+ | Prefix | Shape | Side effect |
111
+ |---|---|---|
112
+ | `showX(df)` | opens a `ui.dialog(...)` | mutates `df` on OK |
113
+ | `openX(df)` | creates a table view / dashboard | adds to the shell |
114
+ | `createX(df, opts?)` | returns a `DG.Viewer` | none — pure factory |
115
+ | `parseXText(text)` | returns a `DG.DataFrame` | none — caller adds to shell + sets name |
116
+
117
+ Menu paths that open a dialog end with `...`; immediate-action items do not.
118
+
119
+ ## Glossary
120
+
121
+ | Concept | Code | Meaning |
122
+ |---|---|---|
123
+ | Intensity column | `DG.Column` with `semType = SEMTYPE.INTENSITY` | One sample's per-protein abundance. Raw or log2-transformed; analysis steps operate on the `log2(...)` variants. |
124
+ | Group assignment | `GroupAssignment` interface (`analysis/experiment-setup.ts`); JSON in `proteomics.groups` tag | Maps two named groups (e.g. Control / Treatment) to the intensity columns belonging to each. Required input for normalization, imputation, DE, PCA, enrichment. |
125
+ | DE result | `log2FC`, `p-value`, `adj.p-value`, `significant` columns added by `runDifferentialExpression` / R scripts | Per-protein output of differential expression. Volcano/heatmap require these — they check `proteomics.de_complete`. |
126
+ | Fallback chain | `try { R } catch { client-side }` in `analysis/` | DE runs DEqMS -> limma -> client t-test; VSN -> quantile. R is optional; the package must still function without it. |
127
+ | Cross-DF link (enrichment) | `enrichDf.onCurrentRowChanged` subscription in `viewers/enrichment-viewers.ts` | Selecting a GO/KEGG term row in the enrichment DataFrame highlights the linked proteins in the protein DataFrame's volcano/heatmap. |
128
+
129
+ ### DataFrame tags
130
+
131
+ All workflow state lives in tags prefixed `proteomics.`. Checking and setting these tags is
132
+ how the pipeline coordinates between steps — there is no other state store.
133
+
134
+ | Tag | Value | Set by |
135
+ |---|---|---|
136
+ | `proteomics.source` | `'maxquant'` / `'spectronaut'` / `'spectronaut-candidates'` / `'fragpipe'` / `'generic'` | parsers |
137
+ | `proteomics.groups` | JSON `GroupAssignment` | `setGroups()` after Annotate Experiment |
138
+ | `proteomics.organism` | g:Profiler code (e.g. `rnorvegicus`) | `setOrganism()` — auto-detected at import from the data's organism column (`detectOrganismCode`), confirmable in Annotate Experiment, and persisted on Enrichment OK; read by enrichment + subcellular-location gene fallback |
139
+ | `proteomics.normalized` | `'true'` | any normalization method |
140
+ | `proteomics.preNormalized` | `'true'` | Spectronaut parser (input was already normalized) |
141
+ | `proteomics.imputed` | `'true'` | any imputation method |
142
+ | `proteomics.de_complete` | `'true'` | DE pipeline completion, or Spectronaut Candidates parser (already-computed DE) |
143
+ | `proteomics.de_method` | `'limma'` / `'deqms'` / `'t-test'` / `'spectronaut'` | DE on completion (which method actually ran) |
144
+ | `proteomics.enrichment` | `'true'` | enrichment DataFrame marker |
145
+
146
+ ## Conventions specific to this package
147
+
148
+ - **Look up columns via `findColumn` / `findProteomicsColumns`**, never by raw name. Imports
149
+ from new vendors may not be semantically typed yet, so the helper falls back to name
150
+ hints. Hard-coded `df.col('Gene names')` will silently miss valid columns.
151
+ - **Use `SEMTYPE.*` from `src/utils/proteomics-types.ts`** for every `Proteomics-*` string.
152
+ These are the single source of truth and are mirrored in `detectors.js`.
153
+ - **Set the matching `proteomics.*` tag** when finishing an analysis step. Downstream
154
+ viewers gate on it. If you add a new step, pick a tag name now and document it here.
155
+ - **Always provide a client-side fallback for R script calls.** The DE pipeline cascades
156
+ DEqMS -> limma -> client-side Welch's t-test; normalization cascades VSN -> quantile.
157
+ Never assume the platform R environment is configured.
158
+ - **Idempotency on re-run.** DE is guarded by checking `proteomics.de_complete` in the
159
+ menu handler. Normalization/imputation are not currently guarded — if you add a step that
160
+ adds columns, follow the `ensureFreshFloat` pattern in `viewers/qc-computations.ts`
161
+ (remove the column if it exists, then add it) so re-running doesn't produce duplicates.
162
+ - **PCA opens in a separate table view.** Its sample-level DataFrame has a different row
163
+ count from the protein DataFrame, so `createPcaPlot` returns `{viewer, pcaDf}` and the
164
+ caller does `grok.shell.addTableView(pcaDf)` before docking the viewer. Do not add a PCA
165
+ viewer to the protein table view.
166
+ - **Heatmap clones the DataFrame for filter isolation.** `createExpressionHeatmap` uses
167
+ `df.clone(filter)` so its top-N sort/filter doesn't leak into the volcano sharing the
168
+ same source DataFrame.
169
+ - **Adding a new semantic type:** update `SEMTYPE` in `src/utils/proteomics-types.ts` **and**
170
+ add a detector in `detectors.js`. The README's "Semantic Type Detection" table is part of
171
+ the public contract.
172
+ - **Adding a new parser, analysis step, or viewer:** the file layout for each is documented
173
+ in `.planning/codebase/STRUCTURE.md` under "Where to Add New Code". Follow that contract
174
+ (new file under the matching directory, register handler in `src/package.ts`, add
175
+ precondition tag check, add test under `src/tests/`).
176
+ - **Deeper context:** `.planning/codebase/{ARCHITECTURE,STRUCTURE,CONVENTIONS,CONCERNS,
177
+ INTEGRATIONS,TESTING,STACK}.md` are kept up to date and go well beyond this file. Read
178
+ them when a task is non-trivial; do not duplicate their content here.
package/README.md CHANGED
@@ -1,3 +1,195 @@
1
- # Proteomics
2
- Proteomics is a Datagrok Package for handling and processing proteins.
3
- The package is under GPL-3 due to some of its dependencies (like antpack 0.3.8.6) are under such licence
1
+ # Proteomics
2
+
3
+ Mass spectrometry-based proteomics data analysis [package](https://datagrok.ai/help/develop/#packages)
4
+ for the [Datagrok](https://datagrok.ai) platform.
5
+
6
+ It takes a study from raw search-engine output to a result a biologist can act on —
7
+ *which proteins changed, what pathways they implicate, and what each protein is* — and lets
8
+ you share that result as a read-only snapshot a collaborator can explore without touching
9
+ the raw data or writing a line of code. It covers **label-free quantitative proteomics**
10
+ from both **DDA** (MaxQuant, FragPipe) and **DIA** (Spectronaut) workflows.
11
+
12
+ Open it from the **Proteomics** top menu.
13
+
14
+ ![The Proteomics analysis view: an interactive volcano plot of differential expression (top), a clustered expression heatmap of the top proteins (bottom), and the UniProt context panel for the selected protein — showing its name, gene, organism, function, GO terms and per-group abundance — all on one screen](images/hero.png)
15
+
16
+ *One screen from statistics to biology: the interactive volcano, the clustered expression heatmap, and in-place UniProt annotation for the selected protein.*
17
+
18
+ ## Where it fits
19
+
20
+ The package picks up **where the search engine leaves off** and carries the study all the
21
+ way to biological interpretation. It does not replace MaxQuant, FragPipe, DIA-NN or
22
+ Spectronaut, and it does not touch raw instrument files (`.raw`, `.mzML`, `.d`) — you
23
+ bring in the protein/quant table they produce. From there it stands in for the four-to-six
24
+ disconnected desktop tools and hand-exported files that normally sit between that table
25
+ and a finished figure: import, QC, statistics, interpretation, and sharing happen in one
26
+ place, and the result stays reproducible instead of scattered across scripts and
27
+ spreadsheets.
28
+
29
+ ## What you can achieve
30
+
31
+ ### If you generate the data — the proteomics scientist
32
+
33
+ - **Stand behind your quantification.** Normalization removes between-sample technical
34
+ bias and principled missing-value handling deals with the dropout that is intrinsic to
35
+ MS, so downstream fold-changes reflect biology rather than loading or detection
36
+ artifacts.
37
+ - **Make a defensible significance call.** Statistics built for the small-sample,
38
+ high-dimensional reality of proteomics (limma / DEqMS) give you a ranked, FDR-controlled
39
+ list of changed proteins you can justify to a reviewer — not an ad-hoc cutoff.
40
+ - **Catch problems before they become false discoveries.** Per-run QC and longitudinal
41
+ process control surface a mislabeled channel, a weak replicate, or instrument drift
42
+ across a batch *before* it drives a result.
43
+ - **Hand off something reproducible.** Publish the finished analysis as a versioned,
44
+ read-only snapshot — the collaborator sees exactly what you saw, and a later re-analysis
45
+ supersedes rather than overwrites it.
46
+
47
+ ### If you interpret the data — the biology scientist
48
+
49
+ - **See what changed.** An interactive volcano shows which proteins go up or down between
50
+ your conditions and how strongly — sorted, labeled, and clickable, not a static figure.
51
+ - **Understand what it means.** Enrichment collapses a list of hundreds of changed
52
+ proteins into the handful of biological processes, pathways, and complexes that are
53
+ actually over-represented (GO, KEGG, Reactome, WikiPathways) — and selecting a pathway
54
+ highlights its proteins back on the volcano, so statistics and biology stay connected.
55
+ - **Know what each hit is.** Click any protein to pull its name, gene, function, and
56
+ subcellular location from UniProt, in place — no separate database lookups.
57
+ - **Explore it yourself.** Open the shared snapshot and filter, select, and cross-link
58
+ interactively. You don't need the raw files, the analysis environment, or another round
59
+ trip through the analyst to ask your own follow-up questions.
60
+
61
+ ## Supported inputs
62
+
63
+ | Format | File | Notes |
64
+ |--------|------|-------|
65
+ | **MaxQuant** | `proteinGroups.txt` | DDA. LFQ / Razor / iBAQ intensities; contaminant & reverse-hit filtering |
66
+ | **Spectronaut — Report** | PG-level export | DIA. Pivots long-form precursor exports to a protein × sample matrix; streams multi-GB files; q-value filtering |
67
+ | **Spectronaut — Candidates** | pre-computed DE export | DIA. Already-computed fold-change / FDR per protein; skips straight to visualization |
68
+ | **FragPipe** | `combined_protein.tsv` | DDA. MaxLFQ / Intensity / Razor; Philosopher contaminant filtering |
69
+ | **Generic matrix** | any CSV/TSV | Column-mapping dialog; auto-detects delimiter and log2 status |
70
+
71
+ > DIA-NN is **not** currently supported as a direct importer — bring DIA-NN output in via
72
+ > the **Generic matrix** path, or as a Spectronaut-style matrix.
73
+
74
+ ## Workflow
75
+
76
+ The pipeline runs as a sequence of steps from the **Proteomics** menu. Each step records
77
+ its completion on the table, and downstream steps read those as preconditions, so the
78
+ menu greys out actions that don't yet apply (and items that need per-sample intensities
79
+ are disabled on the pre-computed Spectronaut Candidates table).
80
+
81
+ ![Proteomics analysis pipeline: Import, Annotate Experiment, Normalize, Impute and Differential Expression, each setting a proteomics.* tag that gates the next step, then branching to Visualize, Enrich and Share](images/pipeline.svg)
82
+
83
+ ### Import
84
+ `Spectronaut Candidates...` · `Spectronaut Report...` · `MaxQuant...` · `FragPipe...` ·
85
+ `Generic Matrix...` — also available from the left-bar main menu with no table open.
86
+
87
+ ### Annotate
88
+ - **Annotate Experiment** — assign intensity columns to two named groups
89
+ (e.g. Control vs. Treatment); required input for normalization, imputation, DE, PCA and
90
+ enrichment.
91
+
92
+ ### Analyze
93
+ - **Normalize** — median centering, quantile normalization, or VSN (variance-stabilizing;
94
+ R, with a quantile fallback).
95
+ - **Impute Missing Values** — k-nearest-neighbor, MinProb (Perseus-style downshifted
96
+ draw), mean, median, or zero.
97
+ - **Differential Expression** — DEqMS (peptide-count-weighted variance) → limma
98
+ (empirical-Bayes moderated t-test) → client-side Welch's t-test, cascading down on
99
+ availability. Adds `log2FC`, `p-value`, `adj.p-value` and a `significant` flag.
100
+ - **Enrichment Analysis** — over-representation against GO (BP/MF/CC), KEGG, Reactome and
101
+ WikiPathways through g:Profiler, for 9 model organisms (human, mouse, rat, yeast,
102
+ *E. coli*, zebrafish, fly, *Arabidopsis*, *C. elegans*), with optional smart GO pruning.
103
+ - **Compute SPC Status** — score the active run against an instrument-QC baseline.
104
+
105
+ ### Visualize
106
+ - **Volcano Plot** — log2FC vs. -log10(p), with significance threshold lines; switch the
107
+ significance metric, color by significance or UniProt subcellular location, and label
108
+ the top-N proteins independently of selection.
109
+ - **Heatmap** — clustered expression heatmap of the top differential proteins.
110
+ - **PCA** — sample-level principal-component scatter (opens in its own view).
111
+ - **Group-Mean Correlation** — group1 vs. group2 mean per protein, with Pearson/Spearman.
112
+ - **QC Dashboard** — MA plot, coefficient-of-variation, missingness matrix and an
113
+ intensity trend.
114
+ - **SPC Dashboard** — longitudinal control charts over instrument metrics
115
+ (median intensity, missingness, replicate correlation, protein count) with Nelson-rule
116
+ flagging and a Pareto of tripped rules.
117
+ - **Enrichment Charts** — dot plot and bar chart of the top terms, split Up/Down by
118
+ direction; selecting a term highlights its proteins back in the volcano.
119
+
120
+ Selecting a term in the enrichment results highlights that pathway's member proteins back
121
+ on the volcano — a pathway you find in the biology view is immediately located in the
122
+ statistics view, so the two never drift apart:
123
+
124
+ ![Enrichment results grid with the KEGG Cell Cycle term (KEGG:04110) selected](images/enrichment-term-selected.png)
125
+
126
+ ![The volcano plot after selecting the term — the Cell Cycle pathway's member proteins appear as highlighted points and selected rows](images/enrichment-crosslink.png)
127
+
128
+ *Selecting the KEGG Cell Cycle term (top) highlights its 25 member proteins on the volcano (bottom).*
129
+
130
+ ### Share
131
+ - **Share Analysis for Review** — publish a trimmed, read-only snapshot (table + volcano +
132
+ enrichment charts) to a reviewer group. The source table is never mutated; reviewer
133
+ access is verified (and rolled back on an over-permissive grant); the snapshot is
134
+ optionally re-opened and shape-checked to confirm it survives a reload; re-publishing
135
+ supersedes the prior version. A **Shared Analysis** context panel surfaces the
136
+ publication's audit trail.
137
+
138
+ ## Protein annotation
139
+
140
+ Clicking a protein-ID cell opens the **UniProt** info panel, which fetches the protein
141
+ name, gene, organism, functional description, GO terms (MF / BP / CC) and KEGG / Reactome /
142
+ InterPro cross-references from the UniProt REST API and caches them in App Data.
143
+
144
+ ## Semantic type detection
145
+
146
+ Columns are auto-detected and tagged so the package keeps working on tables from vendors
147
+ it hasn't seen — detectors match on column-name hints and validate sample values.
148
+
149
+ | Semantic type | Detects |
150
+ |---------------|---------|
151
+ | `Proteomics-ProteinId` | Protein IDs, Majority Protein IDs, UniProt, Accession (validated against the UniProt accession pattern) |
152
+ | `Proteomics-GeneSymbol` | Gene Names, Gene Symbol, Gene |
153
+ | `Proteomics-Log2FC` | columns naming "log2" with "fc"/"fold" |
154
+ | `Proteomics-PValue` | p-value, adj.p, FDR, q-value (range 0–1) |
155
+ | `Proteomics-Intensity` | Intensity, LFQ / Reporter / Razor / MaxLFQ Intensity, iBAQ |
156
+ | `Proteomics-SubcellularLocation` | UniProt subcellular-location strings |
157
+ | `Proteomics-DisplayName`, `Proteomics-SourceId` | gene-label resolver fields |
158
+ | `Proteomics-NumeratorMean`, `Proteomics-DenominatorMean` | per-group means in Spectronaut Candidates exports |
159
+
160
+ ## Integrations & requirements
161
+
162
+ - **No credentials required.** The package calls only public REST APIs — UniProt (protein
163
+ annotation) and g:Profiler (enrichment).
164
+ - **R is optional.** `limma_de.R`, `deqms_de.R` and `vsn_normalize.R` run server-side and
165
+ require a configured Datagrok R compute environment; every R path has a client-side
166
+ TypeScript fallback, so the package is fully functional without R.
167
+
168
+ ## Demo data
169
+
170
+ Example datasets ship in `files/demo/` (see its `README.md`):
171
+
172
+ - `proteinGroups.txt` — small MaxQuant example
173
+ - `cptac-spike-in.txt` — CPTAC UPS spike-in reference with known concentrations
174
+ - `fragpipe-smoke-test.tsv` — FragPipe `combined_protein.tsv` example
175
+ - `spectronaut-hye-candidates.tsv` — Spectronaut Candidates (pre-computed DE)
176
+ - `spectronaut-hye-precursor.tsv` / `spectronaut-hye-mix.tsv` — Spectronaut long-form reports
177
+
178
+ ## Development
179
+
180
+ ```bash
181
+ cd packages/Proteomics
182
+
183
+ npm run build # grok api && grok check --soft && webpack
184
+ grok publish local # build + publish to your configured server
185
+ grok link && npm install # link local datagrok-api / @datagrok-libraries
186
+ grok test --host localhost # run the package tests
187
+ ```
188
+
189
+ ## See also
190
+
191
+ - [Personas & capabilities](docs/personas-and-capabilities.md) — who (analyst vs biology
192
+ scientist) can do what, and how the boundary is enforced.
193
+ - **TODO:** Datagrok Proteomics documentation — the help page at
194
+ `https://datagrok.ai/help/domains/bio/proteomics` does not exist yet and still needs to
195
+ be written.
package/detectors.js CHANGED
@@ -1,9 +1,152 @@
1
- /**
2
- * The class contains semantic type detectors.
3
- * Detectors are functions tagged with `DG.FUNC_TYPES.SEM_TYPE_DETECTOR`.
4
- * See also: https://datagrok.ai/help/develop/how-to/define-semantic-type-detectors
5
- * The class name is comprised of <PackageName> and the `PackageDetectors` suffix.
6
- * Follow this naming convention to ensure that your detectors are properly loaded.
7
- */
8
- class MonomerDBPackageDetectors extends DG.Package {
9
- }
1
+ class ProteomicsPackageDetectors extends DG.Package {
2
+ //meta.role: semTypeDetector
3
+ //input: column col
4
+ //output: string semType
5
+ detectProteinId(col) {
6
+ if (col.type !== DG.TYPE.STRING)
7
+ return null;
8
+ const name = col.name.toLowerCase();
9
+ if (name === 'protein ids' || name === 'protein id' || name === 'majority protein ids' ||
10
+ name === 'primary protein id' || name === 'leading protein' || name === 'leading razor protein' ||
11
+ name === 'uniprot' || name === 'accession') {
12
+ const sample = col.toList().slice(0, 20).filter((v) => v != null);
13
+ const uniprotPattern = /^(?:[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9](?:[A-Z][A-Z0-9]{2}[0-9]){1,2})$/;
14
+ if (sample.some((v) => uniprotPattern.test(v.split(';')[0]))) {
15
+ col.semType = 'Proteomics-ProteinId';
16
+ return col.semType;
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+
22
+ //meta.role: semTypeDetector
23
+ //input: column col
24
+ //output: string semType
25
+ detectGeneSymbol(col) {
26
+ if (col.type !== DG.TYPE.STRING)
27
+ return null;
28
+ const name = col.name.toLowerCase();
29
+ if (name === 'gene names' || name === 'gene name' || name === 'gene symbol' || name === 'gene') {
30
+ col.semType = 'Proteomics-GeneSymbol';
31
+ return col.semType;
32
+ }
33
+ return null;
34
+ }
35
+
36
+ //meta.role: semTypeDetector
37
+ //input: column col
38
+ //output: string semType
39
+ detectSubcellularLocation(col) {
40
+ if (col.type !== DG.TYPE.STRING)
41
+ return null;
42
+ const name = col.name.toLowerCase();
43
+ if (name === 'subcellular location' || name === 'subcellular location [cc]' ||
44
+ name === 'subcellular' || name.includes('subcellular location') || name.includes('subcellular')) {
45
+ col.semType = 'Proteomics-SubcellularLocation';
46
+ return col.semType;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ //meta.role: semTypeDetector
52
+ //input: column col
53
+ //output: string semType
54
+ detectLog2FC(col) {
55
+ if (col.type !== DG.TYPE.FLOAT && col.type !== DG.TYPE.INT)
56
+ return null;
57
+ const name = col.name.toLowerCase();
58
+ if (name.includes('log2') && (name.includes('fc') || name.includes('fold'))) {
59
+ col.semType = 'Proteomics-Log2FC';
60
+ return col.semType;
61
+ }
62
+ return null;
63
+ }
64
+
65
+ //meta.role: semTypeDetector
66
+ //input: column col
67
+ //output: string semType
68
+ detectPValue(col) {
69
+ if (col.type !== DG.TYPE.FLOAT && col.type !== DG.TYPE.INT)
70
+ return null;
71
+ const name = col.name.toLowerCase();
72
+ if ((name.includes('p-value') || name.includes('pvalue') || name.includes('p.value') ||
73
+ name.includes('adj.p') || name.includes('fdr') || name.includes('q-value') || name.includes('qvalue')) &&
74
+ col.min >= 0 && col.max <= 1) {
75
+ col.semType = 'Proteomics-PValue';
76
+ return col.semType;
77
+ }
78
+ return null;
79
+ }
80
+
81
+ //meta.role: semTypeDetector
82
+ //input: column col
83
+ //output: string semType
84
+ detectIntensity(col) {
85
+ if (col.type !== DG.TYPE.FLOAT && col.type !== DG.TYPE.INT)
86
+ return null;
87
+ const name = col.name.toLowerCase();
88
+ const isRawIntensity = name.startsWith('intensity') || name.startsWith('lfq intensity') ||
89
+ name.startsWith('reporter intensity') || name.startsWith('ibaq') ||
90
+ name.endsWith(' maxlfq intensity') || name.endsWith(' razor intensity') ||
91
+ name.endsWith(' intensity');
92
+ const isLog2Intensity = (name.startsWith('log2(') || name.startsWith('log2 ')) &&
93
+ (name.includes('intensity') || name.includes('ibaq'));
94
+ if (isRawIntensity || isLog2Intensity) {
95
+ col.semType = 'Proteomics-Intensity';
96
+ return col.semType;
97
+ }
98
+ return null;
99
+ }
100
+
101
+ //meta.role: semTypeDetector
102
+ //input: column col
103
+ //output: string semType
104
+ detectDisplayName(col) {
105
+ if (col.type !== DG.TYPE.STRING)
106
+ return null;
107
+ if (col.name.toLowerCase() === 'display name') {
108
+ col.semType = 'Proteomics-DisplayName';
109
+ return col.semType;
110
+ }
111
+ return null;
112
+ }
113
+
114
+ //meta.role: semTypeDetector
115
+ //input: column col
116
+ //output: string semType
117
+ detectSourceId(col) {
118
+ if (col.type !== DG.TYPE.STRING)
119
+ return null;
120
+ if (col.name.toLowerCase() === 'source id') {
121
+ col.semType = 'Proteomics-SourceId';
122
+ return col.semType;
123
+ }
124
+ return null;
125
+ }
126
+
127
+ //meta.role: semTypeDetector
128
+ //input: column col
129
+ //output: string semType
130
+ detectNumeratorMean(col) {
131
+ if (col.type !== DG.TYPE.FLOAT && col.type !== DG.TYPE.INT)
132
+ return null;
133
+ if (col.name.toLowerCase() === 'numerator mean') {
134
+ col.semType = 'Proteomics-NumeratorMean';
135
+ return col.semType;
136
+ }
137
+ return null;
138
+ }
139
+
140
+ //meta.role: semTypeDetector
141
+ //input: column col
142
+ //output: string semType
143
+ detectDenominatorMean(col) {
144
+ if (col.type !== DG.TYPE.FLOAT && col.type !== DG.TYPE.INT)
145
+ return null;
146
+ if (col.name.toLowerCase() === 'denominator mean') {
147
+ col.semType = 'Proteomics-DenominatorMean';
148
+ return col.semType;
149
+ }
150
+ return null;
151
+ }
152
+ }