|
1 | | -# CLAUDE.md |
| 1 | +# NA |
2 | 2 |
|
3 | | -This file provides guidance to Claude Code (claude.ai/code) when working |
4 | | -with code in this repository. |
5 | | - |
6 | | -## What is prolfqua |
7 | | - |
8 | | -An R package for mass spectrometry-based label-free quantification (LFQ) |
9 | | -proteomics analysis. It provides a complete workflow: QC, normalization, |
10 | | -protein aggregation, statistical modelling, hypothesis testing, and |
11 | | -sample size estimation. Data is always in long (tidy) format. Branch |
12 | | -`main` is the active development branch. |
13 | | - |
14 | | -## Build & Test Commands |
15 | | - |
16 | | -``` bash |
17 | | -make test # Run testthat suite (runs document first) |
18 | | -make check-fast # R CMD check without vignettes (quick validation) |
19 | | -make check # Full R CMD check (document → build → check) |
20 | | -make document # Generate roxygen2 docs (NAMESPACE + man/) |
21 | | -make install # Install package locally |
22 | | -make lint # Run lintr static analysis |
23 | | -make format # Format with air |
24 | | -make build-vignettes # Build vignettes into inst/doc |
25 | | -make site # Build pkgdown site locally |
26 | | -``` |
27 | | - |
28 | | -Single test file: |
29 | | - |
30 | | -``` bash |
31 | | -Rscript -e "testthat::test_file('tests/testthat/test-LFQData.R')" |
32 | | -``` |
33 | | - |
34 | | -Library setup: |
35 | | - |
36 | | -``` bash |
37 | | -Rscript -e ".libPaths()" |
38 | | -``` |
39 | | - |
40 | | -Use the normal user / system R libraries for this workspace; `renv` |
41 | | -autoload is disabled. |
42 | | - |
43 | | -## Code Style |
44 | | - |
45 | | -- **Line length:** 120 chars, **indentation:** 2 spaces (`.lintr`) |
46 | | -- `object_name_linter` is disabled — the codebase uses camelCase for R6 |
47 | | - classes and snake_case/mixed for functions |
48 | | -- NAMESPACE is **auto-generated** by roxygen2 — never edit directly; run |
49 | | - `make document` |
50 | | -- Roxygen is configured with `r6 = TRUE` for R6 class documentation |
51 | | -- **Never use `\dontrun{}` or `\donttest{}` in `@examples`** — all |
52 | | - examples must run during R CMD check. If an example is too slow, |
53 | | - optimize it instead of skipping it. |
54 | | - |
55 | | -## Architecture |
56 | | - |
57 | | -### Core Data Flow |
58 | | - |
59 | | - Raw Data + AnalysisConfiguration → LFQData |
60 | | - ├── get_Transformer() → LFQDataTransformer (log2, robscale, normalize) |
61 | | - ├── get_Aggregator() → LFQDataAggregator (peptide → protein rollup) |
62 | | - ├── get_Stats() → LFQDataStats (CV, variance per group) |
63 | | - ├── get_Plotter() → LFQDataPlotter (heatmaps, PCA, boxplots) |
64 | | - ├── get_Summariser() → LFQDataSummariser (missingness, hierarchy counts) |
65 | | - └── get_Imputer() → LFQDataImp (missing value imputation) |
66 | | - |
67 | | - LFQData → build_contrast_analysis(lfqdata, modelstr, contrasts, method) |
68 | | - └── Returns a Facade with uniform API: |
69 | | - $get_contrasts(), $get_missing(), $get_Plotter(), $to_wide() |
70 | | - |
71 | | -### Facade Pattern (`ContrastsFacades.R`, `ContrastsChildToParentFacades.R`, `build_contrast_analysis.R`) |
72 | | - |
73 | | -[`build_contrast_analysis()`](https://wolski.github.io/prolfqua/reference/build_contrast_analysis.md) |
74 | | -is the recommended entry point. Each method dispatches to a Facade class |
75 | | -that wires strategy → model → contrasts → moderation internally. |
76 | | - |
77 | | -Facades split by input/output hierarchy shape. |
78 | | -`lookup_facade(name)$needs` returns one of two values: |
79 | | - |
80 | | -**`needs = "same"`** — facade emits contrasts at the same hierarchy |
81 | | -level as its input (protein → protein FC, or peptide/precursor → |
82 | | -peptide/precursor FC; `subject_Id == hierarchy_keys`). Lives in |
83 | | -`R/ContrastsFacades.R`: `lm`, `rlm`, `lm_missing`, `lm_impute`, `limma`, |
84 | | -`limma_impute`, `limma_voom`, `limma_voom_impute`, `deqms`, |
85 | | -`deqms_voom`, `firth`, `limpa` |
86 | | - |
87 | | -**`needs = "nested"`** — facade takes child-level input |
88 | | -(peptide/precursor) and emits parent-level (protein) contrasts; |
89 | | -`subject_Id` is a strict subset of `hierarchy_keys`. Lives in |
90 | | -`R/ContrastsChildToParentFacades.R`: `lmer_nested`, `ropeca_nested`, |
91 | | -`firth_nested`, `limpa_nested` |
92 | | - |
93 | | -Downstream dispatch convention: protein-level readers pair with `"same"` |
94 | | -facades only; peptide-level readers pair with either. |
95 | | - |
96 | | -### Weights & `nr_children` |
97 | | - |
98 | | -`config$nr_children` names the column tracking child-feature counts |
99 | | -(e.g. peptides per protein). After `get_Aggregator()` rollup, each |
100 | | -protein×sample row gets its own count — **`nr_children` is |
101 | | -sample-wise**. For peptide/precursor-level data it is typically 1. |
102 | | - |
103 | | -**Two distinct uses:** |
104 | | - |
105 | | -1. **Fitting weights** (sample-wise): Aggregated facades (`lm`, |
106 | | - `limma`, `lm_missing`, `lm_impute`, `deqms`) pass `nr_children` as |
107 | | - `weights` by default to [`lm()`](https://rdrr.io/r/stats/lm.html) or |
108 | | - [`limma::lmFit()`](https://rdrr.io/pkg/limma/man/lmFit.html). This |
109 | | - down-weights protein intensities derived from fewer peptides in a |
110 | | - given sample. Disable with `weights = NULL`. |
111 | | - |
112 | | -2. **DEqMS variance moderation** (experiment-wide): |
113 | | - `ContrastsDEqMSFacade` additionally aggregates `nr_children` via |
114 | | - [`max()`](https://rdrr.io/r/base/Extremes.html) per protein across |
115 | | - all samples for count-dependent variance shrinkage. This is separate |
116 | | - from the fitting weights. |
117 | | - |
118 | | -**Protein-level input must carry `nr_children`.** If the column is |
119 | | -missing, |
120 | | -[`setup_analysis()`](https://wolski.github.io/prolfqua/reference/setup_analysis.md) |
121 | | -adds it set to 1 with a warning — but this defeats the purpose for |
122 | | -aggregated data where the actual peptide count matters. |
123 | | - |
124 | | -### Key Design Patterns |
125 | | - |
126 | | -**Decorator/Composition**: LFQData factory methods (`get_Transformer()`, |
127 | | -`get_Plotter()`, etc.) return decorator objects that wrap the LFQData. |
128 | | -Decorators hold a reference in their `lfq` field. |
129 | | - |
130 | | -**Method chaining**: Transformer methods return `self` for chaining, |
131 | | -access result via `$lfq`: |
132 | | - |
133 | | -``` r |
134 | | -lfqdata <- lfqdata$get_Transformer()$log2()$robscale()$lfq |
135 | | -``` |
136 | | - |
137 | | -**Strategy pattern for models**: **Strategy R6 classes for models**: |
138 | | -`StrategyLM`, `StrategyRLM`, `StrategyLmer`, `StrategyLogistf` — each |
139 | | -with `model_fun`, `isSingular`, `contrast_fun`, `df_residual`, `sigma` |
140 | | -methods. Wrapper functions |
141 | | -[`strategy_lm()`](https://wolski.github.io/prolfqua/reference/strategy.md), |
142 | | -[`strategy_rlm()`](https://wolski.github.io/prolfqua/reference/strategy.md), |
143 | | -[`strategy_lmer()`](https://wolski.github.io/prolfqua/reference/strategy.md), |
144 | | -[`strategy_logistf()`](https://wolski.github.io/prolfqua/reference/strategy.md) |
145 | | -create instances. |
146 | | -[`strategy_limma()`](https://wolski.github.io/prolfqua/reference/strategy_limma.md) |
147 | | -returns a plain list (formula, trend, robust, weights) consumed by |
148 | | -[`build_model_limma()`](https://wolski.github.io/prolfqua/reference/build_model_limma.md). |
149 | | - |
150 | | -**Config immutability**: AnalysisConfiguration is always deep-cloned |
151 | | -when passed to new LFQData instances. Never modify config in-place on an |
152 | | -existing LFQData. |
153 | | - |
154 | | -### R6 Classes (22 classes across R/) |
155 | | - |
156 | | -| Category | Classes | Files | |
157 | | -|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------| |
158 | | -| Core data | `LFQData`, `AnalysisConfiguration` | LFQData.R, AnalysisConfiguration.R | |
159 | | -| Decorators | `LFQDataTransformer`, `LFQDataAggregator`, `LFQDataStats`, `LFQDataPlotter`, `LFQDataSummariser`, `LFQDataImp` | LFQData\*.R | |
160 | | -| Model interfaces | `ModelInterface`, `Model`, `ModelFirth`, `ModelLimma` | Model\*.R, ContrastsLimma.R | |
161 | | -| Contrast interfaces | `ContrastsInterface`, `Contrasts`, `ContrastsModerated`, `ContrastsLimma`, `ContrastsROPECA`, `ContrastsMissing`, `ContrastsFirth`, `ContrastsTable` | Contrasts\*.R, ContrastFirth.R, ContrastsSimpleImpute.R | |
162 | | -| Visualization | `ContrastsPlotter` | ContrastsPlotter.R | |
163 | | -| Utilities | `MissingHelpers` | tidyMS_missingness_imputation.R | |
164 | | - |
165 | | -### AnalysisConfiguration |
166 | | - |
167 | | -Flat R6 class that maps column roles in the data: - **hierarchy**: |
168 | | -ordered measurement levels (protein_Id → peptide_Id → precursor_Id → |
169 | | -fragment_Id). `hierarchy_depth` controls which level is modelled. - |
170 | | -**factors**: explanatory variables (group, treatment). `factor_depth` |
171 | | -controls interaction depth. - **work_intensity**: response column. Uses |
172 | | -a stack (`set_response()` / `pop_response()` / `get_response()`) for |
173 | | -working with multiple intensity columns. - **file_name**: sample |
174 | | -identifier column. |
175 | | - |
176 | | -Concrete config factories (e.g. `create_config_Skyline()`, |
177 | | -`create_config_Spectronaut_Peptide()`) were in |
178 | | -`tidyMS_R6_ConcreteConfigurations.R` (now removed — |
179 | | -`create_config_MQ_peptide()` was dead code). Remaining factories are in |
180 | | -downstream packages. |
181 | | - |
182 | | -### Key Functions (not in classes) |
183 | | - |
184 | | -- `build_contrast_analysis(lfqdata, modelstr, contrasts, method)` — main |
185 | | - entry point, returns a Facade (in build_contrast_analysis.R) |
186 | | -- `setup_analysis(data, config)` — prepare data for analysis (in |
187 | | - tidyMS_data_setup.R) |
188 | | -- `build_model(data, strategy, subject_Id)` — fit per-protein models (in |
189 | | - tidyMS_build_model.R) |
190 | | -- `build_model_impute(lfqdata, strategy)` — fit with LOD imputation + |
191 | | - borrowed covariance for missing groups (in tidyMS_build_model.R) |
192 | | -- `build_model_limma(lfqdata, strategy)` — fit limma matrix model (in |
193 | | - ContrastsLimma.R) |
194 | | -- `StrategyLM`, `StrategyRLM`, `StrategyLmer` R6 classes + |
195 | | - `strategy_lm/rlm/lmer()` wrappers (tidyMS_R6_Modelling.R); |
196 | | - `StrategyLogistf` + |
197 | | - [`strategy_logistf()`](https://wolski.github.io/prolfqua/reference/strategy.md) |
198 | | - (logistf.R) |
199 | | -- [`strategy_limma()`](https://wolski.github.io/prolfqua/reference/strategy_limma.md) |
200 | | - — limma matrix model strategy (in ContrastsLimma.R) |
201 | | -- [`sim_lfq_data_peptide_config()`](https://wolski.github.io/prolfqua/reference/sim_lfq_data_peptide_config.md) |
202 | | - — simulate test data (in simulate_LFQ_data.R) |
203 | | - |
204 | | -### File Naming Convention |
205 | | - |
206 | | -- `R/LFQData*.R` — Core data container and its decorator classes |
207 | | -- `R/Model*.R`, `R/Contrasts*.R` — Modelling and hypothesis testing |
208 | | -- `R/AnalysisConfiguration.R` — Configuration (column role mapping + |
209 | | - serialization) |
210 | | -- `R/tidyMS_data_setup.R` — `setup_analysis`, `complete_cases`, |
211 | | - `sample_subset` |
212 | | -- `R/tidyMS_summarize_hierarchy.R` — `table_factors`, |
213 | | - `hierarchy_counts`, etc. |
214 | | -- `R/tidyMS_R6_Modelling.R` — Strategy R6 classes (`StrategyLM`, |
215 | | - `StrategyRLM`, `StrategyLmer`) |
216 | | -- `R/tidyMS_build_model.R` — `build_model`, `model_analyse`, imputation |
217 | | - internals |
218 | | -- `R/tidyMS_contrasts.R` — `linfct_*` family, `compute_contrast`, |
219 | | - `contrasts_linfct`, `pivot_model_contrasts_to_wide` |
220 | | -- `R/tidyMS_moderation.R` — `moderated_p_limma*`, `adjust_p_values`, |
221 | | - ROPECA, Fisher |
222 | | -- `R/tidyMS_*.R` — Other utility functions (plotting, stats, |
223 | | - aggregation, missingness) |
224 | | -- `R/utilities.R` — Shared helpers (`make_interaction_column`, |
225 | | - `.error_handler`) |
226 | | - |
227 | | -### Vectorized mode |
228 | | - |
229 | | -`options(prolfqua.vectorize = TRUE)` activates vectorized |
230 | | -implementations of `compute_contrast` and `linfct_matrix_contrasts` |
231 | | -(matrix multiplication instead of per-row loops). Affects all Wald test |
232 | | -facades (lm, rlm, firth, firth_nested, lmer_nested) and limma’s linfct |
233 | | -path. Results are numerically identical. Default is `FALSE`. |
234 | | - |
235 | | -## Testing |
236 | | - |
237 | | -- **When fixing a bug, first add a test that reproduces it**, then fix. |
238 | | - This ensures regressions are caught. |
239 | | - |
240 | | -11 test files in `tests/testthat/`: - `test-LFQData.R` — Core data |
241 | | -container and decorators - `test-Model.R` — Model fitting and |
242 | | -coefficient extraction - `test-Contrasts.R` — Contrast computation (Wald |
243 | | -test path) - `test-ContrastsFacades.R` — All facade classes and |
244 | | -[`build_contrast_analysis()`](https://wolski.github.io/prolfqua/reference/build_contrast_analysis.md) - |
245 | | -`test-ContrastsLimma.R` — Limma backend (ModelLimma, ContrastsLimma, |
246 | | -merge, 2-factor) - `test-ContrastsModeratedDEqMS.R` — DEqMS moderation |
247 | | -and facade - `test-ContrastsPlotter.R` — Contrast visualization - |
248 | | -`test-ImputeModel.R` — LOD imputation with borrowed covariance - |
249 | | -`test-plotting_functions.R` — Low-level plots - |
250 | | -`test-tidyconfig_functions.R` — Configuration and utilities - |
251 | | -`test-vectorize-contrasts.R` — Side-by-side original vs vectorized |
252 | | -contrast functions |
253 | | - |
254 | | -## Cross-Package Context |
255 | | - |
256 | | -prolfqua is part of the prolfqua ecosystem (see `../CLAUDE.md`). |
257 | | -Downstream packages depend on its R6 classes and exported API: - |
258 | | -**prolfquapp** — CLI wrapper for core facility workflows - |
259 | | -**prophosqua** — Phosphoproteomics analysis - **prolfquabenchmark** — |
260 | | -Benchmarking vignettes |
261 | | - |
262 | | -Renaming R6 methods, changing exported function signatures, or modifying |
263 | | -AnalysisConfiguration fields can silently break these packages. |
| 3 | +@AGENTS.md |
0 commit comments