import gradio as gr import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots import warnings warnings.filterwarnings('ignore') class CGMAnalyzer: def __init__(self): self.data = None self.columns = [] # داده‌های نمونه CGM self.sample_data = [ {'تاریخ': '2024-01-15', 'زمان': '07:30', 'قند_خون': 125, 'روند': 'افزایش_تند', 'وضعیت': 'نرمال', 'فعالیت': 'صبحانه', 'وعده_غذایی': 'صبحانه', 'انسولین': 5, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '120/80', 'دارو': 'متفورمین', 'وزن': 75.2, 'ضربان_قلب': 72, 'مایعات_مصرفی': 250, 'قدم_ها': 320, 'کالری_مصرفی': 280, 'یادداشت': ''}, {'تاریخ': '2024-01-15', 'زمان': '08:00', 'قند_خون': 175, 'روند': 'افزایش_آرام', 'وضعیت': 'بالا', 'فعالیت': 'نشسته', 'وعده_غذایی': 'نداشته', 'انسولین': 0, 'استرس': 'متوسط', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '125/85', 'دارو': 'هیچ', 'وزن': 75.1, 'ضربان_قلب': 78, 'مایعات_مصرفی': 180, 'قدم_ها': 150, 'کالری_مصرفی': 0, 'یادداشت': 'کمی گرسنه'}, {'تاریخ': '2024-01-15', 'زمان': '09:00', 'قند_خون': 152, 'روند': 'کاهش_آرام', 'وضعیت': 'بالا', 'فعالیت': 'کار', 'وعده_غذایی': 'نداشته', 'انسولین': 0, 'استرس': 'زیاد', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '130/90', 'دارو': 'هیچ', 'وزن': 75.0, 'ضربان_قلب': 85, 'مایعات_مصرفی': 200, 'قدم_ها': 450, 'کالری_مصرفی': 0, 'یادداشت': ''}, {'تاریخ': '2024-01-15', 'زمان': '10:00', 'قند_خون': 108, 'روند': 'کاهش_آرام', 'وضعیت': 'نرمال', 'فعالیت': 'کار', 'وعده_غذایی': 'نداشته', 'انسولین': 0, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '118/78', 'دارو': 'هیچ', 'وزن': 74.9, 'ضربان_قلب': 70, 'مایعات_مصرفی': 220, 'قدم_ها': 680, 'کالری_مصرفی': 0, 'یادداشت': ''}, {'تاریخ': '2024-01-15', 'زمان': '12:30', 'قند_خون': 135, 'روند': 'افزایش_تند', 'وضعیت': 'نرمال', 'فعالیت': 'ناهار', 'وعده_غذایی': 'ناهار', 'انسولین': 3, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '115/75', 'دارو': 'هیچ', 'وزن': 74.8, 'ضربان_قلب': 68, 'مایعات_مصرفی': 320, 'قدم_ها': 1250, 'کالری_مصرفی': 450, 'یادداشت': ''}, {'تاریخ': '2024-01-15', 'زمان': '13:00', 'قند_خون': 185, 'روند': 'افزایش_آرام', 'وضعیت': 'بالا', 'فعالیت': 'نشسته', 'وعده_غذایی': 'نداشته', 'انسولین': 0, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '122/82', 'دارو': 'هیچ', 'وزن': 74.7, 'ضربان_قلب': 75, 'مایعات_مصرفی': 180, 'قدم_ها': 80, 'کالری_مصرفی': 0, 'یادداشت': ''}, {'تاریخ': '2024-01-15', 'زمان': '17:00', 'قند_خون': 95, 'روند': 'پایدار', 'وضعیت': 'نرمال', 'فعالیت': 'ورزش', 'وعده_غذایی': 'نداشته', 'انسولین': 0, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '110/70', 'دارو': 'هیچ', 'وزن': 74.5, 'ضربان_قلب': 95, 'مایعات_مصرفی': 400, 'قدم_ها': 5200, 'کالری_مصرفی': 0, 'یادداشت': 'احساس خوب'}, {'تاریخ': '2024-01-15', 'زمان': '19:30', 'قند_خون': 145, 'روند': 'افزایش_تند', 'وضعیت': 'نرمال', 'فعالیت': 'شام', 'وعده_غذایی': 'شام', 'انسولین': 4, 'استرس': 'کم', 'خواب': 'خیر', 'کیفیت_خواب': 'خوب', 'آب_وهوا': 'آفتابی', 'فشار_خون': '125/85', 'دارو': 'هیچ', 'وزن': 74.4, 'ضربان_قلب': 72, 'مایعات_مصرفی': 250, 'قدم_ها': 420, 'کالری_مصرفی': 380, 'یادداشت': ''} ] # سوالات آماده self.diabetic_queries = [ 'قند من معمولاً چقدر است؟', 'چقدر وقت قندم نرمال بوده؟', 'قندم چه ساعاتی بالا می‌رود؟', 'چه ساعاتی قندم نرمال است؟', 'چند بار قندم خیلی پایین شده؟', 'چند بار قندم خیلی بالا رفته؟', 'کدام غذاها قندم را بالا می‌برند؟', 'ورزش چه تاثیری روی قندم دارد؟', 'انسولین چقدر قندم را پایین می‌آورد؟', 'استرس چقدر قندم را بالا می‌برد؟', 'آب‌وهوا چطور روی قندم تاثیر می‌گذارد؟', 'کیفیت خوابم چه تاثیری روی قند دارد؟', 'چقدر ورزش کرده‌ام؟', 'فشار خونم چطوره؟', 'وزنم چطور تغییر کرده؟' ] def get_column_safely(self, column_keywords): """پیدا کردن ستون به صورت ایمن""" for keyword in column_keywords: for col in self.columns: if keyword in col: return col return None def load_sample_data(self): """بارگذاری داده‌های نمونه""" self.data = pd.DataFrame(self.sample_data) self.columns = list(self.data.columns) # نمایش همه داده‌های نمونه (8 رکورد) return self.data, "✅ داده‌های نمونه CGM بارگذاری شد" def upload_file(self, file): """بارگذاری فایل CSV""" if file is None: return None, "❌ لطفاً فایل CSV انتخاب کنید" try: # خواندن فایل CSV self.data = pd.read_csv(file.name, encoding='utf-8') # اگر UTF-8 کار نکرد، encoding های دیگر را امتحان کن if self.data.empty: self.data = pd.read_csv(file.name, encoding='cp1256') self.columns = list(self.data.columns) # تبدیل ستون‌های عددی for col in self.columns: if col in ['قند_خون', 'انسولین'] or 'قند' in col: self.data[col] = pd.to_numeric(self.data[col], errors='coerce') # نمایش تعداد بیشتری از رکوردها (25 به جای 10) return self.data.head(25), f"✅ {len(self.data)} رکورد CGM با موفقیت بارگذاری شد" except Exception as e: return None, f"❌ خطا در خواندن فایل: {str(e)}" def calculate_diabetic_stats(self): """محاسبه آمار دیابت""" if self.data is None or self.data.empty: return None # پیدا کردن ستون قند خون glucose_col = None for col in self.columns: if 'قند' in col or 'glucose' in col.lower(): glucose_col = col break if glucose_col is None: return None glucose_values = self.data[glucose_col].dropna() if len(glucose_values) == 0: return None # محاسبه آمار time_in_range = len(glucose_values[(glucose_values >= 70) & (glucose_values <= 140)]) time_in_range_percent = (time_in_range / len(glucose_values)) * 100 hypo_count = len(glucose_values[glucose_values < 70]) hyper_count = len(glucose_values[glucose_values > 180]) mean_glucose = glucose_values.mean() std_glucose = glucose_values.std() cv = (std_glucose / mean_glucose) * 100 if mean_glucose > 0 else 0 return { 'mean': mean_glucose, 'time_in_range': time_in_range_percent, 'hypo_count': hypo_count, 'hyper_count': hyper_count, 'variability': cv, 'total_readings': len(glucose_values) } def create_glucose_trend_chart(self): """نمودار روند قند خون""" if self.data is None: return None # پیدا کردن ستون‌های مورد نیاز glucose_col = None time_col = None for col in self.columns: if 'قند' in col or 'glucose' in col.lower(): glucose_col = col if 'زمان' in col or 'time' in col.lower(): time_col = col if glucose_col is None: return None # ساخت نمودار fig = go.Figure() if time_col: x_data = self.data[time_col] else: x_data = range(len(self.data)) # اضافه کردن خط قند خون fig.add_trace(go.Scatter( x=x_data, y=self.data[glucose_col], mode='lines+markers', name='قند خون', line=dict(color='blue', width=2), marker=dict(size=6) )) # اضافه کردن خطوط مرجع fig.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="حد پایین (70)") fig.add_hline(y=140, line_dash="dash", line_color="green", annotation_text="حد بالا (140)") fig.add_hline(y=180, line_dash="dash", line_color="orange", annotation_text="خطرناک (180)") # تنظیمات نمودار fig.update_layout( title="روند قند خون در طول زمان", xaxis_title="زمان", yaxis_title="قند خون (mg/dL)", hovermode='x', template="plotly_white" ) return fig def create_daily_pattern_chart(self): """نمودار الگوی روزانه قند خون""" if self.data is None: return None glucose_col = None time_col = None for col in self.columns: if 'قند' in col or 'glucose' in col.lower(): glucose_col = col if 'زمان' in col or 'time' in col.lower(): time_col = col if glucose_col is None or time_col is None: return None # گروه‌بندی بر اساس ساعت self.data['hour'] = self.data[time_col].str.split(':').str[0] hourly_avg = self.data.groupby('hour')[glucose_col].mean().reset_index() fig = go.Figure() fig.add_trace(go.Bar( x=hourly_avg['hour'], y=hourly_avg[glucose_col], name='متوسط قند در هر ساعت', marker_color='lightblue' )) fig.update_layout( title="الگوی روزانه قند خون", xaxis_title="ساعت", yaxis_title="متوسط قند خون (mg/dL)", template="plotly_white" ) return fig def create_meal_impact_chart(self): """نمودار تاثیر وعده‌های غذایی""" if self.data is None: return None glucose_col = None meal_col = None for col in self.columns: if 'قند' in col or 'glucose' in col.lower(): glucose_col = col if 'وعده' in col or 'meal' in col.lower(): meal_col = col if glucose_col is None or meal_col is None: return None meal_avg = self.data.groupby(meal_col)[glucose_col].mean().reset_index() fig = go.Figure() fig.add_trace(go.Pie( labels=meal_avg[meal_col], values=meal_avg[glucose_col], name="تاثیر وعده‌های غذایی" )) fig.update_layout( title="متوسط قند خون در وعده‌های مختلف", template="plotly_white" ) return fig def create_weight_trend_chart(self): """نمودار روند وزن""" if self.data is None: return None weight_col = None date_col = None for col in self.columns: if 'وزن' in col: weight_col = col if 'تاریخ' in col: date_col = col if weight_col is None or date_col is None: return None # گروه‌بندی بر اساس تاریخ و میانگین‌گیری daily_weight = self.data.groupby(date_col)[weight_col].mean().reset_index() fig = go.Figure() fig.add_trace(go.Scatter( x=daily_weight[date_col], y=daily_weight[weight_col], mode='lines+markers', name='وزن روزانه', line=dict(color='purple', width=3), marker=dict(size=8) )) fig.update_layout( title="روند تغییرات وزن", xaxis_title="تاریخ", yaxis_title="وزن (کیلوگرم)", template="plotly_white" ) return fig def create_activity_chart(self): """نمودار فعالیت فیزیکی""" if self.data is None: return None steps_col = None date_col = None for col in self.columns: if 'قدم' in col: steps_col = col if 'تاریخ' in col: date_col = col if steps_col is None or date_col is None: return None # گروه‌بندی بر اساس تاریخ daily_steps = self.data.groupby(date_col)[steps_col].sum().reset_index() fig = go.Figure() fig.add_trace(go.Bar( x=daily_steps[date_col], y=daily_steps[steps_col], name='قدم‌های روزانه', marker_color='lightgreen' )) # اضافه کردن خط مرجع 10,000 قدم fig.add_hline(y=10000, line_dash="dash", line_color="red", annotation_text="هدف روزانه (10,000)") fig.update_layout( title="فعالیت فیزیکی روزانه", xaxis_title="تاریخ", yaxis_title="تعداد قدم", template="plotly_white" ) return fig def create_stress_correlation_chart(self): """نمودار همبستگی استرس و قند""" if self.data is None: return None glucose_col = None stress_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'استرس' in col: stress_col = col if glucose_col is None or stress_col is None: return None # تبدیل سطوح استرس به عدد برای نمودار stress_mapping = {'خیلی_کم': 1, 'کم': 2, 'متوسط': 3, 'زیاد': 4, 'خیلی_زیاد': 5} plot_data = self.data.copy() plot_data['stress_numeric'] = plot_data[stress_col].map(stress_mapping) fig = go.Figure() for stress_level in plot_data[stress_col].unique(): stress_data = plot_data[plot_data[stress_col] == stress_level] fig.add_trace(go.Scatter( x=stress_data['stress_numeric'], y=stress_data[glucose_col], mode='markers', name=f'استرس {stress_level}', marker=dict(size=8, opacity=0.7) )) fig.update_layout( title="رابطه بین سطح استرس و قند خون", xaxis_title="سطح استرس", yaxis_title="قند خون (mg/dL)", xaxis=dict( tickmode='array', tickvals=[1, 2, 3, 4, 5], ticktext=['خیلی کم', 'کم', 'متوسط', 'زیاد', 'خیلی زیاد'] ), template="plotly_white" ) return fig def execute_query(self, query): """اجرای سوالات کاربر""" if self.data is None or self.data.empty: return "❌ هیچ داده‌ای موجود نیست. لطفاً ابتدا فایل بارگذاری کنید یا داده نمونه را بارگذاری کنید." query = query.strip() try: if query == 'قند من معمولاً چقدر است؟': stats = self.calculate_diabetic_stats() if stats: return f"متوسط قند خون شما {stats['mean']:.0f} mg/dL است.\n" + \ ("این عدد خوب است! 🎉" if stats['mean'] < 140 else "بهتر است با پزشک مشورت کنید. 👨‍⚕️" if stats['mean'] > 150 else "در حد قابل قبول است.") elif query == 'چقدر وقت قندم نرمال بوده؟': stats = self.calculate_diabetic_stats() if stats: return f"از {stats['total_readings']} بار اندازه‌گیری، {stats['time_in_range']:.1f}% وقت قند شما در حد نرمال (70-140) بوده است.\n" + \ ("عالی! 🎯" if stats['time_in_range'] > 70 else "نیاز به بهبود دارد. 📈" if stats['time_in_range'] < 50 else "قابل قبول است. ✅") elif query == 'چند بار قندم خیلی پایین شده؟': stats = self.calculate_diabetic_stats() if stats: return f"{stats['hypo_count']} بار قند خون شما زیر 70 رفته است (هیپوگلایسمی).\n" + \ ("عالی! هیچ مورد هیپو نداشتید. 🎉" if stats['hypo_count'] == 0 else "⚠️ مراقب باشید و با پزشک صحبت کنید.") elif query == 'چند بار قندم خیلی بالا رفته؟': stats = self.calculate_diabetic_stats() if stats: return f"{stats['hyper_count']} بار قند خون شما بالای 180 رفته است (هایپرگلایسمی).\n" + \ ("عالی! هیچ مورد هایپر شدید نداشتید. 🎉" if stats['hyper_count'] == 0 else "⚠️ نیاز به کنترل بیشتر دارید.") elif query == 'قندم چه ساعاتی بالا می‌رود؟': glucose_col = None time_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'زمان' in col: time_col = col if glucose_col and time_col: high_glucose = self.data[self.data[glucose_col] > 140] if not high_glucose.empty: high_glucose['hour'] = high_glucose[time_col].str.split(':').str[0] hour_counts = high_glucose['hour'].value_counts().head(3) result = "قند شما بیشتر در این ساعات بالا می‌رود:\n" for hour, count in hour_counts.items(): result += f"ساعت {hour}: {count} بار\n" return result else: return "عالی! قند شما در هیچ ساعتی بالای 140 نرفته است. 🎉" elif query == 'چه ساعاتی قندم نرمال است؟': glucose_col = None time_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'زمان' in col: time_col = col if glucose_col and time_col: normal_glucose = self.data[(self.data[glucose_col] >= 70) & (self.data[glucose_col] <= 140)] if not normal_glucose.empty: normal_glucose['hour'] = normal_glucose[time_col].str.split(':').str[0] hour_counts = normal_glucose['hour'].value_counts().head(5) result = "قند شما بیشتر در این ساعات نرمال است:\n" for hour, count in hour_counts.items(): total_in_hour = len(self.data[self.data[time_col].str.startswith(str(hour))]) percentage = (count / total_in_hour * 100) if total_in_hour > 0 else 0 result += f"ساعت {hour}: {count} بار ({percentage:.0f}%) 🟢\n" return result else: return "متأسفانه در هیچ ساعتی قند شما نرمال نبوده است. با پزشک مشورت کنید. ⚠️" elif query == 'کدام غذاها قندم را بالا می‌برند؟': glucose_col = None meal_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'وعده' in col: meal_col = col if glucose_col and meal_col: meal_avg = self.data.groupby(meal_col)[glucose_col].mean().sort_values(ascending=False) result = "متوسط قند خون بعد از وعده‌های مختلف:\n" for meal, avg in meal_avg.items(): status = "🔴" if avg > 160 else "🟡" if avg > 140 else "🟢" result += f"{status} {meal}: {avg:.0f} mg/dL\n" return result elif query == 'ورزش چه تاثیری روی قندم دارد؟': glucose_col = None activity_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'فعالیت' in col: activity_col = col if glucose_col and activity_col: exercise_data = self.data[self.data[activity_col] == 'ورزش'] if not exercise_data.empty: avg_exercise = exercise_data[glucose_col].mean() non_exercise = self.data[self.data[activity_col] != 'ورزش'] avg_non_exercise = non_exercise[glucose_col].mean() difference = avg_non_exercise - avg_exercise return f"هنگام ورزش متوسط قند شما {avg_exercise:.0f} و در سایر اوقات {avg_non_exercise:.0f} است.\n" + \ f"ورزش {difference:.0f} واحد قند را کم می‌کند. 💪" if difference > 0 else \ "ورزش تاثیر منفی روی قند شما نداشته. 👍" else: return "در این دوره فعالیت ورزشی ثبت نشده است." elif query == 'انسولین چقدر قندم را پایین می‌آورد؟': glucose_col = None insulin_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'انسولین' in col: insulin_col = col if glucose_col and insulin_col: # مقایسه قند قبل و بعد از انسولین insulin_data = self.data[self.data[insulin_col] > 0] no_insulin_data = self.data[self.data[insulin_col] == 0] if not insulin_data.empty and not no_insulin_data.empty: avg_with_insulin = insulin_data[glucose_col].mean() avg_without_insulin = no_insulin_data[glucose_col].mean() # محاسبه متوسط انسولین مصرفی avg_insulin_dose = insulin_data[insulin_col].mean() # تاثیر هر واحد انسولین effect_per_unit = (avg_without_insulin - avg_with_insulin) / avg_insulin_dose if avg_insulin_dose > 0 else 0 result = f"تحلیل تاثیر انسولین:\n" result += f"• متوسط قند با انسولین: {avg_with_insulin:.0f} mg/dL\n" result += f"• متوسط قند بدون انسولین: {avg_without_insulin:.0f} mg/dL\n" result += f"• متوسط دوز انسولین: {avg_insulin_dose:.1f} واحد\n" result += f"• هر واحد انسولین تقریباً {effect_per_unit:.0f} واحد قند را کم می‌کند 💉\n" if effect_per_unit > 0: result += "انسولین شما خوب کار می‌کند! ✅" else: result += "ممکن است نیاز به تنظیم دوز داشته باشید. با پزشک مشورت کنید. ⚠️" return result else: return "داده کافی برای تحلیل تاثیر انسولین موجود نیست." elif query == 'استرس چقدر قندم را بالا می‌برد؟': glucose_col = None stress_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'استرس' in col: stress_col = col if glucose_col and stress_col: stress_levels = ['کم', 'متوسط', 'زیاد'] result = "تاثیر سطح استرس روی قند خون:\n" stress_analysis = {} for stress_level in stress_levels: stress_data = self.data[self.data[stress_col] == stress_level] if not stress_data.empty: avg_glucose = stress_data[glucose_col].mean() count = len(stress_data) stress_analysis[stress_level] = {'avg': avg_glucose, 'count': count} if stress_analysis: for stress_level in stress_levels: if stress_level in stress_analysis: data = stress_analysis[stress_level] emoji = "🟢" if data['avg'] < 140 else "🟡" if data['avg'] < 160 else "🔴" result += f"{emoji} استرس {stress_level}: {data['avg']:.0f} mg/dL ({data['count']} بار)\n" # محاسبه تفاوت بین کم‌ترین و بیشترین استرس if 'کم' in stress_analysis and 'زیاد' in stress_analysis: difference = stress_analysis['زیاد']['avg'] - stress_analysis['کم']['avg'] result += f"\n💡 استرس زیاد {difference:.0f} واحد قند را نسبت به استرس کم بالا می‌برد.\n" if difference > 30: result += "استرس تاثیر زیادی روی قند شما دارد. تکنیک‌های آرامش‌بخشی را امتحان کنید. 🧘‍♀️" elif difference > 15: result += "استرس تاثیر متوسطی روی قند شما دارد." else: result += "خوشبختانه استرس تاثیر کمی روی قند شما دارد. 👍" return result else: return "داده کافی برای تحلیل تاثیر استرس موجود نیست." elif query == 'آب‌وهوا چطور روی قندم تاثیر می‌گذارد؟': glucose_col = None weather_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'آب_وهوا' in col or 'آب‌وهوا' in col: weather_col = col if glucose_col and weather_col: weather_analysis = self.data.groupby(weather_col)[glucose_col].agg(['mean', 'count']).reset_index() result = "تاثیر آب‌وهوا روی قند خون:\n" for _, row in weather_analysis.iterrows(): weather = row[weather_col] avg_glucose = row['mean'] count = row['count'] emoji = "🟢" if avg_glucose < 140 else "🟡" if avg_glucose < 160 else "🔴" result += f"{emoji} {weather}: {avg_glucose:.0f} mg/dL ({count} بار)\n" # یافتن بهترین و بدترین آب‌وهوا best_weather = weather_analysis.loc[weather_analysis['mean'].idxmin(), weather_col] worst_weather = weather_analysis.loc[weather_analysis['mean'].idxmax(), weather_col] result += f"\n🌟 بهترین آب‌وهوا برای قند شما: {best_weather}\n" result += f"⚠️ بدترین آب‌وهوا برای قند شما: {worst_weather}" return result else: return "داده آب‌وهوا در فایل شما موجود نیست." elif query == 'کیفیت خوابم چه تاثیری روی قند دارد؟': glucose_col = None sleep_col = None for col in self.columns: if 'قند' in col: glucose_col = col if 'کیفیت_خواب' in col: sleep_col = col if glucose_col and sleep_col: sleep_analysis = self.data.groupby(sleep_col)[glucose_col].agg(['mean', 'count']).reset_index() result = "تاثیر کیفیت خواب روی قند خون:\n" sleep_order = {'عالی': 1, 'خوب': 2, 'متوسط': 3, 'بد': 4, 'خیلی_بد': 5} sleep_analysis['order'] = sleep_analysis[sleep_col].map(sleep_order) sleep_analysis = sleep_analysis.sort_values('order') for _, row in sleep_analysis.iterrows(): sleep_quality = row[sleep_col] avg_glucose = row['mean'] count = row['count'] emoji = "🟢" if avg_glucose < 140 else "🟡" if avg_glucose < 160 else "🔴" result += f"{emoji} خواب {sleep_quality}: {avg_glucose:.0f} mg/dL ({count} بار)\n" # محاسبه تفاوت بین بهترین و بدترین خواب best_sleep = sleep_analysis.iloc[0] worst_sleep = sleep_analysis.iloc[-1] difference = worst_sleep['mean'] - best_sleep['mean'] result += f"\n💤 خواب بد {difference:.0f} واحد قند را بالاتر از خواب خوب می‌برد.\n" if difference > 20: result += "کیفیت خواب تاثیر زیادی روی قند شما دارد. بهبود خواب اولویت باشد! 😴" else: result += "کیفیت خواب تاثیر قابل قبولی روی قند شما دارد. 👍" return result else: return "داده کیفیت خواب در فایل شما موجود نیست." elif query == 'چقدر ورزش کرده‌ام؟': steps_col = None activity_col = None for col in self.columns: if 'قدم' in col: steps_col = col if 'فعالیت' in col: activity_col = col if steps_col: total_steps = self.data[steps_col].sum() avg_daily_steps = total_steps / len(self.data.groupby('تاریخ')) exercise_days = len(self.data[self.data[activity_col] == 'ورزش']) if activity_col else 0 result = f"آمار فعالیت فیزیکی شما:\n" result += f"👟 کل قدم‌ها: {total_steps:,} قدم\n" result += f"📊 متوسط روزانه: {avg_daily_steps:.0f} قدم\n" result += f"💪 روزهای ورزش: {exercise_days} روز\n\n" if avg_daily_steps >= 10000: result += "عالی! شما به هدف 10,000 قدم روزانه رسیده‌اید! 🎯" elif avg_daily_steps >= 7000: result += "خوب است! کمی بیشتر قدم بزنید تا به 10,000 برسید. 📈" else: result += "نیاز به فعالیت بیشتر دارید. سعی کنید بیشتر قدم بزنید. 🚶‍♂️" return result else: return "داده قدم‌ها در فایل شما موجود نیست." elif query == 'فشار خونم چطوره؟': bp_col = None for col in self.columns: if 'فشار' in col: bp_col = col break if bp_col: bp_values = self.data[bp_col].dropna() if not bp_values.empty: # تحلیل فشار خون high_count = 0 normal_count = 0 low_count = 0 for bp in bp_values: try: systolic = int(bp.split('/')[0]) if systolic >= 140: high_count += 1 elif systolic >= 120: normal_count += 1 else: low_count += 1 except: continue total = high_count + normal_count + low_count result = f"آمار فشار خون شما:\n" result += f"🔴 بالا (≥140): {high_count} بار ({high_count/total*100:.0f}%)\n" result += f"🟡 نرمال (120-139): {normal_count} بار ({normal_count/total*100:.0f}%)\n" result += f"🟢 ایده‌آل (<120): {low_count} بار ({low_count/total*100:.0f}%)\n\n" if high_count/total > 0.3: result += "⚠️ فشار خون شما اغلب بالا است. با پزشک مشورت کنید." elif normal_count/total > 0.7: result += "✅ فشار خون شما در حد قابل قبول است." else: result += "👍 فشار خون شما در حد ایده‌آل است." return result else: return "داده فشار خون کافی نیست." else: return "داده فشار خون در فایل شما موجود نیست." elif query == 'وزنم چطور تغییر کرده؟': weight_col = None for col in self.columns: if 'وزن' in col: weight_col = col break if weight_col: weights = self.data[weight_col].dropna() if len(weights) > 1: first_weight = weights.iloc[0] last_weight = weights.iloc[-1] weight_change = last_weight - first_weight avg_weight = weights.mean() result = f"تغییرات وزن شما:\n" result += f"⚖️ وزن اولیه: {first_weight} کیلو\n" result += f"⚖️ وزن فعلی: {last_weight} کیلو\n" result += f"📊 متوسط وزن: {avg_weight:.1f} کیلو\n" result += f"📈 تغییر کلی: {weight_change:+.1f} کیلو\n\n" if abs(weight_change) < 1: result += "👍 وزن شما ثابت مانده است." elif weight_change > 0: result += f"📈 {weight_change:.1f} کیلو افزایش وزن داشته‌اید." else: result += f"📉 {abs(weight_change):.1f} کیلو کاهش وزن داشته‌اید." return result else: return "داده وزن کافی برای تحلیل تغییرات نیست." else: return "داده وزن در فایل شما موجود نیست." else: return "متوجه سوال شما نشدم. لطفاً یکی از سوالات آماده را انتخاب کنید." except Exception as e: return f"❌ مشکلی پیش آمده: {str(e)}" def get_data_summary(self): """خلاصه داده‌ها""" if self.data is None: return "هیچ داده‌ای بارگذاری نشده است." # محاسبه دوره زمانی unique_dates = self.data['تاریخ'].nunique() if 'تاریخ' in self.columns else 1 summary = f"📊 تعداد رکوردها: {len(self.data)}\n" summary += f"📅 دوره زمانی: {unique_dates} روز\n" summary += f"🏷️ تعداد ستون‌ها: {len(self.columns)}\n" # آمار قند خون stats = self.calculate_diabetic_stats() if stats: summary += f"\n🩺 آمار قند خون:\n" summary += f"• متوسط قند: {stats['mean']:.0f} mg/dL\n" summary += f"• زمان در محدوده: {stats['time_in_range']:.1f}%\n" summary += f"• تعداد هیپو: {stats['hypo_count']}\n" summary += f"• تعداد هایپر: {stats['hyper_count']}\n" summary += f"• تنوع گلایسمی: {stats['variability']:.1f}%\n" # آمار فعالیت فیزیکی if 'قدم_ها' in self.columns: total_steps = self.data['قدم_ها'].sum() avg_daily_steps = total_steps / unique_dates summary += f"\n👟 فعالیت فیزیکی:\n" summary += f"• کل قدم‌ها: {total_steps:,}\n" summary += f"• متوسط روزانه: {avg_daily_steps:.0f} قدم\n" # آمار انسولین if 'انسولین' in self.columns: insulin_doses = self.data[self.data['انسولین'] > 0]['انسولین'] if not insulin_doses.empty: summary += f"\n💉 انسولین:\n" summary += f"• تعداد دوزها: {len(insulin_doses)}\n" summary += f"• متوسط دوز: {insulin_doses.mean():.1f} واحد\n" # آمار وزن if 'وزن' in self.columns: weights = self.data['وزن'].dropna() if len(weights) > 1: weight_change = weights.iloc[-1] - weights.iloc[0] summary += f"\n⚖️ وزن:\n" summary += f"• وزن فعلی: {weights.iloc[-1]} کیلو\n" summary += f"• تغییر: {weight_change:+.1f} کیلو\n" return summary # ساخت اپلیکیشن Gradio def create_app(): analyzer = CGMAnalyzer() # CSS برای RTL و استایل بهتر custom_css = """ .rtl { direction: rtl; text-align: right; } .main-title { text-align: center; color: #d63384; font-size: 28px; font-weight: bold; margin: 20px 0; } .subtitle { text-align: center; color: #6c757d; margin-bottom: 30px; font-size: 16px; } .section-title { color: #6f42c1; font-weight: bold; margin: 20px 0 10px 0; } .gradio-container { font-family: 'Segoe UI', Tahoma, Arial, sans-serif; } /* بهبود نمایش جدول */ .dataframe { font-size: 12px !important; width: 100% !important; overflow-x: auto !important; max-height: 500px !important; overflow-y: auto !important; } .dataframe table { table-layout: fixed !important; width: 100% !important; border-collapse: collapse !important; } .dataframe th { background-color: #f8f9fa !important; font-weight: bold !important; padding: 8px 4px !important; text-align: center !important; border: 1px solid #dee2e6 !important; position: sticky !important; top: 0 !important; z-index: 10 !important; } .dataframe td { padding: 6px 4px !important; text-align: center !important; border: 1px solid #dee2e6 !important; word-wrap: break-word !important; overflow: hidden !important; } /* ستون‌های خاص با عرض ثابت */ .dataframe th:nth-child(1), .dataframe td:nth-child(1) { width: 100px !important; } /* تاریخ */ .dataframe th:nth-child(2), .dataframe td:nth-child(2) { width: 70px !important; } /* زمان */ .dataframe th:nth-child(3), .dataframe td:nth-child(3) { width: 80px !important; } /* قند_خون */ .dataframe th:nth-child(4), .dataframe td:nth-child(4) { width: 100px !important; } /* روند */ .dataframe th:nth-child(5), .dataframe td:nth-child(5) { width: 80px !important; } /* وضعیت */ .dataframe th:nth-child(6), .dataframe td:nth-child(6) { width: 90px !important; } /* فعالیت */ .dataframe th:nth-child(7), .dataframe td:nth-child(7) { width: 100px !important; } /* وعده_غذایی */ .dataframe th:nth-child(8), .dataframe td:nth-child(8) { width: 70px !important; } /* انسولین */ .dataframe th:nth-child(9), .dataframe td:nth-child(9) { width: 80px !important; } /* استرس */ .dataframe th:nth-child(10), .dataframe td:nth-child(10) { width: 60px !important; } /* خواب */ .dataframe th:nth-child(11), .dataframe td:nth-child(11) { width: 90px !important; } /* کیفیت_خواب */ .dataframe th:nth-child(12), .dataframe td:nth-child(12) { width: 80px !important; } /* آب_وهوا */ .dataframe th:nth-child(13), .dataframe td:nth-child(13) { width: 90px !important; } /* فشار_خون */ .dataframe th:nth-child(14), .dataframe td:nth-child(14) { width: 90px !important; } /* دارو */ .dataframe th:nth-child(15), .dataframe td:nth-child(15) { width: 60px !important; } /* وزن */ .dataframe th:nth-child(16), .dataframe td:nth-child(16) { width: 80px !important; } /* ضربان_قلب */ .dataframe th:nth-child(17), .dataframe td:nth-child(17) { width: 90px !important; } /* مایعات_مصرفی */ .dataframe th:nth-child(18), .dataframe td:nth-child(18) { width: 80px !important; } /* قدم_ها */ .dataframe th:nth-child(19), .dataframe td:nth-child(19) { width: 90px !important; } /* کالری_مصرفی */ .dataframe th:nth-child(20), .dataframe td:nth-child(20) { width: 120px !important; } /* یادداشت */ /* اسکرول بار بهتر */ .dataframe::-webkit-scrollbar { width: 8px; height: 8px; } .dataframe::-webkit-scrollbar-track { background: #f1f1f1; } .dataframe::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; } .dataframe::-webkit-scrollbar-thumb:hover { background: #555; } """ with gr.Blocks(title="تحلیل‌گر داده‌های CGM", theme=gr.themes.Soft(), css=custom_css) as app: gr.HTML('
🩺 تحلیل‌گر داده‌های CGM
') gr.HTML('
مانیتورینگ مداوم قند خون • ابزار تحلیل هوشمند برای بیماران دیابتی
') with gr.Tab("📂 بارگذاری داده‌ها"): with gr.Row(): with gr.Column(): file_input = gr.File( label="انتخاب فایل CSV", file_types=[".csv"], elem_classes=["rtl"] ) sample_btn = gr.Button("🔄 بارگذاری داده نمونه", variant="secondary") upload_status = gr.Textbox( label="وضعیت بارگذاری", interactive=False, elem_classes=["rtl"] ) with gr.Column(): data_summary = gr.Textbox( label="خلاصه داده‌ها", lines=8, interactive=False, elem_classes=["rtl"] ) # دکمه‌های کنترل نمایش داده‌ها with gr.Row(): more_data_btn = gr.Button("📊 نمایش 50 رکورد اول", variant="secondary") all_data_btn = gr.Button("📋 نمایش همه داده‌ها", variant="secondary") gr.HTML('
💡 نکته: برای مشاهده داده‌های بیشتر از دکمه‌های بالا استفاده کنید
') data_preview = gr.Dataframe( label="نمایش داده‌ها", wrap=True, elem_classes=["rtl"] ) with gr.Tab("🤔 سوالات آماده"): gr.HTML('
سوالات رایج از قند خون - روی هر دکمه کلیک کنید:
') with gr.Row(): with gr.Column(): query_buttons = [] for i, query in enumerate(analyzer.diabetic_queries): btn = gr.Button(query, elem_classes=["rtl"]) query_buttons.append(btn) query_result = gr.Textbox( label="پاسخ سوال", lines=5, interactive=False, elem_classes=["rtl"] ) with gr.Tab("💬 سوال دلخواه"): with gr.Row(): custom_query = gr.Textbox( label="سوال خود را بنویسید", placeholder="مثال: قند من معمولاً چقدر است؟", elem_classes=["rtl"] ) ask_btn = gr.Button("🔍 پرسش", variant="primary") custom_result = gr.Textbox( label="پاسخ", lines=5, interactive=False, elem_classes=["rtl"] ) with gr.Tab("📈 نمودارها"): gr.HTML('
نمودارهای تحلیل داده‌ها
') with gr.Row(): trend_btn = gr.Button("📊 روند قند خون") pattern_btn = gr.Button("🕐 الگوی روزانه") meal_btn = gr.Button("🍽️ تاثیر وعده‌ها") with gr.Row(): weight_btn = gr.Button("⚖️ روند وزن") activity_btn = gr.Button("👟 فعالیت فیزیکی") stress_btn = gr.Button("😰 استرس و قند") chart_output = gr.Plot(label="نمودار") with gr.Tab("💡 راهنما"): gr.HTML("""

💡 راهنمای استفاده:

📊 محدوده‌های قند خون:

🆕 ویژگی‌های جدید:

🏥 نکات مهم:

📋 نمونه فرمت CSV:

تاریخ,زمان,قند_خون,روند,وضعیت,فعالیت,وعده_غذایی,انسولین,استرس,خواب,کیفیت_خواب,آب_وهوا,فشار_خون,دارو,وزن,ضربان_قلب,مایعات_مصرفی,قدم_ها,کالری_مصرفی,یادداشت
2024-01-15,07:30,125,افزایش_تند,نرمال,صبحانه,صبحانه,5,کم,خیر,خوب,آفتابی,120/80,متفورمین,75.2,72,250,320,280,
                

🔒 حریم خصوصی:

داده‌های شما محلی پردازش می‌شوند و ذخیره نمی‌شوند.

""") # Event handlers def upload_and_preview(file): data, status = analyzer.upload_file(file) summary = analyzer.get_data_summary() return data, status, summary def load_sample_and_preview(): data, status = analyzer.load_sample_data() summary = analyzer.get_data_summary() return data, status, summary def show_more_data(): """نمایش داده‌های بیشتر""" if analyzer.data is not None: # نمایش 50 رکورد اول return analyzer.data.head(50) return None def show_all_data(): """نمایش همه داده‌ها""" if analyzer.data is not None: return analyzer.data return None # اتصال رویدادها file_input.upload(upload_and_preview, inputs=file_input, outputs=[data_preview, upload_status, data_summary]) sample_btn.click(load_sample_and_preview, outputs=[data_preview, upload_status, data_summary]) # دکمه‌های نمایش داده‌های بیشتر more_data_btn.click(show_more_data, outputs=data_preview) all_data_btn.click(show_all_data, outputs=data_preview) # اتصال دکمه‌های سوالات آماده for i, btn in enumerate(query_buttons): btn.click( lambda q=analyzer.diabetic_queries[i]: analyzer.execute_query(q), outputs=query_result ) # سوال دلخواه ask_btn.click(analyzer.execute_query, inputs=custom_query, outputs=custom_result) custom_query.submit(analyzer.execute_query, inputs=custom_query, outputs=custom_result) # نمودارها trend_btn.click(analyzer.create_glucose_trend_chart, outputs=chart_output) pattern_btn.click(analyzer.create_daily_pattern_chart, outputs=chart_output) meal_btn.click(analyzer.create_meal_impact_chart, outputs=chart_output) weight_btn.click(analyzer.create_weight_trend_chart, outputs=chart_output) activity_btn.click(analyzer.create_activity_chart, outputs=chart_output) stress_btn.click(analyzer.create_stress_correlation_chart, outputs=chart_output) return app # برای Hugging Face Spaces if __name__ == "__main__": app = create_app() app.launch()