main_effect_contrasts()now emits averaging contrasts with a top-levelLHS - RHS, making its output directly valid for the stricter contrast parser.- Added the
binomial_nestedfacade for nested LFQ data. It reuses the Firth backend's completed peptide-detection representation, fits protein-by-sample detected/undetected peptide counts with a quasibinomial GLM, and reports moderated protein-level log odds-ratio contrasts. A symmetric pseudo-count stabilizes separated fits, and posterior dispersion is bounded below by one by default (binomial_bound = FALSEdisables the bound). - Variance moderation now uses
limma::squeezeVar()directly.moderated_p_limma()— and therefore every moderated Wald-test facade (lm,rlm,rfit,lm_impute,lm_missing,rfit_impute,lmer_nested) and the DEqMS small-group fallback — now callslimma::squeezeVar()instead of the vendoredsqueezeVarRob()fork (limma is already a dependency). Modern limma estimates the empirical-Bayes prior robustly on the fractional / low residual degrees of freedom these estimators produce, without themin_dfworkaround prolfqua inherited from MSqRob. This slightly changes moderated p-values, FDR and confidence bounds; on the IonStar spike-in benchmark AUC is unchanged, FDR calibration is maintained, and ≥99.7% of significance calls are identical. See theSqueezeVar_comparisonvignette inprolfquabenchmark. (ropeca_nestedis unaffected — it aggregates raw peptide-level p-values and never used variance moderation. A contrast with fewer than three features now returns unmoderated results, matching limma, where the empirical-Bayes prior is undefined.) - Fixed the scale used to moderate
rlmcontrasts.StrategyRLM$sigma()now returns the robust scaleMASS::rlmbuilds its standard errors from (fit$s) instead ofstats::sigma()(the ordinary-residual scale). Because the two differ,ContrastsModerated'ssigma / sqrt(var.post)rescaling was leaving a spurious per-protein factor in moderatedrlmstatistics, p-values, FDR and confidence bounds. Only therlmfacade's moderated output is affected —lmandrfitwere already coherent, andrlm's unmoderated statistics (diff,std.error) were always correct. - Removed the exported
squeezeVarRob()and its internal helpers (fitFDist_LG,fitFDistRobustly_LG); the whole vendoredsqueezeVarRob.Rfile is gone. Uselimma::squeezeVar()instead. DEqMS moderation now callslimma::trigammaInverse()(identical to the copy that was removed). This is a breaking change only for code that calledprolfqua::squeezeVarRob()directly — the ecosystem does not. Also dropped the now-unusedstatmodfromImports. - User-facing errors at prolfqua's public entry points now carry typed condition classes (all inheriting from
prolfqua_error), so callers and tests can catch failures by class instead of matching message text. Covered:setup_analysis(),build_contrast_analysis(), theLFQData$set_data()/set_config_value()mutators,LFQData$get_Aggregator(), the aggregators' "must aggregate a multi-level hierarchy" / "aggregate first" checks,LFQDataTransformer$center_to_reference(), andContrastsSimpleImpute$get_contrasts().LFQData$set_data()andset_config_value()additionally now reject data that no longer contains the columns required by the current configuration, instead of accepting invalid state silently. - Abundance-density plots now carry per-sample Plotly highlight keys, allowing interactive reports to fade non-hovered sample curves while preserving the existing ggplot output.
- Breaking — contrast schema. The
modelNamecolumn ofget_contrasts()output is now the selected facade key (lm,rlm,rfit,lm_impute,lm_missing,limma,limma_impute,limma_voom,limma_voom_impute,limpa,deqms,deqms_voom,firth,lmer_nested,ropeca_nested,firth_nested,limpa_nested) instead of the testing-schema label (WaldTest_moderated,*_DEqMS,*_imputed, …). Rescue/imputation state moved to a newestimate_typecolumn with valuesobserved,lod_imputed, ormissing_fallback. The redundantfacadecolumn was removed (its role is now played bymodelName). The moderated-Wald-test wording belongs in methods text, not per-row labels. Downstream code that filtered onmodelName == "WaldTest_moderated"(or the_imputed/_DEqMSvariants) or read thefacadecolumn must migrate to the facade key andestimate_type. build_contrast_analysis()and the exportedFACADE_REGISTRYnow derive method dispatch and the available-method list from a single seeded registry (list_facades()), removing three hand-maintained copies that could drift. New exported base classContrastsFacadeBaseholds the shared facade plumbing; the 19 built-in facades are now thin subclasses.ContrastsPlottercolours volcano/MA/score plots byestimate_typewhen present (and the colour column was left at its default), using black for observed estimates, green for LOD-imputed estimates, and blue for group-mean fallback estimates.- Correctness fixes:
AggregateTopNnow validatesfuncviamatch.arg(an invalid value errors instead of silently meaningmean);Model$get_anova()drops degenerate rows byis.na(factor)rather than a never-matching"NULL"string;sim_lfq_data()now honours itsmean_protargument;LFQDataTransformer/LFQDataSummariserdeep-clone their input like the other decorators (no more mutating the caller's object);ContrastsFirth$get_linfct()returns the linfct-annotated model copies instead of mutating the shared model;contrasts_fisher_exact()pre-allocatesnrow(x)results;ContrastsMissingnow actually validates its output schema. - Fixed the vectorized contrast path (
options(prolfqua.vectorize = TRUE)) to match the default loop path on rank-deficient (aliased) model fits: a contrast whose weights fall on non-estimable coefficients now returnsNAinstead of a spurious fold-change of 0 with aNaNp-value. The invalid-row guard now counts nonzero weights rather than summing signed weights, so contrasts with canceling+1/-1weights on missing coefficients are correctly flagged. The default (non-vectorized) path was never affected. - The vectorized contrast path now assigns stable
contrast_1,contrast_2, ... labels to unnamed contrasts, matching the documented naming behavior instead of leaking an expression into the result row names. get_contrast()now derivesgroup_1/group_2from the contrast's left/right side expressions, fixing mislabeled per-group columns for averaging contrasts such as(group_A + group_B)/2 - group_Ctrl(previously it used the first two group tokens). SimpleA - Bcontrasts are unaffected. A contrast that is not a differenceLHS - RHSnow errors with a clear message instead of silently extracting the first two group tokens. Nested contrasts that reference an earlier contrast by name (e.g.Interaction = "AvsB_gv_X - AvsB_gv_Z") remain supported in theContrastsMissing/lm_missingpath.setup_analysis()now stops with an informative error (listing the offending keys) when a hierarchy-key/sample combination has more than one observation, instead of silently returning a different-schema count table that crashed downstream. Passdebug = TRUEto recover the old behaviour and return the count table for inspection.- Removed the unused
impute_with_zcomp(),estimate_lod_global(), andfunction_lod_quantile()exports (and thezCompositionsdependency). For missing-value imputation useAggregateLimpa$new(lfqdata, impute_only = TRUE)$aggregate(). - Hardened
plot_pca(): errors early on duplicated sample names, an all-missing matrix, or too few samples instead of returningNULL(which brokepca_plotly()); joins scores to annotation with an explicitby; makesprcomp(center = TRUE, scale. = FALSE)explicit. - Hardened abundance heatmaps for sparse significant-feature subsets: when row or column distances are non-finite because of missing values,
plot_heatmap()now falls back to the input order instead of returning aComplexHeatmapobject that fails during drawing. - Abundance heatmaps now retain every partially observed feature that can be meaningfully row z-scored by default, while automatically removing constant, single-observation, and all-missing rows. Significant-feature reports no longer hide bait-only results or require a dataset-specific missing-value threshold, and sparse matrices render safely even when clustering distances are unavailable.
LFQDataPlotter$heatmap()now shows only thetop_nmost variable features (default 1000), ranked by the prolfqua per-feature statistic (CV for untransformed data, sd for transformed, viaLFQDataStats). Row clustering usesstats::hclust, which errors above 65536 features, so peptide-list / entrapment searches with tens of thousands of degenerate protein groups no longer crash the QC heatmap. Passtop_n = NULL(orInf) to keep every feature.StrategyLogistfnow uses Wald confidence intervals instead of profiling every coefficient, preventingfirth_nestedanalyses from stalling for days on proteins with hundreds or thousands of peptides.
- Added a
check-biocMakefile target and a Bioconductor Docker check image. - Updated BiocCheck-related documentation and vignette metadata.
ContrastsPlotter$volcano()andvolcano_plotly()no longer cap positive FDR/p-value scores at1e-4by default.- Renamed
LFQData$to_wide()todata_wide()and removed deprecated compatibility wrappers. - Split
nr_obs_experiment()intonr_children_experiment()andnr_features_experiment(). - Made
LFQDatadata/config internals private and moved callers to accessor methods. - Refactored transformer and aggregation helpers around current
LFQDataAPIs.