.normalize_median

proteopy.pp.normalize_median(adata, *, log_space=True, target='median', fill_na=None, zero_to_na=False, group_by=None, key_added='normalization_factors', inplace=True, force=False, verbose=False)[source]

Median normalization of intensities.

Each sample is rescaled so that its median intensity matches a common target. Let \(m_s\) be the median over the finite (non-NaN) features of sample \(s\); NaNs are ignored when computing it.

In log space (log_space=True) the rescaling is additive; in linear space (log_space=False) it is multiplicative:

\[X'_{s,i} = X_{s,i} + (t - m_s) \qquad X'_{s,i} = X_{s,i} \cdot \frac{t}{m_s}\]

The target \(t\) is derived from the per-sample medians (within each group when group_by is set):

\[t = \operatorname{median}_s(m_s) \qquad t = \max_s(m_s)\]

for target='median' and target='max' respectively. The zero_to_na and fill_na transforms (mutually exclusive) are applied to .X before normalization and persist in the output. A sample of only NaNs yields \(m_s = \mathrm{NaN}\) and thus a NaN factor; this is not an error and is surfaced through verbose.

Parameters:
  • adata (AnnData) – Input AnnData in proteodata format.

  • log_space (bool) – Whether the input intensities are log-transformed. Mismatches with automatic detection raise unless force=True. Defaults to True.

  • target ({'max', 'median'}) – How to compute the scaling target from the per-sample medians. 'max' uses the maximum sample median, 'median' the median of sample medians. Defaults to 'median'.

  • fill_na (float, optional) – Replace non-finite entries in .X with this value before normalization.

  • zero_to_na (bool, default False) – Treat zeros in .X as missing (NaN) before normalization (replaces zeros with np.nan).

  • group_by (str, optional) – Column in adata.obs defining sample groups; when set, normalization is performed independently within each group (e.g. batch, condition, or any other sample grouping).

  • key_added (str, default 'normalization_factors') – Key of the adata.uns slot in which the per-sample factors DataFrame is stored.

  • inplace (bool, default True) – Modify adata in place. If False, return a copy.

  • force (bool, default False) – Proceed even if log_space disagrees with automatic log detection.

  • verbose (bool, default False) – If True, print the resolved log space, samples whose median is NaN (per group when group_by is set), where the factors are stored (adata.uns[key_added]), and a run summary.

Returns:

  • AnnData or None – Normalized AnnData when inplace is False; otherwise None.

  • pandas.DataFrame, optional – Per-sample factors when inplace is False.

Raises:
  • TypeError – If any argument has an unexpected type, or if .X is sparse.

  • ValueError – If target is invalid, key_added is empty, fill_na is non-finite, fill_na and zero_to_na are both set, group_by contains NaN, log_space disagrees with automatic detection and force=False, a sample median is exactly 0 in linear space (log_space=False), or the normalization produces infinite values.

  • KeyError – If group_by is not a column in adata.obs.

Examples

Build a minimal log-space, protein-level proteodata object:

>>> import numpy as np
>>> import pandas as pd
>>> import anndata as ad
>>> import proteopy as pr
>>> adata = ad.AnnData(
...     X=np.array([[18.0, 20.0, 25.0],
...                 [19.0, 21.0, 22.0],
...                 [16.0, 19.0, 28.0]]),
...     obs=pd.DataFrame({"sample_id": ["S0", "S1", "S2"]},
...                      index=["S0", "S1", "S2"]),
...     var=pd.DataFrame({"protein_id": ["P0", "P1", "P2"]},
...                      index=["P0", "P1", "P2"]),
... )

Normalize using the median of sample medians (defaults), returning a copy together with the per-sample factors:

>>> adata_norm, factors = pr.pp.normalize_median(
...     adata, inplace=False)
>>> adata_norm.X
array([[18., 20., 25.],
       [18., 20., 21.],
       [17., 20., 29.]])

Normalize in place using the maximum of sample medians:

>>> pr.pp.normalize_median(adata, target="max")
>>> adata.X
array([[19., 21., 26.],
       [19., 21., 22.],
       [18., 21., 30.]])