Spaces:
Sleeping
Sleeping
| import os | |
| import pandas as pd | |
| import numpy as np | |
| from typing import Dict, Tuple, Optional | |
| from pathlib import Path | |
| from datetime import datetime, timedelta | |
| # Prefer HF router via OpenAI-compatible client. Use env `HF_TOKEN`. | |
| # HF_TOKEN loaded lazily to allow dotenv loading after import | |
| def get_hf_token(): | |
| return os.environ.get('HF_TOKEN') | |
| def openai_summary(text: str, verbosity: str = 'brief', model: str = 'meta-llama/Llama-3.1-8B-Instruct:novita') -> str: | |
| HF_TOKEN = get_hf_token() | |
| if not HF_TOKEN: | |
| return None | |
| try: | |
| # Import here to avoid requiring OpenAI client unless HF_TOKEN set | |
| from openai import OpenAI | |
| client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=HF_TOKEN) | |
| if verbosity == 'analyze': | |
| instruction = 'วิเคราะห์สาเหตุไฟฟ้าจากข้อมูลนี้ สรุปไม่เกิน 3-4 บรรทัด (ไทย) ระบุสาเหตุทางเทคนิค ผลกระทบต่อลูกค้าและระบบ และช่วงเวลา:' | |
| elif verbosity == 'recommend': | |
| instruction = 'วิเคราะห์สาเหตุไฟฟ้าจากข้อมูลนี้ พร้อมแนะนำการแก้ไข สรุปไม่เกิน 3-4 บรรทัด (ไทย) ระบุสาเหตุทางเทคนิค ผลกระทบต่อลูกค้าและระบบ ช่วงเวลาและข้อเสนอแนะในการป้องกัน:' | |
| prompt = f"{instruction}\n\n{text}\n\nสรุป:" | |
| completion = client.chat.completions.create( | |
| model=model, | |
| messages=[{"role": "user", "content": prompt}], | |
| max_tokens=1000, | |
| ) | |
| # Extract text from response | |
| choice = completion.choices[0] | |
| msg = choice.message | |
| content = msg.content | |
| return content.strip() if content else None | |
| except Exception: | |
| return None | |
| def calculate_outage_duration_minutes(df: pd.DataFrame) -> pd.Series: | |
| """ | |
| คำนวณระยะเวลาการขัดข้องเป็นนาที | |
| """ | |
| durations = pd.Series(index=df.index, dtype='float64') | |
| # ลองใช้คอลัมน์ต่างๆ สำหรับคำนวณระยะเวลา | |
| if 'OutageDateTime' in df.columns and 'FirstRestoDateTime' in df.columns: | |
| outage_time = pd.to_datetime(df['OutageDateTime'], dayfirst=True, errors='coerce') | |
| restore_time = pd.to_datetime(df['FirstRestoDateTime'], dayfirst=True, errors='coerce') | |
| durations = (restore_time - outage_time).dt.total_seconds() / 60 | |
| elif 'OutageDateTime' in df.columns and 'LastRestoDateTime' in df.columns: | |
| outage_time = pd.to_datetime(df['OutageDateTime'], dayfirst=True, errors='coerce') | |
| restore_time = pd.to_datetime(df['LastRestoDateTime'], dayfirst=True, errors='coerce') | |
| durations = (restore_time - outage_time).dt.total_seconds() / 60 | |
| elif 'CreateEventDateTime' in df.columns and 'CloseEventDateTime' in df.columns: | |
| create_time = pd.to_datetime(df['CreateEventDateTime'], dayfirst=True, errors='coerce') | |
| close_time = pd.to_datetime(df['CloseEventDateTime'], dayfirst=True, errors='coerce') | |
| durations = (close_time - create_time).dt.total_seconds() / 60 | |
| # แทนที่ค่าลบหรือ NaN ด้วย 0 | |
| durations = durations.fillna(0) | |
| durations = durations.where(durations >= 0, 0) | |
| return durations | |
| def compute_reliability_metrics(df: pd.DataFrame, total_customers: float) -> Dict: | |
| """ | |
| คำนวณตัวชี้วัดความน่าเชื่อถือของระบบไฟฟ้า | |
| สูตรการคำนวณ: | |
| SAIFI = Σ(λᵢ × Nᵢ) / Nₜ | |
| SAIDI = Σ(Uᵢ × Nᵢ) / Nₜ | |
| CAIDI = SAIDI / SAIFI | |
| MAIFI = Σ(λₘᵢ × Nᵢ) / Nₜ | |
| CTAIDI = Σ(rᵢ × Nᵢ × λᵢ) / Σ(Nᵢ × λᵢ) | |
| ASAI = (Nₜ × T - Σ(rᵢ × Nᵢ)) / (Nₜ × T) | |
| โดยที่: | |
| λᵢ = ความถี่การขัดข้องของจุดโหลด i | |
| Nᵢ = จำนวนลูกค้าที่ได้รับผลกระทบในจุดโหลด i | |
| Nₜ = จำนวนลูกค้าทั้งหมดในระบบ | |
| Uᵢ = ระยะเวลาขัดข้องรายปีของจุดโหลด i (นาที) | |
| rᵢ = ระยะเวลาขัดข้องเฉลี่ยต่อครั้งของจุดโหลด i (นาที) | |
| λₘᵢ = ความถี่การขัดข้องชั่วคราว (< 1 นาที) | |
| T = ระยะเวลารวม (8760 ชั่วโมง = 525,600 นาที) | |
| """ | |
| # เตรียมข้อมูล | |
| df_clean = df.copy() | |
| # คำนวณระยะเวลาขัดข้อง | |
| df_clean['duration_minutes'] = calculate_outage_duration_minutes(df_clean) | |
| # จำนวนลูกค้าที่ได้รับผลกระทบ | |
| if 'AffectedCustomer' in df_clean.columns: | |
| df_clean['affected_customers'] = pd.to_numeric(df_clean['AffectedCustomer'], errors='coerce').fillna(0) | |
| else: | |
| df_clean['affected_customers'] = 0 | |
| # แยกประเภทการขัดข้อง | |
| df_clean['is_momentary'] = df_clean['duration_minutes'] < 1 # ขัดข้องชั่วคราว < 1 นาที | |
| df_clean['is_sustained'] = df_clean['duration_minutes'] >= 1 # ขัดข้องยาวนาน >= 1 นาที | |
| # คำนวณตัวชี้วัด | |
| # 1. SAIFI - System Average Interruption Frequency Index | |
| # SAIFI = Σ(λᵢ × Nᵢ) / Nₜ | |
| sustained_interruptions = df_clean[df_clean['is_sustained']] | |
| total_customer_interruptions = sustained_interruptions['affected_customers'].sum() | |
| saifi = total_customer_interruptions / total_customers if total_customers > 0 else 0 | |
| # 2. SAIDI - System Average Interruption Duration Index | |
| # SAIDI = Σ(Uᵢ × Nᵢ) / Nₜ | |
| customer_minutes = (sustained_interruptions['duration_minutes'] * sustained_interruptions['affected_customers']).sum() | |
| saidi = customer_minutes / total_customers if total_customers > 0 else 0 | |
| # 3. CAIDI - Customer Average Interruption Duration Index | |
| # CAIDI = SAIDI / SAIFI | |
| caidi = saidi / saifi if saifi > 0 else 0 | |
| # 4. MAIFI - Momentary Average Interruption Frequency Index | |
| # MAIFI = Σ(λₘᵢ × Nᵢ) / Nₜ | |
| momentary_interruptions = df_clean[df_clean['is_momentary']] | |
| total_momentary_customer_interruptions = momentary_interruptions['affected_customers'].sum() | |
| maifi = total_momentary_customer_interruptions / total_customers if total_customers > 0 else 0 | |
| # 5. CTAIDI - Customer Total Average Interruption Duration Index | |
| # CTAIDI = Σ(rᵢ × Nᵢ × λᵢ) / Σ(Nᵢ × λᵢ) | |
| total_customer_duration = (df_clean['duration_minutes'] * df_clean['affected_customers']).sum() | |
| total_customer_events = df_clean['affected_customers'].sum() | |
| ctaidi = total_customer_duration / total_customer_events if total_customer_events > 0 else 0 | |
| # 6. ASAI - Average Service Availability Index | |
| # ASAI = (Nₜ × T - Σ(rᵢ × Nᵢ)) / (Nₜ × T) | |
| total_minutes_per_year = 525600 # 365 × 24 × 60 | |
| total_available_customer_minutes = total_customers * total_minutes_per_year | |
| total_unavailable_customer_minutes = customer_minutes | |
| asai = (total_available_customer_minutes - total_unavailable_customer_minutes) / total_available_customer_minutes if total_available_customer_minutes > 0 else 0 | |
| asai_percent = asai * 100 | |
| # 7. ASUI - Average Service Unavailability Index | |
| asui = (1 - asai) * 100 | |
| # 8. ENS - Energy Not Supplied (ประมาณการ) | |
| # สมมติว่าลูกค้าแต่ละรายใช้ไฟฟ้าเฉลี่ย 3 kW | |
| average_demand_kw = 3.0 | |
| ens_kwh = (total_unavailable_customer_minutes / 60) * average_demand_kw | |
| # 9. AENS - Average Energy Not Supplied per Customer | |
| aens = ens_kwh / total_customers if total_customers > 0 else 0 | |
| # สถิติเพิ่มเติม | |
| total_events = len(df_clean) | |
| total_sustained_events = len(sustained_interruptions) | |
| total_momentary_events = len(momentary_interruptions) | |
| avg_duration_per_event = df_clean['duration_minutes'].mean() | |
| max_duration = df_clean['duration_minutes'].max() | |
| total_affected = df_clean['affected_customers'].sum() | |
| return { | |
| # Primary Reliability Indices | |
| 'SAIFI': round(saifi, 4), | |
| 'SAIDI': round(saidi, 2), | |
| 'CAIDI': round(caidi, 2), | |
| 'MAIFI': round(maifi, 4), | |
| # Additional Indices | |
| 'CTAIDI': round(ctaidi, 2), | |
| 'ASAI': round(asai, 6), | |
| 'ASAI_percent': round(asai_percent, 4), | |
| 'ASUI_percent': round(asui, 4), | |
| 'ENS_kWh': round(ens_kwh, 2), | |
| 'AENS_kWh_per_customer': round(aens, 4), | |
| # Supporting Statistics | |
| 'total_events': total_events, | |
| 'sustained_events': total_sustained_events, | |
| 'momentary_events': total_momentary_events, | |
| 'total_customer_interruptions': int(total_customer_interruptions), | |
| 'total_customer_minutes': round(customer_minutes, 2), | |
| 'avg_duration_per_event_minutes': round(avg_duration_per_event, 2), | |
| 'max_duration_minutes': round(max_duration, 2), | |
| 'total_affected_customers': int(total_affected) | |
| } | |
| def create_reliability_dataframe(metrics: Dict) -> pd.DataFrame: | |
| """ | |
| สร้าง DataFrame สำหรับแสดงตัวชี้วัดความน่าเชื่อถือ | |
| """ | |
| reliability_data = [ | |
| { | |
| 'Metric': 'SAIFI', | |
| 'Full Name': 'System Average Interruption Frequency Index', | |
| 'Value': metrics['SAIFI'], | |
| 'Unit': 'ครั้ง/ลูกค้า/ปี', | |
| 'Formula': 'Σ(λᵢ × Nᵢ) / Nₜ', | |
| 'Description': 'ความถี่เฉลี่ยของการขัดข้องต่อลูกค้าต่อปี', | |
| 'Good_Value': '< 1.0' | |
| }, | |
| { | |
| 'Metric': 'SAIDI', | |
| 'Full Name': 'System Average Interruption Duration Index', | |
| 'Value': metrics['SAIDI'], | |
| 'Unit': 'นาที/ลูกค้า/ปี', | |
| 'Formula': 'Σ(Uᵢ × Nᵢ) / Nₜ', | |
| 'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อลูกค้าต่อปี', | |
| 'Good_Value': '< 200' | |
| }, | |
| { | |
| 'Metric': 'CAIDI', | |
| 'Full Name': 'Customer Average Interruption Duration Index', | |
| 'Value': metrics['CAIDI'], | |
| 'Unit': 'นาที/ครั้ง', | |
| 'Formula': 'SAIDI / SAIFI', | |
| 'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อครั้งที่เกิดขัดข้อง', | |
| 'Good_Value': '< 120' | |
| }, | |
| { | |
| 'Metric': 'MAIFI', | |
| 'Full Name': 'Momentary Average Interruption Frequency Index', | |
| 'Value': metrics['MAIFI'], | |
| 'Unit': 'ครั้ง/ลูกค้า/ปี', | |
| 'Formula': 'Σ(λₘᵢ × Nᵢ) / Nₜ', | |
| 'Description': 'ความถี่เฉลี่ยของการขัดข้องชั่วคราว (< 1 นาที)', | |
| 'Good_Value': '< 5.0' | |
| }, | |
| { | |
| 'Metric': 'CTAIDI', | |
| 'Full Name': 'Customer Total Average Interruption Duration Index', | |
| 'Value': metrics['CTAIDI'], | |
| 'Unit': 'นาที/ครั้ง', | |
| 'Formula': 'Σ(rᵢ × Nᵢ × λᵢ) / Σ(Nᵢ × λᵢ)', | |
| 'Description': 'ระยะเวลาขัดข้องเฉลี่ยรวมทั้งชั่วคราวและยาวนาน', | |
| 'Good_Value': '< 100' | |
| }, | |
| { | |
| 'Metric': 'ASAI', | |
| 'Full Name': 'Average Service Availability Index', | |
| 'Value': metrics['ASAI_percent'], | |
| 'Unit': '%', | |
| 'Formula': '(Nₜ×T - Σ(rᵢ×Nᵢ)) / (Nₜ×T) × 100', | |
| 'Description': 'ดัชนีความพร้อมใช้งานเฉลี่ยของระบบ', | |
| 'Good_Value': '> 99.95%' | |
| }, | |
| { | |
| 'Metric': 'ASUI', | |
| 'Full Name': 'Average Service Unavailability Index', | |
| 'Value': metrics['ASUI_percent'], | |
| 'Unit': '%', | |
| 'Formula': '100 - ASAI', | |
| 'Description': 'ดัชนีความไม่พร้อมใช้งานเฉลี่ยของระบบ', | |
| 'Good_Value': '< 0.05%' | |
| }, | |
| { | |
| 'Metric': 'AENS', | |
| 'Full Name': 'Average Energy Not Supplied per Customer', | |
| 'Value': metrics['AENS_kWh_per_customer'], | |
| 'Unit': 'kWh/ลูกค้า/ปี', | |
| 'Formula': 'ENS / Nₜ', | |
| 'Description': 'พลังงานไฟฟ้าเฉลี่ยที่ไม่สามารถจ่ายให้ลูกค้าได้', | |
| 'Good_Value': '< 10' | |
| } | |
| ] | |
| return pd.DataFrame(reliability_data) | |
| def interpret_reliability_metrics(metrics: Dict) -> str: | |
| """ | |
| ตีความผลตัวชี้วัดความน่าเชื่อถือ | |
| """ | |
| interpretations = [] | |
| # SAIFI interpretation | |
| saifi = metrics['SAIFI'] | |
| if saifi < 1.0: | |
| interpretations.append("SAIFI: ดีมาก - ลูกค้าขัดข้องน้อยกว่า 1 ครั้งต่อปี") | |
| elif saifi < 2.0: | |
| interpretations.append("SAIFI: ดี - ลูกค้าขัดข้องประมาณ 1-2 ครั้งต่อปี") | |
| elif saifi < 5.0: | |
| interpretations.append("SAIFI: พอใช้ - ลูกค้าขัดข้อง 2-5 ครั้งต่อปี") | |
| else: | |
| interpretations.append("SAIFI: ต้องปรับปรุง - ลูกค้าขัดข้องบ่อยเกิน 5 ครั้งต่อปี") | |
| # SAIDI interpretation | |
| saidi = metrics['SAIDI'] | |
| if saidi < 100: | |
| interpretations.append("SAIDI: ดีมาก - ขัดข้องน้อยกว่า 100 นาทีต่อปี") | |
| elif saidi < 200: | |
| interpretations.append("SAIDI: ดี - ขัดข้อง 100-200 นาทีต่อปี") | |
| elif saidi < 500: | |
| interpretations.append("SAIDI: พอใช้ - ขัดข้อง 200-500 นาทีต่อปี") | |
| else: | |
| interpretations.append("SAIDI: ต้องปรับปรุง - ขัดข้องเกิน 500 นาทีต่อปี") | |
| # ASAI interpretation | |
| asai = metrics['ASAI_percent'] | |
| if asai > 99.98: | |
| interpretations.append("ASAI: ดีเยี่ยม - ระบบพร้อมใช้งานเกิน 99.98%") | |
| elif asai > 99.95: | |
| interpretations.append("ASAI: ดีมาก - ระบบพร้อมใช้งาน 99.95-99.98%") | |
| elif asai > 99.90: | |
| interpretations.append("ASAI: ดี - ระบบพร้อมใช้งาน 99.90-99.95%") | |
| else: | |
| interpretations.append("ASAI: ต้องปรับปรุง - ระบบพร้อมใช้งานต่ำกว่า 99.90%") | |
| return " | ".join(interpretations) | |
| def summarize_overall(df: pd.DataFrame, use_hf: bool = False, model: str = 'meta-llama/Llama-3.1-8B-Instruct:novita', total_customers: float = None) -> Dict: | |
| """Summarize overall outage data with enhanced reliability metrics and GenAI analysis.""" | |
| # Basic statistics | |
| total_events = len(df) | |
| date_cols = ['OutageDateTime', 'FirstRestoDateTime', 'LastRestoDateTime', 'CreateEventDateTime', 'CloseEventDateTime'] | |
| # Parse dates | |
| df_copy = df.copy() | |
| for col in date_cols: | |
| if col in df_copy.columns: | |
| df_copy[col] = pd.to_datetime(df_copy[col], dayfirst=True, errors='coerce') | |
| # Calculate basic metrics | |
| if 'OutageDateTime' in df_copy.columns: | |
| date_range = f"{df_copy['OutageDateTime'].min()} ถึง {df_copy['OutageDateTime'].max()}" if pd.notna(df_copy['OutageDateTime'].min()) else "ไม่ระบุ" | |
| else: | |
| date_range = "ไม่ระบุ" | |
| # Event types | |
| event_types = df_copy.get('EventType', pd.Series()).value_counts().head(5).to_dict() | |
| # Affected customers | |
| total_affected = 0 | |
| if 'AffectedCustomer' in df_copy.columns: | |
| total_affected = pd.to_numeric(df_copy['AffectedCustomer'], errors='coerce').sum() | |
| # Create basic summary text | |
| basic_summary = f""" | |
| ข้อมูลไฟฟ้าล้มทั้งหมด: | |
| - จำนวนเหตุการณ์ทั้งหมด: {total_events} | |
| - ช่วงเวลาที่เกิดเหตุการณ์: {date_range} | |
| - ประเภทเหตุการณ์หลัก: {', '.join([f'{k}: {v}' for k, v in event_types.items()])} | |
| - จำนวนลูกค้าที่ได้รับผลกระทบทั้งหมด: {int(total_affected) if not pd.isna(total_affected) else 'ไม่ระบุ'} | |
| """ | |
| # Compute reliability metrics | |
| reliability_metrics = {} | |
| reliability_df = pd.DataFrame() | |
| reliability_summary = "" | |
| reliability_interpretation = "" | |
| if total_customers and total_customers > 0: | |
| try: | |
| reliability_metrics = compute_reliability_metrics(df_copy, total_customers) | |
| reliability_df = create_reliability_dataframe(reliability_metrics) | |
| reliability_interpretation = interpret_reliability_metrics(reliability_metrics) | |
| reliability_summary = f""" | |
| ดัชนีความน่าเชื่อถือของระบบไฟฟ้า: | |
| ตัวชี้วัดหลัก: | |
| - SAIFI: {reliability_metrics['SAIFI']:.4f} ครั้ง/ลูกค้า/ปี (ความถี่การขัดข้อง) | |
| - SAIDI: {reliability_metrics['SAIDI']:.2f} นาที/ลูกค้า/ปี (ระยะเวลาขัดข้อง) | |
| - CAIDI: {reliability_metrics['CAIDI']:.2f} นาที/ครั้ง (ระยะเวลาเฉลี่ยต่อครั้ง) | |
| - MAIFI: {reliability_metrics['MAIFI']:.4f} ครั้ง/ลูกค้า/ปี (ขัดข้องชั่วคราว) | |
| ตัวชี้วัดเพิ่มเติม: | |
| - ASAI: {reliability_metrics['ASAI_percent']:.4f}% (ความพร้อมใช้งาน) | |
| - ASUI: {reliability_metrics['ASUI_percent']:.4f}% (ความไม่พร้อมใช้งาน) | |
| - AENS: {reliability_metrics['AENS_kWh_per_customer']:.4f} kWh/ลูกค้า/ปี (พลังงานที่สูญเสีย) | |
| การตีความ: {reliability_interpretation} | |
| สถิติสนับสนุน: | |
| - เหตุการณ์ยาวนาน: {reliability_metrics['sustained_events']} ครั้ง | |
| - เหตุการณ์ชั่วคราว: {reliability_metrics['momentary_events']} ครั้ง | |
| - ระยะเวลาเฉลี่ยต่อเหตุการณ์: {reliability_metrics['avg_duration_per_event_minutes']:.2f} นาที | |
| - ระยะเวลาสูงสุด: {reliability_metrics['max_duration_minutes']:.2f} นาที | |
| """ | |
| basic_summary += reliability_summary | |
| except Exception as e: | |
| reliability_summary = f"ไม่สามารถคำนวณดัชนีความน่าเชื่อถือได้: {str(e)}" | |
| # Use GenAI for overall summary with enhanced context | |
| ai_summary = None | |
| if use_hf and get_hf_token(): | |
| try: | |
| instruction = """สรุปภาพรวมข้อมูลไฟฟ้าล้มและวิเคราะห์ตัวชี้วัดความน่าเชื่อถือ สรุปเป็น 2-3 ย่อหน้า (ไทย) ประกอบด้วย: | |
| 1. ภาพรวมเหตุการณ์และผลกระทบ | |
| 2. การวิเคราะห์ตัวชี้วัด SAIFI, SAIDI, ASAI และการตีความ | |
| 3. ข้อเสนอแนะในการปรับปรุงประสิทธิภาพและความน่าเชื่อถือของระบบ:""" | |
| prompt = f"{instruction}\n\n{basic_summary}\n\nสรุปภาพรวมและการวิเคราะห์:" | |
| ai_summary = openai_summary(prompt, verbosity='recommend', model=model) | |
| except Exception as e: | |
| ai_summary = f"ไม่สามารถสร้างสรุปด้วย AI ได้: {str(e)}" | |
| return { | |
| 'total_events': total_events, | |
| 'date_range': date_range, | |
| 'event_types': event_types, | |
| 'total_affected_customers': int(total_affected) if not pd.isna(total_affected) else None, | |
| 'basic_summary': basic_summary.strip(), | |
| 'reliability_summary': reliability_summary.strip() if reliability_summary else None, | |
| 'reliability_metrics': reliability_metrics, | |
| 'reliability_df': reliability_df, | |
| 'reliability_interpretation': reliability_interpretation, | |
| 'ai_summary': ai_summary, | |
| 'formulas': { | |
| 'SAIFI': 'Σ(λᵢ × Nᵢ) / Nₜ', | |
| 'SAIDI': 'Σ(Uᵢ × Nᵢ) / Nₜ', | |
| 'CAIDI': 'SAIDI / SAIFI', | |
| 'MAIFI': 'Σ(λₘᵢ × Nᵢ) / Nₜ', | |
| 'ASAI': '(Nₜ×T - Σ(rᵢ×Nᵢ)) / (Nₜ×T) × 100', | |
| 'AENS': 'ENS / Nₜ' | |
| } | |
| } |