import csv from datetime import datetime, timedelta from collections import defaultdict # Load CGM data cgm = [] with open('/home/ben/downloads/Blood Glucose-2026-02-19-2026-03-17.csv') as f: reader = csv.DictReader(f) for row in reader: dt = datetime.strptime(row['Date/Time'], '%Y-%m-%d %H:%M:%S') bg = float(row['Blood Glucose (mg/dL)']) cgm.append((dt, bg)) cgm.sort(key=lambda x: x[0]) def get_cgm_window(start, end): return [(dt, bg) for dt, bg in cgm if start <= dt <= end] # Feb 25 Coke Zero investigation print("=== Feb 25 Coke Zero deep dive ===") print("Meal log: 13:24 cream soda (zero sugar), 16:38 Coke zero, 18:19 big carb meal") print() # Get full trace from 12pm to midnight feb25_trace = get_cgm_window(datetime(2026,2,25,12,0), datetime(2026,2,26,0,0)) for dt, bg in feb25_trace: print(f" {dt.strftime('%H:%M')} → {bg:.0f}") print() print("The +90 spike attributed to Coke Zero at 16:38 is likely WRONG.") print("Pre-BG was 95 at 16:38, but the 185 peak at ~19:07 is 2.5 hours later.") print("That peak is almost certainly from the 18:19 meal (corn, noodles, cookies, milk).") print("The Coke Zero itself (zero sugar) should not spike glucose.") # Better attribution: check if 185 peak aligns with 18:19 meal print() print("=== TTGNG summary statistics ===") # From the main analysis, extract nadir times ttgng_hours = [] # Manually entering the reliable ones from the output # Format: (date, last_meal_time_str, nadir_hours, post_nadir_delta, food) ttgng_data = [ ("Feb 20", "20:30", 10.2, 19.3, "milk and honey"), ("Feb 21", "17:39", 5.0, 39.3, "chicken pasta stew"), ("Feb 22", "21:00", 10.7, 22.7, "chamomile tea with honey"), ("Feb 23", "17:08", 5.2, 30.0, "Indian food OMAD"), ("Feb 24", "18:29", 9.1, 5.8, "poppi soda (after rice meal)"), ("Feb 26", "18:06", 3.0, 14.5, "vegetable and bean stew"), ("Feb 28", "18:15", 5.2, 7.2, "Beef with vegetables"), ("Mar 1", "19:17", 4.0, 11.0, "dark chocolate and milk"), ("Mar 2", "18:30", 4.4, 19.8, "dried figs and watermelon"), ("Mar 3", "12:30", 5.1, 8.2, "Greek yogurt w/ protein"), ("Mar 6", "19:15", 9.0, 7.0, "ice cream"), ("Mar 7", "18:32", 13.6, 9.7, "ground beef, sweet potato, avocado"), ("Mar 8", "18:07", 16.8, 12.7, "steak with salad, bread, beer"), ("Mar 11", "20:10", 6.0, 30.2, "chocolate pudding"), ("Mar 12", "18:30", 13.6, 17.2, "Pork pizzolli, veggies, pita, cookies"), ("Mar 13", "18:20", 17.2, 6.8, "Baked chicken, mac&cheese, etc"), ("Mar 14", "18:31", 3.2, 23.7, "peach and strawberry cobbler"), ] # The reliable TTGNG signal is the nadir timing — but we need to distinguish: # - Post-prandial nadir (reactive hypo, happens 2-4h after carb spike) # - True GNG onset nadir (happens 8-16h after last meal when glycogen depleted) # Short nadirs (< 6h) are likely reactive hypoglycemia, not GNG reactive = [d for d in ttgng_data if d[2] < 6] gng_like = [d for d in ttgng_data if d[2] >= 6] print(f"\nReactive hypo nadirs (<6h post-meal): {len(reactive)}") for d in reactive: print(f" {d[0]}: nadir at {d[2]:.1f}h, rebound +{d[3]:.1f} | {d[4]}") print(f"\nTrue GNG-onset nadirs (≥6h post-meal): {len(gng_like)}") for d in gng_like: print(f" {d[0]}: nadir at {d[2]:.1f}h, rebound +{d[3]:.1f} | {d[4]}") if gng_like: avg_ttgng = sum(d[2] for d in gng_like) / len(gng_like) print(f"\nAverage TTGNG (GNG-like nadirs): {avg_ttgng:.1f} hours") # By meal type carby_gng = [d for d in gng_like if any(w in d[4].lower() for w in ['honey', 'tea', 'ice cream', 'chocolate', 'soda', 'rice', 'cookie', 'cobbler', 'fig'])] protein_gng = [d for d in gng_like if d not in carby_gng] if carby_gng: print(f" After carby last meal: avg {sum(d[2] for d in carby_gng)/len(carby_gng):.1f}h (n={len(carby_gng)})") if protein_gng: print(f" After protein last meal: avg {sum(d[2] for d in protein_gng)/len(protein_gng):.1f}h (n={len(protein_gng)})") # Fasting threshold - deeper analysis print("\n=== Fasting threshold: controlling for food type ===") # From the main analysis, protein-heavy first meals only protein_first_meals = [ # (fast_hours, pre_bg, spike, food) (19.2, 114, 5, "Beef butter soup and poached egg"), (19.4, 103, 1, "decaf coffee with heavy cream"), (20.2, 109, 12, "cheeseburger patty, butter, raw milk"), (21.5, 97, 8, "Butter soup with poached egg and beef patty"), (21.9, 102, 11, "Beef steak with green beans and pinto beans"), (17.0, 109, 10, "meatball soup"), (18.7, 109, 27, "Greek-style shrimp salad platter"), (18.8, 96, 20, "White bread toast, scrambled eggs, salmon, macadamia nuts"), ] carb_first_meals = [ (8.2, 104, 40, "Quinoa bowl with salmon and sauce"), (8.3, 99, 88, "Rice and vegetable stew with bread"), (10.8, 119, 14, "Vegetable quiche slices"), (11.8, 117, 27, "Eggs on pita bread"), (13.6, 111, 28, "black coffee and colac cu nuca"), (14.3, 121, 25, "scrambled eggs on toast with pastry"), (14.6, 127, 39, "whole wheat breakfast burritos"), (16.5, 108, 45, "Hearty vegetable beef stew with roll and toast"), (20.1, 104, 77, "Indian food (OMAD)"), (75.9, 93, 58, "Croissant, chicken broth and poached eggs"), ] print(f"\nProtein-heavy first meals (n={len(protein_first_meals)}):") avg_pre = sum(d[1] for d in protein_first_meals)/len(protein_first_meals) avg_spike = sum(d[2] for d in protein_first_meals)/len(protein_first_meals) print(f" Avg pre-meal BG: {avg_pre:.0f}, Avg spike: +{avg_spike:.0f}") print(f"\nCarb-containing first meals (n={len(carb_first_meals)}):") avg_pre = sum(d[1] for d in carb_first_meals)/len(carb_first_meals) avg_spike = sum(d[2] for d in carb_first_meals)/len(carb_first_meals) print(f" Avg pre-meal BG: {avg_pre:.0f}, Avg spike: +{avg_spike:.0f}") # Within protein meals, does fast length matter? print(f"\n Protein meals by fast duration:") protein_first_meals.sort(key=lambda x: x[0]) for d in protein_first_meals: print(f" {d[0]:.1f}h fast → pre:{d[1]}, spike:+{d[2]} | {d[3]}")