Skip to content

API Reference — Metrics

The metrics layer computes clinical statistics from a CGM time series.

High-level facade

The GlucoseAnalysis facade (in cgmpy.analysis.core) provides all metrics via methods:

from cgmpy import GlucoseAnalysis

analysis = GlucoseAnalysis("data.csv")
analysis.mean()
analysis.TIR()
analysis.cv()
analysis.gmi()
analysis.MAGE()
# ... see the API reference for the full list.

See cgmpy.analysis.core.GlucoseAnalysis for the full method list.

Pure functions

All metric calculations are also available as standalone functions:

Module Functions
cgmpy.metrics.basic mean, median, sd, cv, gmi, percentile
cgmpy.metrics.time_in_range tir, tar, tbr, data_completeness
cgmpy.metrics.variability.sd sd_global, sd_within_day, sd_between_timepoints, ...
cgmpy.metrics.variability.mage mage_simple, mage_baghurst
cgmpy.metrics.variability.modd modd
cgmpy.metrics.variability.conga conga
cgmpy.metrics.variability.lability lability_index
cgmpy.metrics.variability.risk j_index, lbgi, hbgi, gri, adrr, m_value, grade

Targets

cgmpy.metrics.targets.GlucoseTargets dataclass

Class to define glucose targets for metrics calculation.

Source code in cgmpy/metrics/targets.py
@dataclass
class GlucoseTargets:
    """Class to define glucose targets for metrics calculation."""

    hypo_level2: float
    hypo_level1: float
    target_low: float
    target_high: float
    hyper_level1: float
    hyper_level2: float
    name: str = "Standard"

    @property
    def tir_low(self) -> float:
        """Lower bound of the Time-in-Range band (mg/dL). Alias for ``target_low``."""
        return self.target_low

    @property
    def tir_high(self) -> float:
        """Upper bound of the Time-in-Range band (mg/dL). Alias for ``target_high``."""
        return self.target_high

    @property
    def very_low(self) -> float:
        """Level-2 hypoglycemia cut-off (mg/dL). Alias for ``hypo_level2``."""
        return self.hypo_level2

    @property
    def very_high(self) -> float:
        """Level-2 hyperglycemia cut-off (mg/dL). Alias for ``hyper_level2``."""
        return self.hyper_level2

    @classmethod
    def standard(cls):
        """
        Standard targets for general diabetes.
        Level 2 hypo < 54, Level 1 hypo 54-70, TIR 70-180, TAR 180-250, TAR > 250.
        """
        return cls(
            hypo_level2=54,
            hypo_level1=70,  # Below 70
            target_low=70,
            target_high=180,
            hyper_level1=180,  # Above 180
            hyper_level2=250,  # Above 250
            name="Diabetes",
        )

    @classmethod
    def pregnancy(cls):
        """
        Specific targets for pregnancy.
        Level 2 hypo < 55, Level 1 hypo 55-63, TIR 63-140, TAR 140-250, TAR > 250.
        """
        return cls(
            hypo_level2=55,
            hypo_level1=63,  # Below 63
            target_low=63,
            target_high=140,
            hyper_level1=140,  # Above 140
            hyper_level2=250,  # Above 250
            name="Pregnancy",
        )

standard classmethod

standard()

Standard targets for general diabetes. Level 2 hypo < 54, Level 1 hypo 54-70, TIR 70-180, TAR 180-250, TAR > 250.

Source code in cgmpy/metrics/targets.py
@classmethod
def standard(cls):
    """
    Standard targets for general diabetes.
    Level 2 hypo < 54, Level 1 hypo 54-70, TIR 70-180, TAR 180-250, TAR > 250.
    """
    return cls(
        hypo_level2=54,
        hypo_level1=70,  # Below 70
        target_low=70,
        target_high=180,
        hyper_level1=180,  # Above 180
        hyper_level2=250,  # Above 250
        name="Diabetes",
    )

pregnancy classmethod

pregnancy()

Specific targets for pregnancy. Level 2 hypo < 55, Level 1 hypo 55-63, TIR 63-140, TAR 140-250, TAR > 250.

Source code in cgmpy/metrics/targets.py
@classmethod
def pregnancy(cls):
    """
    Specific targets for pregnancy.
    Level 2 hypo < 55, Level 1 hypo 55-63, TIR 63-140, TAR 140-250, TAR > 250.
    """
    return cls(
        hypo_level2=55,
        hypo_level1=63,  # Below 63
        target_low=63,
        target_high=140,
        hyper_level1=140,  # Above 140
        hyper_level2=250,  # Above 250
        name="Pregnancy",
    )

cgmpy.metrics.targets.get_targets

get_targets(target_type: str = 'diabetes') -> GlucoseTargets

Factory function to get glucose targets.

Parameters:

Name Type Description Default
target_type str

Either 'diabetes' (default) or 'pregnancy'.

'diabetes'

Returns:

Type Description
GlucoseTargets

GlucoseTargets object.

Raises:

Type Description
ValueError

If target_type is not a recognised profile name.

Source code in cgmpy/metrics/targets.py
def get_targets(target_type: str = "diabetes") -> GlucoseTargets:
    """
    Factory function to get glucose targets.

    Args:
        target_type: Either 'diabetes' (default) or 'pregnancy'.

    Returns:
        GlucoseTargets object.

    Raises:
        ValueError: If `target_type` is not a recognised profile name.
    """
    normalized = target_type.lower()
    if normalized not in _VALID_TARGET_TYPES:
        valid = ", ".join(sorted(_VALID_TARGET_TYPES))
        raise ValueError(f"Unknown target type {target_type!r}. Valid options are: {valid}.")
    if normalized == "pregnancy":
        return GlucoseTargets.pregnancy()
    return GlucoseTargets.standard()

Pregnancy

cgmpy.metrics.pregnancy.PregnancyAnalysis

Analysis of CGM data during pregnancy (pregestational and gestational).

Provides trimester-specific metrics and reports for pregnant women with diabetes.

Parameters:

Name Type Description Default
data_source str | DataFrame | GlucoseData

A file path or DataFrame with glucose data.

required
delivery_date str

Estimated delivery date in 'YYYY-MM-DD' format.

required
week int

Gestation week at delivery.

required
day int

Additional days (default 0).

0
**kwargs Any

Additional arguments passed to PregnancyData.

{}
Source code in cgmpy/metrics/pregnancy.py
class PregnancyAnalysis:
    """Analysis of CGM data during pregnancy (pregestational and gestational).

    Provides trimester-specific metrics and reports for pregnant women
    with diabetes.

    Args:
        data_source: A file path or DataFrame with glucose data.
        delivery_date: Estimated delivery date in 'YYYY-MM-DD' format.
        week: Gestation week at delivery.
        day: Additional days (default 0).
        **kwargs: Additional arguments passed to ``PregnancyData``.
    """

    def __init__(
        self,
        data_source: str | pd.DataFrame | GlucoseData,
        delivery_date: str,
        week: int,
        day: int = 0,
        **kwargs: Any,
    ):
        if isinstance(data_source, GlucoseData):
            # Use the already-loaded GlucoseData directly
            self._pregnancy_data = PregnancyData(
                data_source=data_source.data,
                delivery_date=delivery_date,
                week=week,
                day=day,
                target_type="pregnancy",
                **kwargs,
            )
            self._analysis = GlucoseAnalysis(data_source)
        else:
            self._pregnancy_data = PregnancyData(
                data_source=data_source,
                delivery_date=delivery_date,
                week=week,
                day=day,
                **kwargs,
            )
            self._analysis = GlucoseAnalysis(
                GlucoseData(
                    data_source=self._pregnancy_data.data,
                    target_type="pregnancy",
                )
            )

        def _wrap_trimester(df: pd.DataFrame):
            if len(df) == 0:
                return None
            return GlucoseAnalysis(GlucoseData(data_source=df, target_type="pregnancy"))

        self.t1 = _wrap_trimester(self._pregnancy_data.trimesters["first_trimester"])
        self.t2 = _wrap_trimester(self._pregnancy_data.trimesters["second_trimester"])
        self.t3 = _wrap_trimester(self._pregnancy_data.trimesters["third_trimester"])

    # ── Delegate basic accessors ────────────────────────────────────────

    @property
    def data(self) -> pd.DataFrame:
        return self._pregnancy_data.data

    def gmi(self) -> float:
        return self._analysis.gmi()

    def TIR(self) -> float:
        return self._analysis.TIR()

    def data_completeness(self) -> int:
        return self._analysis.data_completeness()

    def sd_within_day(self) -> dict:
        return self._analysis.sd_within_day()

    def sd_daily_mean(self) -> dict:
        return self._analysis.sd_daily_mean()

    def MAGE(self) -> float:
        return self._analysis.MAGE()

    def MODD(self, days: int = 1) -> dict:
        return self._analysis.MODD(days)

    def CONGA(self, hours: int = 4, max_gap_minutes: float | None = None) -> dict:
        return self._analysis.CONGA(hours, max_gap_minutes)

    def LBGI(self) -> float:
        return self._analysis.LBGI()

    def HBGI(self) -> float:
        return self._analysis.HBGI()

    def ADRR(self) -> dict:
        return self._analysis.ADRR()

    def GRI(self, pregnancy: bool = False) -> dict:
        return self._analysis.GRI(pregnancy)

    def j_index(self) -> float:
        return self._analysis.j_index()

    def get_weeks_days(self) -> tuple[int, int]:
        return self._pregnancy_data.get_weeks_days()

    # ── Pregnancy-specific methods ──────────────────────────────────────

    def summary_by_trimester(self) -> dict[str, Any]:
        """Simplified comparative summary."""

        def _trimester_metrics(ta):
            if ta is None:
                return None
            basic = ta.calculate_all_metrics()
            return {
                "DataCompleteness": ta.data_completeness(),
                "GMI": basic.get("GMI"),
                "Mean": basic.get("Mean"),
                "Median": basic.get("Median"),
                "SD": basic.get("Std"),
                "CV": basic.get("CV"),
                "TIR": ta.TIR(),
                "TIR_tight": ta.TIR_tight(),
                "TAR140": ta.TAR140(),
                "TAR250": ta.TAR250(),
                "TBR63": ta.TBR63(),
                "TBR55": ta.TBR55(),
                "SDw": ta.sd_within_day().get("sd"),
                "SDdm": ta.sd_daily_mean().get("sd"),
                "MAGE": ta.MAGE(),
                "MODD": ta.MODD().get("value"),
                "CONGA4": ta.CONGA(hours=4).get("value"),
                "LBGI": ta.LBGI(),
                "HBGI": ta.HBGI(),
                "ADRR": ta.ADRR().get("adrr"),
                "GRI": ta.GRI(pregnancy=True).get("GRI"),
                "J-Index": ta.j_index(),
            }

        return {
            "T1": _trimester_metrics(self.t1),
            "T2": _trimester_metrics(self.t2),
            "T3": _trimester_metrics(self.t3),
        }

    def all_simplified(self) -> dict:
        """Simplified metrics dictionary for overall pregnancy data."""
        basic = self._analysis.calculate_all_metrics()
        simplified = {
            "DataCompleteness": self.data_completeness(),
            "GMI": basic.get("GMI"),
            "Mean": basic.get("Mean"),
            "Median": basic.get("Median"),
            "SD": basic.get("Std"),
            "CV": basic.get("CV"),
            "TIR": self.TIR(),
            "TIR_tight": self._analysis.TIR_tight(),
            "TAR140": self._analysis.TAR_total(),
            "TAR250": self._analysis.TAR250(),
            "TBR63": self._analysis.TBR_total(),
            "TBR55": self._analysis.TBR55(),
            "SDw": self.sd_within_day().get("sd"),
            "SDdm": self.sd_daily_mean().get("sd"),
            "MAGE": self.MAGE(),
            "MODD": self.MODD().get("value"),
            "CONGA4": self.CONGA(hours=4).get("value"),
            "LBGI": self.LBGI(),
            "HBGI": self.HBGI(),
            "ADRR": self.ADRR().get("adrr"),
            "GRI": self.GRI(pregnancy=True).get("GRI"),
            "J-Index": self.j_index(),
        }
        return simplified

    def calculate_all_metrics(self, flatten: bool = False) -> dict[str, Any]:
        """Complete analysis summary.

        Args:
            flatten: If True, returns a flat dictionary with prefixes
                    (total_, t1_, t2_, t3_, gest_) suitable for CSV/DataFrames.
        """
        w, d = self.get_weeks_days()
        results = {
            "gestation": {
                "weeks": w,
                "days": d,
                "conception": self._pregnancy_data.conception_date.isoformat(),
                "delivery": self._pregnancy_data.delivery_date.isoformat(),
            },
            "overall": self.all_simplified(),
            "trimesters": self.summary_by_trimester(),
        }

        if not flatten:
            return results

        flat = {}
        for k, v in results["gestation"].items():
            flat[f"gest_{k}"] = v
        for k, v in results["overall"].items():
            flat[f"total_{k}"] = v
        for t_key, t_metrics in results["trimesters"].items():
            if t_metrics:
                for k, v in t_metrics.items():
                    flat[f"{t_key.lower()}_{k}"] = v
            else:
                template = self.all_simplified().keys()
                for k in template:
                    flat[f"{t_key.lower()}_{k}"] = None

        return flat

    def __str__(self) -> str:
        w, d = self.get_weeks_days()
        output = [
            f"=== PREGNANCY ANALYSIS REPORT ({w}+{d} weeks) ===",
            f"Overall GMI: {self.gmi():.1f}% | TIR (63-140): {self.TIR():.1f}%",
            "\nTrimester Breakdown:",
        ]

        summary = self.summary_by_trimester()
        for t_label, metrics in summary.items():
            if metrics:
                output.append(
                    f"  {t_label}: GMI {metrics['GMI']:.1f}% | TIR {metrics['TIR']:.1f}% | CV {metrics['CV']:.1f}%"
                )
            else:
                output.append(f"  {t_label}: (No data available)")

        return "\n".join(output)

summary_by_trimester

summary_by_trimester() -> dict[str, Any]

Simplified comparative summary.

Source code in cgmpy/metrics/pregnancy.py
def summary_by_trimester(self) -> dict[str, Any]:
    """Simplified comparative summary."""

    def _trimester_metrics(ta):
        if ta is None:
            return None
        basic = ta.calculate_all_metrics()
        return {
            "DataCompleteness": ta.data_completeness(),
            "GMI": basic.get("GMI"),
            "Mean": basic.get("Mean"),
            "Median": basic.get("Median"),
            "SD": basic.get("Std"),
            "CV": basic.get("CV"),
            "TIR": ta.TIR(),
            "TIR_tight": ta.TIR_tight(),
            "TAR140": ta.TAR140(),
            "TAR250": ta.TAR250(),
            "TBR63": ta.TBR63(),
            "TBR55": ta.TBR55(),
            "SDw": ta.sd_within_day().get("sd"),
            "SDdm": ta.sd_daily_mean().get("sd"),
            "MAGE": ta.MAGE(),
            "MODD": ta.MODD().get("value"),
            "CONGA4": ta.CONGA(hours=4).get("value"),
            "LBGI": ta.LBGI(),
            "HBGI": ta.HBGI(),
            "ADRR": ta.ADRR().get("adrr"),
            "GRI": ta.GRI(pregnancy=True).get("GRI"),
            "J-Index": ta.j_index(),
        }

    return {
        "T1": _trimester_metrics(self.t1),
        "T2": _trimester_metrics(self.t2),
        "T3": _trimester_metrics(self.t3),
    }

all_simplified

all_simplified() -> dict

Simplified metrics dictionary for overall pregnancy data.

Source code in cgmpy/metrics/pregnancy.py
def all_simplified(self) -> dict:
    """Simplified metrics dictionary for overall pregnancy data."""
    basic = self._analysis.calculate_all_metrics()
    simplified = {
        "DataCompleteness": self.data_completeness(),
        "GMI": basic.get("GMI"),
        "Mean": basic.get("Mean"),
        "Median": basic.get("Median"),
        "SD": basic.get("Std"),
        "CV": basic.get("CV"),
        "TIR": self.TIR(),
        "TIR_tight": self._analysis.TIR_tight(),
        "TAR140": self._analysis.TAR_total(),
        "TAR250": self._analysis.TAR250(),
        "TBR63": self._analysis.TBR_total(),
        "TBR55": self._analysis.TBR55(),
        "SDw": self.sd_within_day().get("sd"),
        "SDdm": self.sd_daily_mean().get("sd"),
        "MAGE": self.MAGE(),
        "MODD": self.MODD().get("value"),
        "CONGA4": self.CONGA(hours=4).get("value"),
        "LBGI": self.LBGI(),
        "HBGI": self.HBGI(),
        "ADRR": self.ADRR().get("adrr"),
        "GRI": self.GRI(pregnancy=True).get("GRI"),
        "J-Index": self.j_index(),
    }
    return simplified

calculate_all_metrics

calculate_all_metrics(flatten: bool = False) -> dict[str, Any]

Complete analysis summary.

Parameters:

Name Type Description Default
flatten bool

If True, returns a flat dictionary with prefixes (total_, t1_, t2_, t3_, gest_) suitable for CSV/DataFrames.

False
Source code in cgmpy/metrics/pregnancy.py
def calculate_all_metrics(self, flatten: bool = False) -> dict[str, Any]:
    """Complete analysis summary.

    Args:
        flatten: If True, returns a flat dictionary with prefixes
                (total_, t1_, t2_, t3_, gest_) suitable for CSV/DataFrames.
    """
    w, d = self.get_weeks_days()
    results = {
        "gestation": {
            "weeks": w,
            "days": d,
            "conception": self._pregnancy_data.conception_date.isoformat(),
            "delivery": self._pregnancy_data.delivery_date.isoformat(),
        },
        "overall": self.all_simplified(),
        "trimesters": self.summary_by_trimester(),
    }

    if not flatten:
        return results

    flat = {}
    for k, v in results["gestation"].items():
        flat[f"gest_{k}"] = v
    for k, v in results["overall"].items():
        flat[f"total_{k}"] = v
    for t_key, t_metrics in results["trimesters"].items():
        if t_metrics:
            for k, v in t_metrics.items():
                flat[f"{t_key.lower()}_{k}"] = v
        else:
            template = self.all_simplified().keys()
            for k in template:
                flat[f"{t_key.lower()}_{k}"] = None

    return flat