Skip to content

Color Space

Color mapping from gene set scores to RGB (pipeline steps 2–3).

Both blend_to_rgb and reduce_to_rgb return an RGBResult object that wraps the RGB array with metadata (method, gene set names, colors). This metadata is auto-detected by the plotting functions.

RGBResult

RGBResult dataclass

RGBResult(
    rgb: NDArray,
    method: str,
    gene_set_names: list[str],
    colors: list[tuple[float, float, float]] | None = None,
    prefix: str = SCORE_PREFIX,
    suffix: str = "",
)

Container for RGB mapping results with metadata.

Returned by :func:blend_to_rgb and :func:reduce_to_rgb. Carries the (n_cells, 3) RGB array together with metadata that downstream plotting functions can use automatically.

The object supports the numpy array protocol, so np.asarray(result) returns the underlying RGB array and indexing/slicing works transparently.

Parameters:

Name Type Description Default
rgb NDArray

(n_cells, 3) RGB array with values in [0, 1].

required
method str

"direct" for multiplicative blend, or the reduction method name ("pca", "nmf", "ica", ...).

required
gene_set_names list[str]

Human-readable gene set labels, derived from score column names.

required
colors list[tuple[float, float, float]] | None

Base colours used for blending (only set for blend_to_rgb).

None
prefix str

Column name prefix used to identify score columns (default "score-").

SCORE_PREFIX
suffix str

Column name suffix stripped from gene set names (default "").

''

Blending (2–3 gene sets)

blend_to_rgb

blend_to_rgb(
    scores: DataFrame,
    *,
    colors: list[tuple[float, float, float]] | None = None,
    prefix: str = SCORE_PREFIX,
    suffix: str = "",
) -> RGBResult

Map gene set scores to RGB via multiplicative blending from white.

Parameters:

Name Type Description Default
scores DataFrame

DataFrame returned by :func:score_gene_sets. Only columns whose names match prefix and suffix are used.

required
colors list[tuple[float, float, float]] | None

One (R, G, B) tuple per gene set. If None, defaults are chosen based on the number of gene sets (2 → blue/red, 3 → RGB).

None
prefix str

Column name prefix identifying score columns (default "score-").

SCORE_PREFIX
suffix str

Column name suffix identifying score columns (default ""). Stripped from gene set names in the result.

''

Returns:

Type Description
RGBResult

Contains the (n_cells, 3) RGB array with values in [0, 1], together with metadata (method="direct", gene set names, colours).

Raises:

Type Description
ValueError

If fewer than 2 or more than 3 gene sets are present, or if the number of supplied colours does not match expectations.

Source code in src/multiscoresplot/_colorspace.py
def blend_to_rgb(
    scores: DataFrame,
    *,
    colors: list[tuple[float, float, float]] | None = None,
    prefix: str = SCORE_PREFIX,
    suffix: str = "",
) -> RGBResult:
    """Map gene set scores to RGB via multiplicative blending from white.

    Parameters
    ----------
    scores
        DataFrame returned by :func:`score_gene_sets`.  Only columns whose
        names match *prefix* and *suffix* are used.
    colors
        One ``(R, G, B)`` tuple per gene set.  If *None*, defaults are chosen
        based on the number of gene sets (2 → blue/red, 3 → RGB).
    prefix
        Column name prefix identifying score columns (default ``"score-"``).
    suffix
        Column name suffix identifying score columns (default ``""``).
        Stripped from gene set names in the result.

    Returns
    -------
    RGBResult
        Contains the ``(n_cells, 3)`` RGB array with values in [0, 1],
        together with metadata (``method="direct"``, gene set names, colours).

    Raises
    ------
    ValueError
        If fewer than 2 or more than 3 gene sets are present, or if the
        number of supplied colours does not match expectations.
    """
    score_cols = _validate_score_columns(scores, prefix=prefix, suffix=suffix)
    n_sets = len(score_cols)

    if n_sets < 2:
        raise ValueError("At least 2 gene sets are required.")
    if n_sets > 3:
        raise ValueError(
            f"Direct projection supports at most 3 gene sets (got {n_sets}). "
            "Use reduce_to_rgb() for higher dimensions."
        )

    mat = scores[score_cols].to_numpy(dtype=np.float64)

    default = DEFAULT_COLORS_2 if n_sets == 2 else DEFAULT_COLORS_3
    if colors is None:
        colors = default
    if len(colors) != n_sets:
        raise ValueError(f"Expected {n_sets} colors for {n_sets} gene sets, got {len(colors)}.")

    gene_set_names = _extract_gene_set_names(score_cols, prefix, suffix)

    return RGBResult(
        rgb=_multiplicative_blend(mat, colors),
        method="direct",
        gene_set_names=gene_set_names,
        colors=colors,
        prefix=prefix,
        suffix=suffix,
    )

Dimensionality Reduction (2+ gene sets)

reduce_to_rgb

reduce_to_rgb(
    scores: DataFrame,
    *,
    method: str | Callable[..., NDArray] = "pca",
    n_components: int = 3,
    component_prefix: str | None = None,
    prefix: str = SCORE_PREFIX,
    suffix: str = "",
    **kwargs: object,
) -> RGBResult

Map gene set scores to RGB via dimensionality reduction.

Parameters:

Name Type Description Default
scores DataFrame

DataFrame returned by :func:score_gene_sets.

required
method str | Callable[..., NDArray]

Reduction method. Can be a string ("pca", "nmf", "ica", or any method registered via :func:register_reducer) or a callable with signature (X, n_components, **kwargs) -> NDArray returning an (n_cells, 3) array with values in [0, 1].

'pca'
n_components int

Number of components to retain (max 3 for RGB).

3
component_prefix str | None

Label prefix for legend axes (e.g. "MY" → MY1, MY2, MY3). Overrides the default prefix for the method.

None
prefix str

Column name prefix identifying score columns (default "score-").

SCORE_PREFIX
suffix str

Column name suffix identifying score columns (default ""). Stripped from gene set names in the result.

''
**kwargs object

Extra keyword arguments forwarded to the reducer function.

{}

Returns:

Type Description
RGBResult

Contains the (n_cells, 3) RGB array with values in [0, 1], together with metadata (method name, gene set names).

Raises:

Type Description
ValueError

If fewer than 2 gene sets are present or method is an unknown string.

TypeError

If method is neither a string nor a callable.

Notes

For one-off custom reductions, passing a callable directly is more convenient than :func:register_reducer. For reusable methods that should appear in error messages and be available by name, use :func:register_reducer instead.

Color interpretation caveat: Reduction methods project the full score matrix into just 3 dimensions. Only the top 3 axes of variation are captured — all other differences are collapsed. Two cells with very different gene set score profiles can appear the same color if their differences lie along dropped components. Additionally, the R, G, and B channels represent learned linear combinations of all gene set scores, not individual gene sets. For more interpretable components use method="nmf" or :func:blend_to_rgb for ≤ 3 gene sets.

Source code in src/multiscoresplot/_colorspace.py
def reduce_to_rgb(
    scores: DataFrame,
    *,
    method: str | Callable[..., NDArray] = "pca",
    n_components: int = 3,
    component_prefix: str | None = None,
    prefix: str = SCORE_PREFIX,
    suffix: str = "",
    **kwargs: object,
) -> RGBResult:
    """Map gene set scores to RGB via dimensionality reduction.

    Parameters
    ----------
    scores
        DataFrame returned by :func:`score_gene_sets`.
    method
        Reduction method. Can be a string (``"pca"``, ``"nmf"``, ``"ica"``,
        or any method registered via :func:`register_reducer`) or a callable
        with signature ``(X, n_components, **kwargs) -> NDArray`` returning an
        ``(n_cells, 3)`` array with values in [0, 1].
    n_components
        Number of components to retain (max 3 for RGB).
    component_prefix
        Label prefix for legend axes (e.g. ``"MY"`` → MY1, MY2, MY3).
        Overrides the default prefix for the method.
    prefix
        Column name prefix identifying score columns (default ``"score-"``).
    suffix
        Column name suffix identifying score columns (default ``""``).
        Stripped from gene set names in the result.
    **kwargs
        Extra keyword arguments forwarded to the reducer function.

    Returns
    -------
    RGBResult
        Contains the ``(n_cells, 3)`` RGB array with values in [0, 1],
        together with metadata (method name, gene set names).

    Raises
    ------
    ValueError
        If fewer than 2 gene sets are present or *method* is an unknown string.
    TypeError
        If *method* is neither a string nor a callable.

    Notes
    -----
    For one-off custom reductions, passing a callable directly is more
    convenient than :func:`register_reducer`.  For reusable methods that
    should appear in error messages and be available by name, use
    :func:`register_reducer` instead.

    **Color interpretation caveat:** Reduction methods project the full score
    matrix into just 3 dimensions.  Only the top 3 axes of variation are
    captured — all other differences are collapsed.  Two cells with very
    different gene set score profiles can appear the same color if their
    differences lie along dropped components.  Additionally, the R, G, and B
    channels represent learned linear combinations of all gene set scores, not
    individual gene sets.  For more interpretable components use
    ``method="nmf"`` or :func:`blend_to_rgb` for ≤ 3 gene sets.
    """
    if callable(method):
        reducer_fn = method
        method_name = getattr(method, "__name__", "custom")
        if component_prefix is not None:
            _COMPONENT_PREFIXES[method_name] = component_prefix
    elif isinstance(method, str):
        if method not in _REDUCERS:
            available = ", ".join(sorted(_REDUCERS))
            raise ValueError(
                f"Unknown reduction method {method!r}. "
                f"Available: {available}. You can also pass a callable directly."
            )
        reducer_fn = _REDUCERS[method]
        method_name = method
        if component_prefix is not None:
            _COMPONENT_PREFIXES[method_name] = component_prefix
    else:
        raise TypeError(f"method must be a string or callable, got {type(method).__name__}")

    score_cols = _validate_score_columns(scores, prefix=prefix, suffix=suffix)
    if len(score_cols) < 2:
        raise ValueError("At least 2 gene sets are required.")

    mat = scores[score_cols].to_numpy(dtype=np.float64)
    k = min(n_components, 3)

    gene_set_names = _extract_gene_set_names(score_cols, prefix, suffix)

    return RGBResult(
        rgb=reducer_fn(mat, k, **kwargs),
        method=method_name,
        gene_set_names=gene_set_names,
        prefix=prefix,
        suffix=suffix,
    )

Custom Reducers

register_reducer

register_reducer(
    name: str,
    fn: Callable[..., NDArray],
    *,
    component_prefix: str | None = None,
) -> None

Register a dimensionality reduction method for use with reduce_to_rgb.

Parameters:

Name Type Description Default
name str

Short identifier (e.g. "pca", "nmf").

required
fn Callable[..., NDArray]

Callable with signature (X, n_components, **kwargs) -> NDArray returning an (n_cells, 3) array with values in [0, 1].

required
component_prefix str | None

Label prefix for legend axes (e.g. "PC" → PC1, PC2, PC3).

None
Source code in src/multiscoresplot/_colorspace.py
def register_reducer(
    name: str,
    fn: Callable[..., NDArray],
    *,
    component_prefix: str | None = None,
) -> None:
    """Register a dimensionality reduction method for use with ``reduce_to_rgb``.

    Parameters
    ----------
    name
        Short identifier (e.g. ``"pca"``, ``"nmf"``).
    fn
        Callable with signature ``(X, n_components, **kwargs) -> NDArray``
        returning an ``(n_cells, 3)`` array with values in [0, 1].
    component_prefix
        Label prefix for legend axes (e.g. ``"PC"`` → PC1, PC2, PC3).
    """
    _REDUCERS[name] = fn
    if component_prefix is not None:
        _COMPONENT_PREFIXES[name] = component_prefix

Utility

get_component_labels

get_component_labels(method: str) -> list[str]

Return ["<prefix>1", "<prefix>2", "<prefix>3"] for a registered method.

Source code in src/multiscoresplot/_colorspace.py
def get_component_labels(method: str) -> list[str]:
    """Return ``["<prefix>1", "<prefix>2", "<prefix>3"]`` for a registered method."""
    prefix = _COMPONENT_PREFIXES.get(method, "C")
    return [f"{prefix}{i + 1}" for i in range(3)]