Repository navigation
v0.4.0
Added
normalise(method="pairwise_median")— between-sample normalisation that aligns
samples on the features they observe in common: the median log2 difference over each pair's
co-observed features, combined into zero-sum per-sample shifts. Sparse-native; a block whose
samples share no feature raises aValueErrornaming the sample pairs. Existing methods are
unchanged.adata.uns["normalisation"]— everynormalisecall now records, per sample and block, the
observed feature count, the applied shift and the observed-value median before and after; for
pairwise_medianalso the pairwise median and shared-count matrices.- Structured provenance and replay (
mm.pv). Supported calls now record parameters, input
lineage, execution environment and optional content hashes (enabled by default) in
mdata.uns["_log"].get_loginspects the history,replayverifies supported file-backed
workflows, andto_scriptandto_envexport scripts and environment version specifications. - Recordable data operations (
mm.dt).assign,map,replaceanddroprecord
table and metadata edits that can be replayed.concatcombines two or more sample-aligned
MuDatainputs while retaining each input's provenance. - Explicit filter names.
add_filter(name=...)stores a stable name for a filter condition
and rejects reuse of that name for a different condition.
Changed
- Provenance history uses
mdata.uns["_log"]instead of_cmd. Breaking for consumers
of the old history format: existing_cmdrecords are not migrated or replayed bymm.pv. - Content hashes follow the fixed
msmu-v1policy. Floating-point values use 12
significant digits; file bytes, axes and non-floating values are hashed exactly. The public
option controls whether hashing is enabled, not the precision policy. mm.merge_mudatadelegates tomm.dt.concatand warns on use. The concat contract
requires at least two sample-aligned inputs; calls that previously passed one input need updating.mm.io.write_pinnow requires a filename. Breaking for callers that omitted it to
receive an in-memory DataFrame; the function writes a PIN file instead.- Automatic matrix-filter names include their source and key. Breaking for workflows
that select filters by the old generated names; use the new names or set an explicitname. - Documentation and validation were rebuilt. Public API reference links, installation,
tutorials and How it works guides now match the current package. PR CI checks a strict MkDocs
build and executes Quick Start; other notebooks render from saved outputs. - A multiply modified peptidoform quantifies its site combination, not each site.
to_ptm
gave a doubly phosphorylated peptide's value to both of its sites, so a site pooled singly and
multiply modified peptidoforms and one measurement reached several sites. A multiply modified
peptide's change cannot be attributed to one of its sites — the same problem a shared peptide
poses in protein inference, and the same answer: it is reported as the group it belongs to.
Breaking: by default (multisite="combination") such a peptidoform becomes one feature named
for its site set,"P1|S12_S25", and no longer contributes to"P1|S12"or"P1|S25". Every
peptidoform then feeds exactly one feature; peptidoforms differing only in other modifications or
missed cleavages still share one.var["count_site"]says how many sites a feature names, so
count_site == 1selects the site table built from singly modified peptidoforms alone — what
site-centric tools such as PTM-SEA read.multisite="pool"restores the previous behaviour. On a
real TMTpro phospho run, two singly phosphorylated forms of one site agreed on the treatment
effect (r = 0.80) where a single and a multiple form did not (r = 0.36), and 6.6% of pooled sites
carried values identical to another site's. This is the convention of Spectrum Mill's phosphosite
tables (CPTAC), TMT-Integrator's multi-site report and MSstatsPTM. adjust_ptm_by_proteinaccepts the global dataset as an.h5mupath. Passed as aMuData,
the global container's own history merges into the result, andmm.pv.replay/mm.pv.to_script
accept a secondMuDatainput only forconcat— so a PTM workflow could be reproduced only up
to the adjustment. Passed as a path (global_mdata=Path("global.h5mu")), the file is recorded as an
input with its content hash, like a reader's source file, and the whole workflow replays. Replay
needs the original files either way, so this adds no requirement. Pass aPathrather than a
strfor the content hash: provenance treats a string as a file only when the parameter's name
says so.- PTM adjustment finds its denominator by accession instead of by peptide. A site's parent
protein is now located by translating the accessions it was localised on through the global
dataset'suns["protein_map"], rather than by looking the PTM peptide up in the global peptide
map. Under enrichment a phospho peptide is routinely absent from the global run while its protein
is quantified there from other peptides, so the old lookup failed on the normal case — and failed
loudly, aborting the whole run. The PTM container no longer needsinfer_proteinat all; see
Removed forpropagated_from. - PTM sites are localised from the peptide's own accessions.
to_ptmreadsproteinsinstead
of an inferredprotein_group, so site ids no longer depend on which global dataset the PTM data
happened to be processed alongside. Breaking: site ids are now flat —"P1|S30;P2|S30;P3|S45"
where they previously nested a comma tier inside the semicolons — and, because the search engine's
own accession list is used, a site may list accessions that the global parsimony had removed.
Accessions are sorted, so a site's id no longer depends on the order the engine listed them in. adjust_ptm_by_proteindefaults tomethod="ratio". The previous default,ridge, both
discarded every site with two or fewer paired observations and shrank its slope by
Sxx / (Sxx + alpha); with the hardcodedalpha=100and proteomics-scale variance that left a
few percent of the slope, so the residual reduced to centring each site and no protein correction
happened.ridgeremains available andalphais now reachable asridge_alpha.- Adjustment no longer drops the sites it could not adjust. The adjusted values replace the
matrix that was read —.X, orlayers[layer]when given — matchinglog2_transform,
normaliseandcorrect_batch_effect, so tools that default to.Xsee the adjusted data. A
site without a valid denominator is set toNaNrather than left holding its raw abundance, so
residuals and raw abundances never share a matrix. Every site gainsvar["adjustment_status"],
var["denominator_group"]andvar["is_protein_adjusted"], and the per-reason counts are logged
— including a distinct status for accessions missing from the global FASTA, which means the two
searches used different databases rather than that the protein went undetected. to_ptm'smodificationis matched as a whole tag and accepts several. It is the tag exactly
as written inpeptide("[+79.9663]","(UniMod:21)"), optionally qualified by its residue
("S[167]"), or a list of them summarised into one modality — FragPipe writes pS, pT and pY as
three different residue masses. Breaking: matching is by equality with a whole tag, not by
substring, so a fragment such as"79.97"is rejected. A modification that matches nothing now
raises an error listing the tags the data does contain, instead of an unrelated pandas error.to_ptmreports the peptide–protein matches the attached FASTA cannot reproduce. An accession
the FASTA does not hold, or whose sequence does not contain the peptide, is dropped from the site
— silently until now, although it both loses sites and can turn a site whose accessions should
span two protein groups into an adjusted one. The counts and examples are now aWARNING, with
how many modified peptidoforms produced no site at all; missing contaminant accessions (Cont_)
are reported atINFO, since search engines add contaminant entries a user FASTA routinely lacks.to_ptmdefaults toagg_method="median_polish". It models a per-peptidoform effect, so a
site's value no longer moves when the set of peptidoforms supporting it changes between samples;
for a site backed by a single peptidoform it is identical tomedian. Linear-looking input is
warned about, since the additive rollups require log space.
Fixed
- The wheel contains only the
msmupackage. Package discovery no longer includes the
repository's data, documentation, tests or local scratch files in the installable wheel. - Tied PEPs receive the same target-decoy q-value. Q-values are calculated at PEP-group
boundaries, so equal-PEP rows no longer depend on their input order. Existing q-values and
membership at a q-value cutoff can change. - Missing sample-group labels are rejected before normalisation.
normalise(group_obs=...)no longer silently processes incomplete groups. - Filter masks and decoys stay consistent. Missing source values do not pass comparisons;
unknown axes, missing requested filters and absent decoy conditions fail explicitly instead of
partially filtering the data. apply_sdrf_to_obs(set_index=...)carries the obs rename intoobsm. The obs filter that
add_filterrecords inobsm["filter"]kept the old observation labels after the rename, and the
next copy of the modality failed with "value.index does not match parent's obs names". That is
the ordinary TMT order — blank channels have no sample name to index by, so they are filtered out
first and the observations renamed afterwards — so every TMT run that named its samples hit it.- PTM site positions no longer count the letters inside modification tags.
to_ptmlocated a
site by counting every letter before the modification, so each tag with letters in it —
DIA-NN's(UniMod:4), MaxQuant's(Oxidation (M))— pushed every later site further along the
protein. Positions were right only for Sage's mass notation. Oxidised and unoxidised forms of one
phosphopeptide split into two sites, and two genuinely different sites could merge into one.
Peptides are now parsed into residues and tags, and each parse is checked against
stripped_peptide; a notation msmu cannot read raises instead of producing misplaced sites. A
target modification on the peptide N-terminus, which crashed withIndexError, is now placed on
residue 1. modified_proteinkeeps accessions that contain|. It was cut from the site label at the
first|, so a GENCODE or NCBIgi|…accession was truncated and its sites could never find a
denominator inadjust_ptm_by_protein, which resolves from this column.- PSM counts are no longer multiplied by a site's accession count.
to_ptmsummedcount_psm
after exploding each peptidoform over its accessions, inflating the count by that many times. - Matrix rollups no longer try to aggregate the grouping column.
median_polishanddirectlfq
received the PTM path'sprotein_sitecolumn alongside the sample columns and failed on it. normalisedocumentation — the method list now includesmedian_center, and the
how-it-works page no longer claimsmethoddefaults to"median"(it is required).
Removed
msmu.setup_loggeris no longer exported from the package root. Breaking for callers
importing it directly; default import-time console logging remains configured internally.infer_protein(propagated_from=...). It copied another dataset'speptide_mapand
protein_maponto the container, and raised if any peptide was missing from the copied map — so it
only ever ran when the target held no peptide the source lacked, which in practice meant the same
data. Its one documented use, carrying a global dataset's grouping onto PTM data, is gone: PTM
denominators are now resolved from the globalprotein_mapdirectly. Breaking: passing
propagated_fromraisesTypeError; drop the argument and pass the globalMuDatato
adjust_ptm_by_proteininstead.
Notes
- Sites whose accessions span two or more quantified global protein groups are reported but left
unadjusted. Their measured signal is a sum over those groups, and protein rollup values carry a
per-protein offset that makes them incomparable across groups, so neither picking one group nor
summing them yields a valid denominator. Accessions that the global dataset could not tell apart
are a single group and remain adjustable.
Merged pull requests
What's Changed
- BID-264 Upgrade artifact actions to Node 24 runtimes by @jlthepi in #35
- BID-289 Add structured provenance and verified workflow replay by @jlthepi in #36
- BID-313 Compute q-values by tied PEP groups by @jlthepi in #37
- BID-315 Support provenance-aware MuData concat and verified replay by @jlthepi in #38
- BID-319 Reject missing batch groups before normalisation by @jlthepi in #39
- BID-317 Fix filter consistency and support explicit names by @jlthepi in #40
- BID-320 Simplify hashing with the msmu-v1 policy by @jlthepi in #41
- BID-268 Resolve PTM denominators by accession and build sites by site combination by @hyungwookc07 in #42
- BID-316 Add pairwise_median normalisation and record what normalise did by @hyungwookc07 in #43
- BID-318 Fix MSMU documentation build and align examples with the public API by @jlthepi in #44
- BID-328 Release MSMU 0.4.0 by @jlthepi in #45
- BID-328 Release MSMU 0.4.0 by @jlthepi in #46
Full Changelog: v0.3.2...v0.4.0