Skip to content

Statistical

Fill each column from a summary statistic of its own observed values.

Univariate statistical imputers.

Each column is filled independently from a summary statistic of its own observed values (mean, median, mode, quantile, ...).

MeanImputer

Bases: BaseImputer

Impute missing values using column means.

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import MeanImputer
>>> df = pd.DataFrame({"a": [1, 2, np.nan, 4]})
>>> imputer = MeanImputer()
>>> imputed = imputer.impute(df)
>>> print(imputed.loc[2, "a"])  # Mean of [1, 2, 4]
2.333...

impute

impute(df: DataFrame) -> DataFrame

Fill each column's missing values with that column's mean.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with potential NaN values.

required

Returns:

Type Description
DataFrame

Imputed dataframe. Columns with no observed values stay NaN.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Fill each column's missing values with that column's mean.

    Args:
        df: Dataframe with potential NaN values.

    Returns:
        Imputed dataframe. Columns with no observed values stay NaN.
    """
    df = self._ensure_numeric(df)
    result = df.copy()
    for column in result.columns:
        mean_val = result[column].mean()
        result[column] = result[column].fillna(mean_val)

    return result

MedianImputer

Bases: BaseImputer

Impute missing values using column medians.

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import MedianImputer
>>> df = pd.DataFrame({"a": [1, 2, np.nan, 10]})
>>> imputer = MedianImputer()
>>> imputed = imputer.impute(df)
>>> print(imputed.loc[2, "a"])  # Median of [1, 2, 10]
2.0

impute

impute(df: DataFrame) -> DataFrame

Fill each column's missing values with that column's median.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with potential NaN values.

required

Returns:

Type Description
DataFrame

Imputed dataframe. Columns with no observed values stay NaN.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Fill each column's missing values with that column's median.

    Args:
        df: Dataframe with potential NaN values.

    Returns:
        Imputed dataframe. Columns with no observed values stay NaN.
    """
    df = self._ensure_numeric(df)
    result = df.copy()
    for column in result.columns:
        median_val = observed_median(result[column])
        result[column] = result[column].fillna(median_val)
    return result

ModeImputer

ModeImputer(dropna: bool = True)

Bases: BaseImputer

Impute missing values with the mode (most frequent value).

Particularly useful for categorical data or discrete numeric data. For continuous data with no repeated values, falls back to median.

Parameters:

Name Type Description Default
dropna bool

Whether to exclude NaN values when computing mode. Default: True

True

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import ModeImputer
>>> df = pd.DataFrame({'a': [1, 2, 2, np.nan, 2, 3]})
>>> imputer = ModeImputer()
>>> imputed = imputer.impute(df)
>>> # Missing value filled with 2 (most frequent)
References

Standard statistical technique for categorical/discrete data.

Initialize the mode imputer.

Parameters:

Name Type Description Default
dropna bool

Whether to exclude NaN values when computing mode

True
Source code in src/imputation_methods/statistical.py
def __init__(self, dropna: bool = True) -> None:
    """Initialize the mode imputer.

    Args:
        dropna: Whether to exclude NaN values when computing mode
    """
    self.dropna = dropna

impute

impute(df: DataFrame) -> DataFrame

Impute using the mode of each column.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute using the mode of each column.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    for column in result.columns:
        if result[column].isna().any():
            # Get mode (most frequent value)
            mode_values = result[column].mode(dropna=self.dropna)

            if len(mode_values) > 0:
                # If multiple modes exist, take the first one
                fill_value = mode_values[0]
            else:
                # Fallback to median if no mode found
                fill_value = observed_median(result[column])

            result[column] = result[column].fillna(fill_value)

    return result

ConstantImputer

ConstantImputer(fill_value: float | dict[str, float] = 0)

Bases: BaseImputer

Impute missing values with a user-specified constant.

Allows different constants for different columns or a single constant for all columns. Useful for domain-specific imputation strategies.

Parameters:

Name Type Description Default
fill_value float | dict[str, float]

Constant value(s) to use for imputation. Can be: - A scalar (applied to all columns) - A dict mapping column names to fill values Default: 0

0

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import ConstantImputer
>>> df = pd.DataFrame({'a': [1, np.nan, 3], 'b': [np.nan, 2, 3]})
>>> # Single value for all columns
>>> imputer = ConstantImputer(fill_value=-999)
>>> imputed = imputer.impute(df)
>>>
>>> # Different values per column
>>> imputer = ConstantImputer(fill_value={'a': 0, 'b': 100})
>>> imputed = imputer.impute(df)
References

Common practice in many domains (e.g., -999 for missing sensor data).

Initialize the constant imputer.

Parameters:

Name Type Description Default
fill_value float | dict[str, float]

Constant value(s) for imputation

0
Source code in src/imputation_methods/statistical.py
def __init__(self, fill_value: float | dict[str, float] = 0) -> None:
    """Initialize the constant imputer.

    Args:
        fill_value: Constant value(s) for imputation
    """
    self.fill_value = fill_value

impute

impute(df: DataFrame) -> DataFrame

Impute using constant value(s).

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Raises:

Type Description
ValueError

If fill_value dict contains unknown column names

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute using constant value(s).

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.

    Raises:
        ValueError: If fill_value dict contains unknown column names
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    if isinstance(self.fill_value, dict):
        # Check that all keys in fill_value are valid column names
        unknown_cols = set(self.fill_value.keys()) - set(df.columns)
        if unknown_cols:
            raise ValueError(f"fill_value contains unknown columns: {unknown_cols}")

        # Fill each column with its specific value
        for column, value in self.fill_value.items():
            if column in result.columns and result[column].isna().any():
                result[column] = result[column].fillna(value)
    else:
        # Fill all columns with the same value
        result = result.fillna(self.fill_value)

    return result

QuantileImputer

QuantileImputer(quantile: float = 0.5)

Bases: BaseImputer

Impute using specified quantile of observed values.

Parameters:

Name Type Description Default
quantile float

Quantile to use (0.0 to 1.0). Default: 0.5 (median)

0.5

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import QuantileImputer
>>> df = pd.DataFrame({'a': [1, 2, np.nan, 4, 5]})
>>> # Use 75th percentile
>>> imputer = QuantileImputer(quantile=0.75)
>>> imputed = imputer.impute(df)

Initialize the quantile imputer.

Parameters:

Name Type Description Default
quantile float

Quantile value (0.0 to 1.0)

0.5

Raises:

Type Description
ValueError

If quantile is not between 0 and 1

Source code in src/imputation_methods/statistical.py
def __init__(self, quantile: float = 0.5) -> None:
    """Initialize the quantile imputer.

    Args:
        quantile: Quantile value (0.0 to 1.0)

    Raises:
        ValueError: If quantile is not between 0 and 1
    """
    if not 0 <= quantile <= 1:
        raise ValueError(f"quantile must be between 0 and 1, got {quantile}")

    self.quantile = quantile

impute

impute(df: DataFrame) -> DataFrame

Impute using specified quantile.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute using specified quantile.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    for column in result.columns:
        if result[column].isna().any():
            quantile_value = result[column].quantile(self.quantile)
            result[column] = result[column].fillna(quantile_value)

    return result

TrimmedMeanImputer

TrimmedMeanImputer(trim_fraction: float = 0.1)

Bases: BaseImputer

Trimmed mean imputation excluding extreme values.

Computes mean after removing a percentage of extreme values from both ends. More robust than simple mean.

Parameters:

Name Type Description Default
trim_fraction float

Fraction to trim from each end (0-0.5). Default: 0.1

0.1

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import TrimmedMeanImputer
>>> df = pd.DataFrame({'a': [1, 2, np.nan, 4, 100]})  # 100 is outlier
>>> imputer = TrimmedMeanImputer(trim_fraction=0.2)
>>> imputed = imputer.impute(df)
>>> # Excludes 100 from mean calculation
References

Robust statistics using trimmed estimators.

Initialize the trimmed mean imputer.

Parameters:

Name Type Description Default
trim_fraction float

Fraction to trim (0-0.5)

0.1

Raises:

Type Description
ValueError

If trim_fraction not in [0, 0.5]

Source code in src/imputation_methods/statistical.py
def __init__(self, trim_fraction: float = 0.1) -> None:
    """Initialize the trimmed mean imputer.

    Args:
        trim_fraction: Fraction to trim (0-0.5)

    Raises:
        ValueError: If trim_fraction not in [0, 0.5]
    """
    if not (0 <= trim_fraction < 0.5):
        raise ValueError(f"trim_fraction must be in [0, 0.5), got {trim_fraction}")

    self.trim_fraction = trim_fraction

impute

impute(df: DataFrame) -> DataFrame

Impute using trimmed mean.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute using trimmed mean.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    for column in result.columns:
        observed = result[column].dropna()
        if result[column].isna().any() and not observed.empty:
            trimmed_mean = stats.trim_mean(observed, self.trim_fraction)
            result[column] = result[column].fillna(trimmed_mean)

    return result

EndOfDistributionImputer

EndOfDistributionImputer(position: str = 'high', n_std: float = 3.0)

Bases: BaseImputer

Impute at the edges of the distribution (mean ± n_std*std).

Useful for flagging or handling extreme/suspicious values. Can impute at low end (mean - n_stdstd) or high end (mean + n_stdstd).

Parameters:

Name Type Description Default
position str

Where to impute ('low' or 'high'). Default: 'high'

'high'
n_std float

Number of standard deviations from mean. Default: 3.0

3.0

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import EndOfDistributionImputer
>>> df = pd.DataFrame({'a': [1, 2, 3, np.nan, 5]})
>>> imputer = EndOfDistributionImputer(position='high', n_std=2)
>>> imputed = imputer.impute(df)
>>> # Missing value filled with mean + 2*std
References

Used in outlier detection and robust imputation strategies.

Initialize the end-of-distribution imputer.

Parameters:

Name Type Description Default
position str

'low' (mean - n_stdstd) or 'high' (mean + n_stdstd)

'high'
n_std float

Number of standard deviations

3.0

Raises:

Type Description
ValueError

If position is not 'low' or 'high'

Source code in src/imputation_methods/statistical.py
@renamed_parameters(k="n_std")
def __init__(self, position: str = "high", n_std: float = 3.0) -> None:
    """Initialize the end-of-distribution imputer.

    Args:
        position: 'low' (mean - n_std*std) or 'high' (mean + n_std*std)
        n_std: Number of standard deviations

    Raises:
        ValueError: If position is not 'low' or 'high'
    """
    if position not in ["low", "high"]:
        raise ValueError(f"position must be 'low' or 'high', got {position}")

    self.position = position
    self.n_std = n_std

impute

impute(df: DataFrame) -> DataFrame

Impute at distribution edges.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute at distribution edges.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    for column in result.columns:
        if result[column].isna().any():
            mean = result[column].mean()
            std = result[column].std()

            if self.position == "low":
                fill_value = mean - self.n_std * std
            else:  # high
                fill_value = mean + self.n_std * std

            result[column] = result[column].fillna(fill_value)

    return result

GroupMeanImputer

GroupMeanImputer(group_col: str, strategy: str = 'mean', global_fallback: bool = True)

Bases: BaseImputer

Group-wise mean or median imputation.

Imputes missing values using statistics computed within groups. Useful for panel data, time series with categories, etc.

Parameters:

Name Type Description Default
group_col str

Column name to group by (must be in the dataframe)

required
strategy str

Aggregation strategy ('mean' or 'median'). Default: 'mean'

'mean'
global_fallback bool

Use global statistic if group has no data. Default: True

True

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import GroupMeanImputer
>>> df = pd.DataFrame({
...     'category': [1, 1, 2, 2, 1],
...     'value': [10, np.nan, 20, np.nan, 12]
... })
>>> imputer = GroupMeanImputer(group_col='category', strategy='mean')
>>> imputed = imputer.impute(df)
>>> # Row 1 filled with mean of category 1, row 3 with mean of category 2
References

Common in hierarchical data and panel data analysis.

Initialize the group mean imputer.

Parameters:

Name Type Description Default
group_col str

Column name to group by

required
strategy str

'mean' or 'median'

'mean'
global_fallback bool

Use global statistic for groups with no data

True

Raises:

Type Description
ValueError

If strategy is not 'mean' or 'median'

Source code in src/imputation_methods/statistical.py
@renamed_parameters(method="strategy")
def __init__(
    self, group_col: str, strategy: str = "mean", global_fallback: bool = True
) -> None:
    """Initialize the group mean imputer.

    Args:
        group_col: Column name to group by
        strategy: 'mean' or 'median'
        global_fallback: Use global statistic for groups with no data

    Raises:
        ValueError: If strategy is not 'mean' or 'median'
    """
    if strategy not in ["mean", "median"]:
        raise ValueError(f"strategy must be 'mean' or 'median', got {strategy}")

    self.group_col = group_col
    self.strategy = strategy
    self.global_fallback = global_fallback

impute

impute(df: DataFrame) -> DataFrame

Impute using group-wise statistics.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe.

Raises:

Type Description
ValueError

If group_col is not in dataframe columns

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute using group-wise statistics.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe.

    Raises:
        ValueError: If group_col is not in dataframe columns
    """
    if self.group_col not in df.columns:
        raise ValueError(
            f"group_col '{self.group_col}' not found in dataframe columns"
        )

    result = df.copy()

    # Get numeric columns (excluding group column)
    numeric_cols = [
        col
        for col in result.columns
        if col != self.group_col and pd.api.types.is_numeric_dtype(result[col])
    ]

    for column in numeric_cols:
        if result[column].isna().any():
            grouped = result.groupby(self.group_col)[column]
            group_stats = (
                grouped.transform("mean")
                if self.strategy == "mean"
                else grouped.transform("median")
            )
            result[column] = result[column].fillna(group_stats)

            # Handle any remaining NaNs with global fallback
            if self.global_fallback and result[column].isna().any():
                if self.strategy == "mean":
                    global_stat = df[column].mean()
                else:  # median
                    global_stat = observed_median(df[column])

                result[column] = result[column].fillna(global_stat)

    return result

IndicatorImputer

IndicatorImputer(strategy: str = 'mean', indicator_prefix: str = 'missing_')

Bases: BaseImputer

Impute and add binary indicator columns for missingness.

Creates indicator columns showing which values were missing, then imputes the original columns. Useful when missingness itself is informative.

Parameters:

Name Type Description Default
strategy str

Imputation strategy for values ('mean', 'median', 'zero'). Default: 'mean'

'mean'
indicator_prefix str

Prefix for indicator column names. Default: 'missing_'

'missing_'

Examples:

>>> import pandas as pd
>>> import numpy as np
>>> from imputation_methods import IndicatorImputer
>>> df = pd.DataFrame({'a': [1, 2, np.nan, 4], 'b': [5, np.nan, 7, 8]})
>>> imputer = IndicatorImputer(strategy='mean')
>>> imputed = imputer.impute(df)
>>> print(imputed.columns.tolist())
['a', 'b', 'missing_a', 'missing_b']

Initialize the indicator imputer.

Parameters:

Name Type Description Default
strategy str

Imputation strategy

'mean'
indicator_prefix str

Prefix for indicator columns

'missing_'
Source code in src/imputation_methods/statistical.py
def __init__(
    self, strategy: str = "mean", indicator_prefix: str = "missing_"
) -> None:
    """Initialize the indicator imputer.

    Args:
        strategy: Imputation strategy
        indicator_prefix: Prefix for indicator columns
    """
    if strategy not in ["mean", "median", "zero"]:
        raise ValueError(
            f"strategy must be 'mean', 'median', or 'zero', got {strategy}"
        )

    self.strategy = strategy
    self.indicator_prefix = indicator_prefix

impute

impute(df: DataFrame) -> DataFrame

Impute and add indicator columns.

Parameters:

Name Type Description Default
df DataFrame

Dataframe with missing values.

required

Returns:

Type Description
DataFrame

Imputed dataframe with additional indicator columns.

Source code in src/imputation_methods/statistical.py
@preserve_dtypes
def impute(self, df: pd.DataFrame) -> pd.DataFrame:
    """Impute and add indicator columns.

    Args:
        df: Dataframe with missing values.

    Returns:
        Imputed dataframe with additional indicator columns.
    """
    df = self._ensure_numeric(df)
    result = df.copy()

    # Add indicator columns for missingness
    for column in df.columns:
        indicator_name = f"{self.indicator_prefix}{column}"
        result[indicator_name] = df[column].isna().astype(int)

    # Impute the original columns
    for column in df.columns:
        if result[column].isna().any():
            if self.strategy == "mean":
                fill_value = result[column].mean()
            elif self.strategy == "median":
                fill_value = observed_median(result[column])
            else:  # zero
                fill_value = 0

            result[column] = result[column].fillna(fill_value)

    return result