File size: 1,553 Bytes
4e2940e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""Per-layer library-size normalization."""

from __future__ import annotations

import numpy as np
from anndata import AnnData
from scipy.sparse import issparse

from .._constants import UNSPLICED, SPLICED
from .._utils import log_params


def normalize_layers(
    adata: AnnData,
    target_sum: float | None = None,
    layers: tuple[str, ...] = (SPLICED, UNSPLICED),
) -> None:
    """Library-size normalize spliced and unspliced layers independently.

    Each cell's counts in each layer are divided by the cell's total
    counts in that layer and multiplied by *target_sum* (defaults to
    the median library size across cells for that layer).

    Modifies *adata* in place — layers are converted to dense float32.

    Parameters
    ----------
    adata
        Annotated data matrix.
    target_sum
        Target total counts per cell. If ``None``, use the median.
    layers
        Which layers to normalize.
    """
    for layer in layers:
        mat = adata.layers[layer]
        if issparse(mat):
            mat = np.asarray(mat.todense(), dtype=np.float32)
        else:
            mat = np.asarray(mat, dtype=np.float32)

        lib_sizes = mat.sum(axis=1, keepdims=True)
        lib_sizes = np.clip(lib_sizes, 1e-10, None)

        if target_sum is None:
            ts = np.median(lib_sizes)
        else:
            ts = target_sum

        mat = mat / lib_sizes * ts
        adata.layers[layer] = mat

    log_params(adata, "normalize_layers", {
        "target_sum": target_sum,
        "layers": list(layers),
    })