Source code for acore.normalization
"""Module normalization for analysis. This module provides functions to normalize data for analysis.
The higher-level convience functions are `normalize_data` and `normalize_data_by_group`.
Combat normalization was added using the inmoose package.
The actual normalization functions are in strategies.py.
"""
from __future__ import annotations
import pandas as pd
from .strategies import (
linear_normalization,
median_normalization,
median_polish_normalization,
median_zero_normalization,
quantile_normalization,
zscore_normalization,
)
__all__ = ["combat_batch_correction", "normalize_data", "normalize_data_per_group"]
[docs]
def normalize_data_per_group(
data: pd.DataFrame,
group: str | int | list[str | int],
method: str = "median",
normalize: str = None,
) -> pd.DataFrame:
"""
This function normalizes the data by group using the selected method
:param data: DataFrame with the data to be normalized (samples x features)
:param group_col: Column containing the groups, passed to pandas.DataFrame.groupby
:param str method: normalization method to choose among: median_polish, median,
quantile, linear
:param str normalize: whether the normalization should be done by 'features' (columns) or 'samples' (rows) (default None)
:return: pandas.DataFrame.
Example::
result = normalize_data_per_group(data, group='group' method='median')
"""
ndf = pd.DataFrame(columns=data.columns)
for _, gdf in data.groupby(group):
norm_group = normalize_data(gdf, method=method, normalize=normalize)
ndf = ndf.append(norm_group)
return ndf
[docs]
def normalize_data(
data: pd.DataFrame,
method: str = "median",
normalize: str = None,
):
"""
This function normalizes the data using the selected method. Normalizes only nummeric
data, but keeps the non-numeric columns in the output DataFrame.
:param data: DataFrame with the data to be normalized (samples x features)
:param str method: normalization method to choose among: median (default),
median_polish, median_zero, quantile, linear, zscore
:param str normalize: whether the normalization should be done by 'features' (columns)
or 'samples' (rows) (default None)
:return: pandas.DataFrame.
Example::
result = normalize_data(data, method='median_polish')
"""
numeric_cols = data.select_dtypes(
include=["int64", "float64"]
) # ! too restrictive?
non_numeric_cols = data.select_dtypes(
exclude=["int64", "float64"]
) # ! too restrictive?
if numeric_cols.empty:
raise ValueError("No numeric columns found in data.")
if method == "median_polish":
norm_data = median_polish_normalization(numeric_cols, max_iter=250)
elif method == "median_zero":
norm_data = median_zero_normalization(numeric_cols, normalize)
elif method == "median":
norm_data = median_normalization(numeric_cols, normalize)
elif method == "quantile":
norm_data = quantile_normalization(numeric_cols)
elif method == "linear":
norm_data = linear_normalization(numeric_cols, method="l1", normalize=normalize)
elif method == "zscore":
norm_data = zscore_normalization(numeric_cols, normalize)
else:
raise ValueError(
"Invalid normalization method. Should be one of:"
" 'median', 'median_polish', 'median_zero', 'quantile', 'linear', 'zscore'"
)
if non_numeric_cols is not None and not non_numeric_cols.empty:
norm_data = norm_data.join(non_numeric_cols)
return norm_data