|
| 1 | +""" |
| 2 | +This module implements various plots for the iglu_python package. |
| 3 | +""" |
| 4 | + |
| 5 | +import matplotlib.pyplot as plt |
| 6 | +import pandas as pd |
| 7 | + |
| 8 | + |
| 9 | +def plot_daily(cgm_timeseries: pd.Series, lower: int = 70, upper: int = 140) -> plt.Figure: |
| 10 | + """ |
| 11 | + Plot daily Glucose values for each day separately |
| 12 | +
|
| 13 | +
|
| 14 | +
|
| 15 | + Args: |
| 16 | + - cgm_timeseries: pd.Series |
| 17 | + - lower: int, default=70, Lower bound used for hypoglycemia cutoff, in mg/dL |
| 18 | + - upper: int, default=140, Upper bound used for hyperglycemia cutoff, in mg/dL |
| 19 | +
|
| 20 | + Returns: |
| 21 | + plt.Figure object |
| 22 | + """ |
| 23 | + # divide cgm_timeseries into list of daily series |
| 24 | + cgm_daily_group = cgm_timeseries.resample("D") |
| 25 | + cgm_timeseries_daily = {day: cgm_daily_group.get_group(day) for day in cgm_daily_group.groups} |
| 26 | + |
| 27 | + # plot each day separately |
| 28 | + # Create one figure with subplots for each day |
| 29 | + num_days = len(cgm_timeseries_daily) |
| 30 | + fig, axes = plt.subplots(num_days, 1, figsize=(12, 3 * num_days)) |
| 31 | + |
| 32 | + # If only one day, axes will be a single object, not an array |
| 33 | + if num_days == 1: |
| 34 | + axes = [axes] |
| 35 | + |
| 36 | + for i, (day, cgm_one_day) in enumerate(cgm_timeseries_daily.items()): |
| 37 | + # Convert datetime index to time-only for x-axis display |
| 38 | + axes[i].plot(cgm_one_day.index, cgm_one_day.values) |
| 39 | + axes[i].set_title(f"Day: {day.strftime('%Y-%m-%d')}") |
| 40 | + axes[i].set_ylabel("Glucose (mg/dL)") |
| 41 | + axes[i].set_ylim(0, max(max(cgm_one_day.values), 300)) |
| 42 | + |
| 43 | + # Fill area above upper limit and plot it in orange |
| 44 | + upper_array = [upper] * len(cgm_one_day.values) |
| 45 | + area_over_upper = [ |
| 46 | + cgm_one_day.values[i] if cgm_one_day.values[i] > upper else upper for i in range(len(cgm_one_day.values)) |
| 47 | + ] |
| 48 | + axes[i].fill_between(cgm_one_day.index, area_over_upper, upper_array, alpha=0.3, color="orange") |
| 49 | + axes[i].axhline(y=upper, color="orange", linestyle="--", alpha=0.7, label=f"Hyper threshold ({upper} mg/dL)") |
| 50 | + |
| 51 | + # Fill area below lower limit and plot it in blue |
| 52 | + lower_array = [lower] * len(cgm_one_day.values) |
| 53 | + area_below_lower = [ |
| 54 | + cgm_one_day.values[i] if cgm_one_day.values[i] < lower else lower for i in range(len(cgm_one_day.values)) |
| 55 | + ] |
| 56 | + axes[i].fill_between(cgm_one_day.index, lower_array, area_below_lower, alpha=0.3, color="blue") |
| 57 | + axes[i].axhline(y=lower, color="blue", linestyle="--", alpha=0.7, label=f"Hypo threshold ({lower} mg/dL)") |
| 58 | + |
| 59 | + # on horisontal axis, show only time in hours |
| 60 | + axes[i].set_xlabel("Time (hours)") |
| 61 | + time_range = pd.date_range(start=day, periods=24, freq="1h") |
| 62 | + axes[i].set_xticks(time_range) # Show every hour from 0 to 24 |
| 63 | + axes[i].set_xticklabels([f"{h.hour}" for h in time_range]) # Format as HH:00 |
| 64 | + axes[i].grid(True, alpha=0.3, linestyle="--") |
| 65 | + axes[i].legend() |
| 66 | + |
| 67 | + return fig |
0 commit comments