import csv from datetime import datetime, timedelta from collections import defaultdict import statistics # 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 = int(row['Blood Glucose (mg/dL)']) cgm.append((dt, bg)) cgm.sort(key=lambda x: x[0]) print(f"CGM data: {cgm[0][0]} to {cgm[-1][0]}, {len(cgm)} readings") # Group by date daily = defaultdict(list) for dt, bg in cgm: daily[dt.strftime('%Y-%m-%d')].append((dt, bg)) # Parse meal log meals_raw = """ # March 16 12:55 White bread toast, scrambled eggs, salmon, macadamia nuts 13:40 Greek yogurt with frozen blueberries 17:10 Cheese, meat sticks, olipop 18:25 Spaghetti with shrimp and cream sauce, roasted broccoli and mushrooms # March 15 8:48 Scrambled eggs on toast with coffee 14:25 Apple, peanut butter, cheddar cheese sticks, salmon and cream cheese toast, lapsang tea 18:10 Oat roll, bud light, mamaliga, shrimp, green beans, hash brown casserole, chocolate cake # March 14 7:55 black coffee and colac cu nuca 8:35 sunny side up eggs 11:40 peanut butter, strawberry jam, rice cake, raw milk 14:40 smoked salmon and cream cheese toast 18:10 tacos with avocado, carrots, and cucumber 18:31 peach and strawberry cobbler # March 13 13:10 Greek-style shrimp salad platter 16:10 Protein bar, macadamia nuts, olipop soda 18:20 Baked chicken, macaroni and cheese, green beans, cornbread, mashed potatoes # March 12 8:00 Eggs on pita bread 11:15 Figs 18:30 Pork pizzolli, roasted veggies, pita bread, girl scout cookies # March 11 8:25 Vegetable quiche slices and black coffee 12:55 Greek yogurt with protein powder, blueberries and coconut granola 17:55 Beef stir fry with vegetables and rice 20:10 Homemade chocolate pudding # March 10 8:40 scrambled eggs on toast with pastry and black coffee 13:30 greek yogurt with protein powder 18:10 beef gyro salad with pita and hummus 21:35 glass of raw milk # March 9 13:31 decaf coffee with heavy cream 17:15 coconut protein bar and carnivore bar 17:55 dried figs and popcorn chips 18:24 spaghetti with chicken and vegetables # March 8 13:09 tea with honey, bread roll, mixed salad, a few bites of birthday cake 15:05 ham and cheese tortillas with apple and peanut butter 18:07 sirloin steak with salad, vegetables, bread, and beer # March 7 9:48 whole wheat breakfast burritos 11:34 pancakes with sausage and syrup 15:17 Peanut butter and jelly sandwich, olipop 18:32 Ground beef, roasted sweet potato and avocado plate # March 6 16:23 Croissant, chicken broth and poached eggs 17:55 Bacon cheeseburger with salad and porter beer 19:15 Vanilla ice cream with cherries and chocolate pieces # March 3 11:30 meatball soup 12:30 Greek yogurt with protein powder # March 2 14:30 Beef butter soup and poached egg 15:30 cheese slices 16:00 greek yogurt with protein powder 18:30 dried figs and watermelon # March 1 16:07 Beef steak with green beans and pinto beans 18:18 beef patty, sauteed vegetables, tilapia, avocado slices 19:17 dark chocolate bar and milk # Feb 28 16:09 Butter soup with poached egg and beef patty 18:15 Beef with vegetables and cream sauce """ meals = [] current_date = None for line in meals_raw.strip().split('\n'): line = line.strip() if not line: continue if line.startswith('# '): date_str = line[2:] # Parse date date_map = { 'March 16': '2026-03-16', 'March 15': '2026-03-15', 'March 14': '2026-03-14', 'March 13': '2026-03-13', 'March 12': '2026-03-12', 'March 11': '2026-03-11', 'March 10': '2026-03-10', 'March 9': '2026-03-09', 'March 8': '2026-03-08', 'March 7': '2026-03-07', 'March 6': '2026-03-06', 'March 3': '2026-03-03', 'March 2': '2026-03-02', 'March 1': '2026-03-01', 'Feb 28': '2026-02-28', } current_date = date_map.get(date_str) else: # Parse time and meal parts = line.split(' ', 1) if len(parts) == 2 and ':' in parts[0]: time_str = parts[0] meal_desc = parts[1] try: dt = datetime.strptime(f"{current_date} {time_str}", '%Y-%m-%d %H:%M') meals.append((dt, meal_desc)) except: pass meals.sort(key=lambda x: x[0]) print(f"\nMeal log: {len(meals)} meals from {meals[0][0].date()} to {meals[-1][0].date()}") # For each meal, find the glucose at meal time and the peak in the next 2 hours print("\n" + "="*100) print("MEAL-BY-MEAL GLUCOSE RESPONSE") print("="*100) meal_responses = [] for meal_dt, meal_desc in meals: date_str = meal_dt.strftime('%Y-%m-%d') if date_str not in daily: print(f"\n{meal_dt.strftime('%b %d %H:%M')} — {meal_desc}") print(f" ⚠️ No CGM data for this date") continue day_data = daily[date_str] # Also check next day for late meals next_date = (meal_dt + timedelta(days=1)).strftime('%Y-%m-%d') if next_date in daily: day_data = day_data + daily[next_date] # Find glucose at meal time (closest reading within 10 min) baseline_bg = None min_diff = timedelta(minutes=15) for dt, bg in day_data: diff = abs(dt - meal_dt) if diff < min_diff: min_diff = diff baseline_bg = bg # Find peak in next 2 hours peak_bg = None peak_time = None for dt, bg in day_data: if timedelta(0) <= (dt - meal_dt) <= timedelta(hours=2): if peak_bg is None or bg > peak_bg: peak_bg = bg peak_time = dt # Find nadir in 2-4 hours (reactive hypoglycemia check) nadir_bg = None nadir_time = None for dt, bg in day_data: if timedelta(hours=1) <= (dt - meal_dt) <= timedelta(hours=4): if nadir_bg is None or bg < nadir_bg: nadir_bg = bg nadir_time = dt if baseline_bg and peak_bg: delta = peak_bg - baseline_bg meal_responses.append({ 'dt': meal_dt, 'meal': meal_desc, 'baseline': baseline_bg, 'peak': peak_bg, 'delta': delta, 'peak_time': peak_time, 'nadir': nadir_bg, 'nadir_time': nadir_time, }) severity = "🔴" if delta > 40 or peak_bg > 160 else "🟡" if delta > 25 or peak_bg > 140 else "🟢" peak_min = int((peak_time - meal_dt).total_seconds() / 60) if peak_time else 0 print(f"\n{severity} {meal_dt.strftime('%b %d %H:%M')} — {meal_desc}") print(f" Baseline: {baseline_bg} → Peak: {peak_bg} (+{delta}) at {peak_min}min") if nadir_bg and nadir_bg < baseline_bg - 10: print(f" ⚠️ Reactive drop to {nadir_bg} at {nadir_time.strftime('%H:%M')}") else: print(f"\n{meal_dt.strftime('%b %d %H:%M')} — {meal_desc}") print(f" Baseline: {baseline_bg}, Peak: {peak_bg} (incomplete data)") # Daily summary stats print("\n\n" + "="*100) print("DAILY SUMMARY") print("="*100) for date_str in sorted(daily.keys()): readings = [bg for dt, bg in daily[date_str]] if len(readings) < 10: continue mean = statistics.mean(readings) std = statistics.stdev(readings) if len(readings) > 1 else 0 cv = (std / mean) * 100 if mean > 0 else 0 tir = sum(1 for bg in readings if 70 <= bg <= 140) / len(readings) * 100 # Overnight (midnight to 6am) overnight = [bg for dt, bg in daily[date_str] if dt.hour < 6] overnight_mean = statistics.mean(overnight) if overnight else None # First meal time day_meals = [(dt, desc) for dt, desc in meals if dt.strftime('%Y-%m-%d') == date_str] first_meal = day_meals[0][0].strftime('%H:%M') if day_meals else "no log" last_meal = day_meals[-1][0].strftime('%H:%M') if day_meals else "no log" n_meals = len(day_meals) eating_window = "" if len(day_meals) >= 2: window_h = (day_meals[-1][0] - day_meals[0][0]).total_seconds() / 3600 eating_window = f" ({window_h:.1f}h window)" dn = datetime.strptime(date_str, '%Y-%m-%d') day_name = dn.strftime('%a') quality = "🟢" if mean < 115 and tir > 90 else "🟡" if mean < 125 and tir > 80 else "🔴" overnight_str = f", overnight avg {overnight_mean:.0f}" if overnight_mean else "" print(f"{quality} {date_str} ({day_name}): mean {mean:.0f}, max {max(readings)}, min {min(readings)}, " f"CV {cv:.1f}%, TIR {tir:.0f}%{overnight_str}") if n_meals: print(f" Meals: {n_meals}x, {first_meal}-{last_meal}{eating_window}") # Pattern analysis print("\n\n" + "="*100) print("PATTERN ANALYSIS: FOOD CATEGORIES") print("="*100) # Categorize meals categories = { 'Protein-heavy (no carbs)': [], 'Eggs': [], 'Greek yogurt / protein': [], 'Bread/toast/pastry': [], 'Rice': [], 'Pasta': [], 'Fruit/sweet': [], 'Mixed/balanced': [], } for r in meal_responses: m = r['meal'].lower() if any(w in m for w in ['steak', 'gyro', 'burger', 'beef patty', 'ground beef', 'tilapia', 'chicken', 'pork']) and not any(w in m for w in ['rice', 'pasta', 'spaghetti', 'bread', 'toast', 'bun', 'tortilla', 'macaroni']): categories['Protein-heavy (no carbs)'].append(r) elif 'egg' in m and not any(w in m for w in ['toast', 'bread', 'pita', 'burrito']): categories['Eggs'].append(r) elif 'yogurt' in m or 'protein powder' in m or 'protein bar' in m: categories['Greek yogurt / protein'].append(r) elif any(w in m for w in ['toast', 'bread', 'croissant', 'colac', 'pastry', 'roll', 'cornbread']): categories['Bread/toast/pastry'].append(r) elif 'rice' in m and 'rice cake' not in m: categories['Rice'].append(r) elif any(w in m for w in ['spaghetti', 'pasta', 'macaroni']): categories['Pasta'].append(r) elif any(w in m for w in ['fig', 'fruit', 'apple', 'watermelon', 'cobbler', 'cake', 'ice cream', 'chocolate', 'cookie', 'pudding', 'pancake', 'syrup']): categories['Fruit/sweet'].append(r) else: categories['Mixed/balanced'].append(r) for cat, items in categories.items(): if not items: continue deltas = [r['delta'] for r in items] peaks = [r['peak'] for r in items] avg_delta = statistics.mean(deltas) avg_peak = statistics.mean(peaks) max_peak = max(peaks) print(f"\n*{cat}* (n={len(items)}):") print(f" Avg delta: +{avg_delta:.0f}, Avg peak: {avg_peak:.0f}, Max peak: {max_peak}") for r in sorted(items, key=lambda x: x['delta'], reverse=True): print(f" {r['dt'].strftime('%b %d %H:%M')} {r['meal'][:60]}: +{r['delta']} → {r['peak']}") # Fasting analysis print("\n\n" + "="*100) print("FASTING WINDOW ANALYSIS") print("="*100) # For each day with meal data, calculate fasting glucose trend for date_str in sorted(daily.keys()): day_meals = [(dt, desc) for dt, desc in meals if dt.strftime('%Y-%m-%d') == date_str] if not day_meals: continue first_meal = day_meals[0][0] # Look at glucose from midnight to first meal fasting_readings = [(dt, bg) for dt, bg in daily[date_str] if dt < first_meal] if len(fasting_readings) < 6: continue # Split into overnight (0-6am) and morning (6am-first meal) overnight = [bg for dt, bg in fasting_readings if dt.hour < 6] morning = [bg for dt, bg in fasting_readings if dt.hour >= 6] # Dawn phenomenon: glucose rise between 4-8am dawn = [(dt, bg) for dt, bg in fasting_readings if 4 <= dt.hour <= 8] fasting_hours = first_meal.hour + first_meal.minute/60 # Approximate: assume last meal previous day prev_date = (first_meal - timedelta(days=1)).strftime('%Y-%m-%d') prev_meals = [(dt, desc) for dt, desc in meals if dt.strftime('%Y-%m-%d') == prev_date] if prev_meals: last_prev = prev_meals[-1][0] fast_duration = (first_meal - last_prev).total_seconds() / 3600 else: fast_duration = None pre_meal_bg = fasting_readings[-1][1] if fasting_readings else None overnight_mean = statistics.mean(overnight) if overnight else None morning_mean = statistics.mean(morning) if morning else None fast_str = f"{fast_duration:.1f}h fast" if fast_duration else "unknown fast" on_str = f"overnight {overnight_mean:.0f}" if overnight_mean else "" mn_str = f"morning {morning_mean:.0f}" if morning_mean else "" print(f"{date_str}: {fast_str}, pre-meal BG {pre_meal_bg}, {on_str}, {mn_str}") # Overnight trends over the full period print("\n\n" + "="*100) print("OVERNIGHT GLUCOSE TREND (4-6AM WINDOW)") print("="*100) for date_str in sorted(daily.keys()): window = [bg for dt, bg in daily[date_str] if 4 <= dt.hour < 6] if len(window) >= 3: avg = statistics.mean(window) print(f"{date_str}: {avg:.0f}") print("\n\n" + "="*100) print("TOP 10 SPIKES AND TOP 10 BEST-CONTROLLED MEALS") print("="*100) sorted_by_delta = sorted(meal_responses, key=lambda x: x['delta'], reverse=True) print("\nWORST SPIKES:") for i, r in enumerate(sorted_by_delta[:10]): print(f" {i+1}. +{r['delta']} → {r['peak']}: {r['dt'].strftime('%b %d %H:%M')} {r['meal']}") print("\nBEST CONTROLLED:") for i, r in enumerate(sorted_by_delta[-10:]): print(f" {i+1}. +{r['delta']} → {r['peak']}: {r['dt'].strftime('%b %d %H:%M')} {r['meal']}") # Weekly progression print("\n\n" + "="*100) print("WEEKLY PROGRESSION") print("="*100) weeks = defaultdict(list) for dt, bg in cgm: # ISO week week = dt.isocalendar()[1] weeks[week].append(bg) for week in sorted(weeks.keys()): readings = weeks[week] mean = statistics.mean(readings) tir = sum(1 for bg in readings if 70 <= bg <= 140) / len(readings) * 100 print(f"Week {week}: mean {mean:.0f}, TIR {tir:.0f}%, n={len(readings)}")