leilaghomashchi's picture
upload
8470ba0 verified
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('<div class="main-title">🩺 تحلیل‌گر داده‌های CGM</div>')
gr.HTML('<div class="subtitle">مانیتورینگ مداوم قند خون • ابزار تحلیل هوشمند برای بیماران دیابتی</div>')
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('<div style="color: #6c757d; font-size: 14px; padding: 10px;">💡 نکته: برای مشاهده داده‌های بیشتر از دکمه‌های بالا استفاده کنید</div>')
data_preview = gr.Dataframe(
label="نمایش داده‌ها",
wrap=True,
elem_classes=["rtl"]
)
with gr.Tab("🤔 سوالات آماده"):
gr.HTML('<div class="section-title">سوالات رایج از قند خون - روی هر دکمه کلیک کنید:</div>')
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('<div class="section-title">نمودارهای تحلیل داده‌ها</div>')
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("""
<div class="rtl" style="padding: 20px; font-family: Tahoma;">
<h3>💡 راهنمای استفاده:</h3>
<ul style="line-height: 1.8;">
<li><strong>بارگذاری داده‌ها:</strong> فایل CSV حاوی ستون‌های زیر:
<br>• اجباری: تاریخ، زمان، قند_خون، وضعیت، فعالیت، وعده_غذایی، انسولین، استرس
<br>• اختیاری: کیفیت_خواب، آب_وهوا، فشار_خون، دارو، وزن، ضربان_قلب، مایعات_مصرفی، قدم_ها، کالری_مصرفی، یادداشت
</li>
<li><strong>سوالات آماده:</strong> روی هر سوال کلیک کنید تا پاسخ را ببینید</li>
<li><strong>سوال دلخواه:</strong> سوال خود را با کلمات ساده بنویسید</li>
<li><strong>نمودارها:</strong> تحلیل بصری داده‌های شما شامل 6 نوع نمودار مختلف</li>
</ul>
<h3>📊 محدوده‌های قند خون:</h3>
<ul style="line-height: 1.8;">
<li><span style="color: red; font-weight: bold;">🔴 خطرناک:</span> کمتر از 70 (فوری به پزشک مراجعه کنید)</li>
<li><span style="color: green; font-weight: bold;">🟢 خوب:</span> 70 تا 140 (عالی است)</li>
<li><span style="color: orange; font-weight: bold;">🟡 بالا:</span> بیشتر از 140 (بهتر است کنترل کنید)</li>
<li><span style="color: red; font-weight: bold;">🔴 خیلی بالا:</span> بیشتر از 180 (حتماً با پزشک صحبت کنید)</li>
</ul>
<h3>🆕 ویژگی‌های جدید:</h3>
<ul style="line-height: 1.8;">
<li><strong>تحلیل کیفیت خواب:</strong> بررسی تاثیر خواب روی قند خون</li>
<li><strong>تاثیر آب‌وهوا:</strong> نحوه تاثیر شرایط جوی بر کنترل قند</li>
<li><strong>ردیابی وزن:</strong> پیگیری تغییرات وزن در طول زمان</li>
<li><strong>فعالیت فیزیکی:</strong> تحلیل میزان قدم‌زدن و ورزش</li>
<li><strong>مانیتورینگ فشار خون:</strong> کنترل همزمان فشار خون</li>
<li><strong>مدیریت دارو:</strong> پیگیری مصرف داروها</li>
</ul>
<h3>🏥 نکات مهم:</h3>
<ul style="line-height: 1.8;">
<li><strong>هدف:</strong> بیش از 70% وقت قند در محدوده نرمال باشد</li>
<li><strong>هدف روزانه:</strong> حداقل 10,000 قدم در روز</li>
<li>اگر قند زیر 70 رفت، فوری شکر یا آبمیوه بخورید</li>
<li>اگر قند بالای 300 رفت، فوری به بیمارستان بروید</li>
<li><strong>کیفیت خواب:</strong> 7-8 ساعت خواب باکیفیت ضروری است</li>
<li><strong>مدیریت استرس:</strong> تکنیک‌های آرامش‌بخشی یاد بگیرید</li>
<li><strong style="color: red;">این برنامه جایگزین مشاوره پزشک نیست</strong></li>
</ul>
<h3>📋 نمونه فرمت CSV:</h3>
<pre style="background: #f8f9fa; padding: 10px; border-radius: 5px; font-size: 12px;">
تاریخ,زمان,قند_خون,روند,وضعیت,فعالیت,وعده_غذایی,انسولین,استرس,خواب,کیفیت_خواب,آب_وهوا,فشار_خون,دارو,وزن,ضربان_قلب,مایعات_مصرفی,قدم_ها,کالری_مصرفی,یادداشت
2024-01-15,07:30,125,افزایش_تند,نرمال,صبحانه,صبحانه,5,کم,خیر,خوب,آفتابی,120/80,متفورمین,75.2,72,250,320,280,
</pre>
<h3>🔒 حریم خصوصی:</h3>
<p>داده‌های شما محلی پردازش می‌شوند و ذخیره نمی‌شوند.</p>
</div>
""")
# 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()