Skip to main content

Python toolkit for CGM analysis with individually callable glycemic metrics and ML-ready feature extraction.

Project description

GlycoSignal

PyPI version Python versions Tests License: MIT

GlycoSignal

Python library for CGM data analysis — individually callable glycemic metrics, day/sliding-window segmentation, and ML-ready feature matrices.

pip install GlycoSignal

Quick start

import glycosignal as gs

df = gs.load_csv("cgm.csv")
df = gs.clean_cgm(df)

# Individual metrics
gs.mean_glucose(df)                             # 138.5
gs.time_in_range_percent(df, low=70, high=180)  # 93.1

# Segment → feature matrix  (the standard pipeline)
segs = gs.create_day_segments(df)               # 24 h, midnight anchor
X    = gs.build_feature_map(segs.windows)       # 32 features × n_days

Input format

Two required columns; everything else is auto-detected (case-insensitive).

Column Required Recognized aliases
Timestamp Yes time, datetime, date_time, date
Glucose Yes Glucose Value (mg/dL), gl, sgv, glucose_mg_dl, bg
subject Multi-subject only id, ptid, patient_id, subjectid
# Override auto-detection
df = gs.load_csv("data.csv", timestamp_col="time_utc", glucose_col="bg_mg_dl")

# Multiple subjects — one file with subject column
df = gs.io.load_cgm_file("all.csv", subject_col="ptid")

# Multiple subjects — one CSV per subject in a folder
df = gs.io.load_cgm_folder("data/subjects/")

Data segmentation

Both functions return WindowResult(windows, metadata). The windows DataFrame flows directly into build_feature_map().

Daily segments

from glycosignal import windows

segs = windows.create_day_segments(df)                    # midnight–midnight (default)
segs = windows.create_day_segments(df, anchor_time="08:00")             # 8 AM–8 AM
segs = windows.create_day_segments(df, anchor_time="08:00",
                                   min_fraction=0.7,      # drop days < 70 % coverage
                                   group_col="subject")   # multi-subject

print(segs.metadata)
# {'n_groups': 1, 'n_valid_windows': 7, 'n_discarded_partial_days': 0, ...}

Sliding windows

result = windows.create_sliding_windows(df, window_hours=6, step_hours=1)
result = windows.create_sliding_windows(df, window_hours=12, step_hours=12,
                                        anchor_time="08:00")
result = windows.create_sliding_windows(df, window_hours=24, overlap_hours=12)
result = windows.create_sliding_windows(df, window_hours=24, step_hours=6,
                                        group_col="subject")

Key parameters: window_hours, step_hours, anchor_time, min_fraction (default 0.0 — keep all windows), group_col, interpolate, max_gap_points.

Output

The windows DataFrame is long-format — one row per (window, time-point):

window_id subject date Timestamp Glucose
S01_2023-01-02 S01 2023-01-02 2023-01-02 00:00 112.4

Non-midnight anchors append _HHMM to the window ID (e.g. S01_2023-01-02_0800).

Saving as wide-format CSV

wide = windows.pivot_windows_wide(segs.windows)
wide.to_csv("segments.csv", index=False)
# → columns: date | subject | 00:00 | 00:05 | … | 23:55
# For non-midnight anchor (e.g. "08:00"): 08:00 | … | 23:55 | 00:00 | … | 07:55

Glycemic metrics

Every metric is a standalone function. See the full metric reference for all formulas.

from glycosignal import metrics

# Core
metrics.mean_glucose(df)                              # 138.5
metrics.median_glucose(df)
metrics.std_glucose(df)
metrics.cv(df)                                        # 17.7 %
metrics.time_in_range_percent(df, low=70, high=180)   # 93.1 %
metrics.time_above_range(df, threshold=180)
metrics.time_below_range(df, threshold=70)

# Variability
metrics.mage(df)                                      # 27.0
metrics.j_index(df)
metrics.conga(df)
metrics.modd(df)
metrics.grade(df)

# Risk indices
metrics.lbgi(df)
metrics.hbgi(df)
metrics.adrr(df)
metrics.gri(df)                                       # 7.2

# Grouped helpers — return dicts
metrics.basic_stats(df)          # mean, median, min, max, q1, q3
metrics.variability_metrics(df)  # sd, cv, j_index, mage
metrics.risk_indices(df)         # lbgi, hbgi, adrr, gri
metrics.summary_dict(df)         # all of the above combined

Performance tip — call prepare() once when computing many metrics on the same data:

from glycosignal.schemas import prepare
p = prepare(df)
metrics.mean_glucose(p); metrics.cv(p); metrics.lbgi(p)

Feature matrices

from glycosignal import windows, features

segs = windows.create_day_segments(df)
X = features.build_feature_map(segs.windows)           # 32 features × n_windows

# Subset
X = features.build_feature_map(segs.windows,
    feature_names=["mean_glucose", "cv", "tir_70_180_pct", "mage", "lbgi"])

# Single window → dict
features.build_feature_vector(window_df, feature_names=["mean_glucose", "cv"])

# List of DataFrames (one per subject)
features.build_feature_table([df_s01, df_s02], record_ids=["S01", "S02"])

Feature registry

gs.list_features()                      # all 32 names
gs.list_features(category="risk")       # ['adrr', 'gri', 'hbgi', 'lbgi']
gs.get_feature_metadata()               # DataFrame: name | description | category
gs.get_feature("gri").description       # 'Glucose Risk Index (Klonoff et al. 2023)'

# Register a custom feature
from glycosignal.registry import DEFAULT_REGISTRY
DEFAULT_REGISTRY.register(name="my_metric", func=my_fn,
                           description="...", category="variability")

Preprocessing

from glycosignal import preprocessing

df     = preprocessing.clean_cgm(df)                              # drop NaN, sort, enforce positive
report = preprocessing.validate_cgm(df)                           # structured quality report
gaps   = preprocessing.detect_gaps(df)                            # DataFrame of gap intervals
df     = preprocessing.resample_cgm(df, freq="5min")              # regular grid
df     = preprocessing.interpolate_cgm(df, method="pchip",
                                        max_gap_points=12)
df     = preprocessing.convert_units(df, from_unit="mmol/L",
                                      to_unit="mg/dL")

Event detection

Returns a DataFrame with start_time, end_time, duration_minutes, event_type.

from glycosignal import detect

detect.detect_hypoglycemia(df, threshold=70, min_duration_minutes=15)
detect.detect_hyperglycemia(df, threshold=180, min_duration_minutes=15)
detect.detect_nocturnal_events(df, start_hour=0, end_hour=6)
detect.detect_postprandial_excursions(df, rise_threshold=50)

Plotting

All functions return (fig, ax) and never call plt.show().

from glycosignal import plotting

fig, ax = plotting.plot_glucose_timeseries(df, subject="P001")
fig, ax = plotting.plot_daily_overlay(df)
fig, ax = plotting.plot_agp(df)
fig, ax = plotting.plot_histogram(df)
fig.savefig("output.png", dpi=150)

CLI

glycosignal summary data.csv
glycosignal windows data.csv --window-hours 24 --overlap-hours 0 --output windows.csv
glycosignal features windows.csv --output features.csv
glycosignal features windows.csv --features mean_glucose,cv,lbgi,gri
glycosignal list-features
glycosignal list-features --category risk

License

MIT. Copyright (c) 2024 Jiafeng Song. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

glycosignal-0.2.0.tar.gz (75.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

glycosignal-0.2.0-py3-none-any.whl (50.3 kB view details)

Uploaded Python 3

File details

Details for the file glycosignal-0.2.0.tar.gz.

File metadata

  • Download URL: glycosignal-0.2.0.tar.gz
  • Upload date:
  • Size: 75.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for glycosignal-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b890fc22d1d79d191621786c53c573243775cd3691cdef594ad69ba4f447677d
MD5 88f75b48cf162b0c3d15733abb3c27ce
BLAKE2b-256 192c8f733a755043f04c77edebc6fef4d54ac79b7abc5e5d6f576b742a31e0fc

See more details on using hashes here.

File details

Details for the file glycosignal-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: glycosignal-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 50.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for glycosignal-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 601a17d884c2f0397e14bae3a48f6cb16012333dd5d9524805eee62caf620b11
MD5 844826a892684e8436c7dea3b9dca3c5
BLAKE2b-256 3fd0a1431638a646b779a277e86db23676834cdeaa57ac92e223c24781fc3534

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page