Spaces:
Sleeping
Sleeping
Update scripts/summary.py
Browse files- scripts/summary.py +284 -75
scripts/summary.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
| 1 |
import os
|
| 2 |
import pandas as pd
|
| 3 |
-
|
|
|
|
| 4 |
from pathlib import Path
|
|
|
|
| 5 |
|
| 6 |
# Prefer HF router via OpenAI-compatible client. Use env `HF_TOKEN`.
|
| 7 |
# HF_TOKEN loaded lazily to allow dotenv loading after import
|
|
@@ -34,9 +36,252 @@ def openai_summary(text: str, verbosity: str = 'brief', model: str = 'meta-llama
|
|
| 34 |
except Exception:
|
| 35 |
return None
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
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:
|
| 39 |
-
"""Summarize overall outage data with GenAI and reliability metrics."""
|
|
|
|
| 40 |
# Basic statistics
|
| 41 |
total_events = len(df)
|
| 42 |
date_cols = ['OutageDateTime', 'FirstRestoDateTime', 'LastRestoDateTime', 'CreateEventDateTime', 'CloseEventDateTime']
|
|
@@ -49,7 +294,7 @@ def summarize_overall(df: pd.DataFrame, use_hf: bool = False, model: str = 'meta
|
|
| 49 |
|
| 50 |
# Calculate basic metrics
|
| 51 |
if 'OutageDateTime' in df_copy.columns:
|
| 52 |
-
date_range = f"{df_copy['OutageDateTime'].min()} ถึง {df_copy['OutageDateTime'].max()}" if pd.notna(df_copy['OutageDateTime'].min()) else "ไม่ระบุ"
|
| 53 |
else:
|
| 54 |
date_range = "ไม่ระบุ"
|
| 55 |
|
|
@@ -61,99 +306,63 @@ def summarize_overall(df: pd.DataFrame, use_hf: bool = False, model: str = 'meta
|
|
| 61 |
if 'AffectedCustomer' in df_copy.columns:
|
| 62 |
total_affected = pd.to_numeric(df_copy['AffectedCustomer'], errors='coerce').sum()
|
| 63 |
|
| 64 |
-
#
|
| 65 |
-
|
| 66 |
-
ข้อมูลไฟฟ้าล้มทั้งหมด:
|
| 67 |
-
- จำนวนเหตุการณ์ทั้งหมด: {total_events}
|
| 68 |
-
- ช่วงเวลาที่เกิดเหตุการณ์: {date_range}
|
| 69 |
-
- ประเภทเหตุการณ์หลัก: {', '.join([f'{k}: {v}' for k, v in event_types.items()])}
|
| 70 |
-
- จำนวนลูกค้าที่ได้รับผลกระทบทั้งหมด: {int(total_affected) if not pd.isna(total_affected) else 'ไม่ระบุ'}
|
| 71 |
-
"""
|
| 72 |
-
|
| 73 |
-
# Reliability metrics DataFrame
|
| 74 |
reliability_df = pd.DataFrame()
|
| 75 |
reliability_summary = ""
|
| 76 |
-
|
|
|
|
| 77 |
if total_customers and total_customers > 0:
|
| 78 |
try:
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
if not overall_metrics.empty:
|
| 92 |
-
row = overall_metrics.iloc[0]
|
| 93 |
-
|
| 94 |
-
# Create reliability DataFrame with proper metric names
|
| 95 |
-
reliability_data = [
|
| 96 |
-
{
|
| 97 |
-
'Metric': 'SAIFI',
|
| 98 |
-
'Full Name': 'System Average Interruption Frequency Index',
|
| 99 |
-
'Value': f"{row.get('SAIFI', 'N/A'):.4f}",
|
| 100 |
-
'Unit': 'ครั้ง/ลูกค้า',
|
| 101 |
-
'Description': 'ความถี่เฉลี่ยของการขัดข้องต่อลูกค้า'
|
| 102 |
-
},
|
| 103 |
-
{
|
| 104 |
-
'Metric': 'SAIDI',
|
| 105 |
-
'Full Name': 'System Average Interruption Duration Index',
|
| 106 |
-
'Value': f"{row.get('SAIDI', 'N/A'):.2f}",
|
| 107 |
-
'Unit': 'นาที/ลูกค้า',
|
| 108 |
-
'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อลูกค้า'
|
| 109 |
-
},
|
| 110 |
-
{
|
| 111 |
-
'Metric': 'CAIDI',
|
| 112 |
-
'Full Name': 'Customer Average Interruption Duration Index',
|
| 113 |
-
'Value': f"{row.get('CAIDI', 'N/A'):.2f}",
|
| 114 |
-
'Unit': 'นาที/ครั้ง',
|
| 115 |
-
'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อครั้ง'
|
| 116 |
-
},
|
| 117 |
-
{
|
| 118 |
-
'Metric': 'MAIFI',
|
| 119 |
-
'Full Name': 'Momentary Average Interruption Frequency Index',
|
| 120 |
-
'Value': f"{row.get('MAIFI', 'N/A'):.4f}",
|
| 121 |
-
'Unit': 'ครั้ง/ลูกค้า',
|
| 122 |
-
'Description': 'ความถี่เฉลี่ยของการขัดข้องชั่วคราวต่อลูกค้า'
|
| 123 |
-
}
|
| 124 |
-
]
|
| 125 |
-
reliability_df = pd.DataFrame(reliability_data)
|
| 126 |
-
|
| 127 |
-
reliability_summary = f"""
|
| 128 |
-
ดัชนีความน่าเชื่อถือ:
|
| 129 |
-
- SAIFI (System Average Interruption Frequency Index): {row.get('SAIFI', 'N/A'):.4f} ครั้ง/ลูกค้า
|
| 130 |
-
- SAIDI (System Average Interruption Duration Index): {row.get('SAIDI', 'N/A'):.2f} นาที/ลูกค้า
|
| 131 |
-
- CAIDI (Customer Average Interruption Duration Index): {row.get('CAIDI', 'N/A'):.2f} นาที/ครั้ง
|
| 132 |
-
- MAIFI (Momentary Average Interruption Frequency Index): {row.get('MAIFI', 'N/A'):.4f} ครั้ง/ลูกค้า
|
| 133 |
"""
|
| 134 |
-
summary_text += reliability_summary
|
| 135 |
-
finally:
|
| 136 |
-
os.unlink(temp_path)
|
| 137 |
except Exception as e:
|
| 138 |
reliability_summary = f"ไม่สามารถคำนวณดัชนีความน่าเชื่อถือได้: {str(e)}"
|
| 139 |
|
| 140 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
ai_summary = None
|
| 142 |
if use_hf and get_hf_token():
|
| 143 |
try:
|
| 144 |
-
instruction = "
|
| 145 |
-
prompt = f"{instruction}\n\n{summary_text}\n\n
|
| 146 |
ai_summary = openai_summary(prompt, verbosity='recommend', model=model)
|
| 147 |
except Exception as e:
|
| 148 |
-
ai_summary = f"
|
| 149 |
|
| 150 |
return {
|
| 151 |
'total_events': total_events,
|
| 152 |
'date_range': date_range,
|
| 153 |
'event_types': event_types,
|
| 154 |
'total_affected_customers': int(total_affected) if not pd.isna(total_affected) else None,
|
|
|
|
| 155 |
'basic_summary': summary_text.strip(),
|
| 156 |
'reliability_summary': reliability_summary.strip() if reliability_summary else None,
|
|
|
|
|
|
|
| 157 |
'reliability_df': reliability_df,
|
| 158 |
'ai_summary': ai_summary,
|
| 159 |
}
|
|
|
|
| 1 |
import os
|
| 2 |
import pandas as pd
|
| 3 |
+
import numpy as np
|
| 4 |
+
from typing import Dict, Optional, Tuple
|
| 5 |
from pathlib import Path
|
| 6 |
+
from datetime import datetime, timedelta
|
| 7 |
|
| 8 |
# Prefer HF router via OpenAI-compatible client. Use env `HF_TOKEN`.
|
| 9 |
# HF_TOKEN loaded lazily to allow dotenv loading after import
|
|
|
|
| 36 |
except Exception:
|
| 37 |
return None
|
| 38 |
|
| 39 |
+
def calculate_outage_duration(outage_time: pd.Series, restore_time: pd.Series) -> pd.Series:
|
| 40 |
+
"""Calculate outage duration in minutes."""
|
| 41 |
+
try:
|
| 42 |
+
outage_dt = pd.to_datetime(outage_time, dayfirst=True, errors='coerce')
|
| 43 |
+
restore_dt = pd.to_datetime(restore_time, dayfirst=True, errors='coerce')
|
| 44 |
+
|
| 45 |
+
# Calculate duration in minutes
|
| 46 |
+
duration = (restore_dt - outage_dt).dt.total_seconds() / 60
|
| 47 |
+
return duration.fillna(0)
|
| 48 |
+
except:
|
| 49 |
+
return pd.Series([0] * len(outage_time))
|
| 50 |
+
|
| 51 |
+
def calculate_reliability_indices(df: pd.DataFrame, total_customers: float) -> Dict:
|
| 52 |
+
"""
|
| 53 |
+
Calculate power system reliability indices with proper formulas.
|
| 54 |
+
|
| 55 |
+
Formulas:
|
| 56 |
+
- SAIFI = Σ(Ni) / NT
|
| 57 |
+
- SAIDI = Σ(Ni × Ti) / NT
|
| 58 |
+
- CAIDI = SAIDI / SAIFI = Σ(Ni × Ti) / Σ(Ni)
|
| 59 |
+
- MAIFI = Σ(Ni_momentary) / NT
|
| 60 |
+
- ASAI = (NT × T - Σ(Ni × Ti)) / (NT × T)
|
| 61 |
+
- ASUI = Σ(Ni × Ti) / (NT × T)
|
| 62 |
+
|
| 63 |
+
Where:
|
| 64 |
+
- Ni = Number of customers affected by interruption i
|
| 65 |
+
- Ti = Duration of interruption i (minutes)
|
| 66 |
+
- NT = Total number of customers served
|
| 67 |
+
- T = Time period (typically 1 year = 525,600 minutes)
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
# Parse datetime columns
|
| 71 |
+
df_calc = df.copy()
|
| 72 |
+
|
| 73 |
+
# Calculate outage duration
|
| 74 |
+
if 'OutageDateTime' in df_calc.columns and ('FirstRestoDateTime' in df_calc.columns or 'LastRestoDateTime' in df_calc.columns):
|
| 75 |
+
restore_col = 'LastRestoDateTime' if 'LastRestoDateTime' in df_calc.columns else 'FirstRestoDateTime'
|
| 76 |
+
df_calc['Duration_Minutes'] = calculate_outage_duration(df_calc['OutageDateTime'], df_calc[restore_col])
|
| 77 |
+
else:
|
| 78 |
+
df_calc['Duration_Minutes'] = 0
|
| 79 |
+
|
| 80 |
+
# Handle affected customers
|
| 81 |
+
if 'AffectedCustomer' in df_calc.columns:
|
| 82 |
+
df_calc['Customers_Affected'] = pd.to_numeric(df_calc['AffectedCustomer'], errors='coerce').fillna(0)
|
| 83 |
+
else:
|
| 84 |
+
df_calc['Customers_Affected'] = 0
|
| 85 |
+
|
| 86 |
+
# Filter out invalid data
|
| 87 |
+
valid_data = df_calc[
|
| 88 |
+
(df_calc['Duration_Minutes'] >= 0) &
|
| 89 |
+
(df_calc['Customers_Affected'] >= 0) &
|
| 90 |
+
(~pd.isna(df_calc['Duration_Minutes'])) &
|
| 91 |
+
(~pd.isna(df_calc['Customers_Affected']))
|
| 92 |
+
].copy()
|
| 93 |
+
|
| 94 |
+
# Separate planned and unplanned outages
|
| 95 |
+
if 'EventType' in valid_data.columns:
|
| 96 |
+
# Assume planned outages contain keywords like "planned", "maintenance", "scheduled"
|
| 97 |
+
planned_keywords = ['plan', 'maintenance', 'scheduled', 'แผน', 'บำรุงรักษา']
|
| 98 |
+
planned_mask = valid_data['EventType'].str.contains('|'.join(planned_keywords), case=False, na=False)
|
| 99 |
+
unplanned_data = valid_data[~planned_mask].copy()
|
| 100 |
+
planned_data = valid_data[planned_mask].copy()
|
| 101 |
+
else:
|
| 102 |
+
unplanned_data = valid_data.copy()
|
| 103 |
+
planned_data = pd.DataFrame()
|
| 104 |
+
|
| 105 |
+
# Calculate customer-minutes for unplanned outages
|
| 106 |
+
unplanned_data['Customer_Minutes'] = unplanned_data['Customers_Affected'] * unplanned_data['Duration_Minutes']
|
| 107 |
+
|
| 108 |
+
# Identify momentary interruptions (< 5 minutes)
|
| 109 |
+
momentary_data = unplanned_data[unplanned_data['Duration_Minutes'] < 5].copy()
|
| 110 |
+
sustained_data = unplanned_data[unplanned_data['Duration_Minutes'] >= 5].copy()
|
| 111 |
+
|
| 112 |
+
# Calculate indices
|
| 113 |
+
total_interruptions = len(unplanned_data)
|
| 114 |
+
total_customer_interruptions = unplanned_data['Customers_Affected'].sum()
|
| 115 |
+
total_customer_minutes = unplanned_data['Customer_Minutes'].sum()
|
| 116 |
+
|
| 117 |
+
# Momentary interruptions
|
| 118 |
+
momentary_customer_interruptions = momentary_data['Customers_Affected'].sum()
|
| 119 |
+
|
| 120 |
+
# Time period (assume 1 year = 525,600 minutes)
|
| 121 |
+
time_period_minutes = 525600
|
| 122 |
+
|
| 123 |
+
# Calculate SAIFI (System Average Interruption Frequency Index)
|
| 124 |
+
# SAIFI = Σ(Customer Interruptions) / Total Customers
|
| 125 |
+
saifi = total_customer_interruptions / total_customers if total_customers > 0 else 0
|
| 126 |
+
|
| 127 |
+
# Calculate SAIDI (System Average Interruption Duration Index)
|
| 128 |
+
# SAIDI = Σ(Customer Minutes) / Total Customers
|
| 129 |
+
saidi = total_customer_minutes / total_customers if total_customers > 0 else 0
|
| 130 |
+
|
| 131 |
+
# Calculate CAIDI (Customer Average Interruption Duration Index)
|
| 132 |
+
# CAIDI = SAIDI / SAIFI = Σ(Customer Minutes) / Σ(Customer Interruptions)
|
| 133 |
+
caidi = total_customer_minutes / total_customer_interruptions if total_customer_interruptions > 0 else 0
|
| 134 |
+
|
| 135 |
+
# Calculate MAIFI (Momentary Average Interruption Frequency Index)
|
| 136 |
+
# MAIFI = Σ(Momentary Customer Interruptions) / Total Customers
|
| 137 |
+
maifi = momentary_customer_interruptions / total_customers if total_customers > 0 else 0
|
| 138 |
+
|
| 139 |
+
# Calculate ASAI (Average System Availability Index)
|
| 140 |
+
# ASAI = (Total Customer Hours - Customer Hours of Interruption) / Total Customer Hours
|
| 141 |
+
total_customer_hours = total_customers * time_period_minutes / 60
|
| 142 |
+
customer_hours_interrupted = total_customer_minutes / 60
|
| 143 |
+
asai = (total_customer_hours - customer_hours_interrupted) / total_customer_hours if total_customer_hours > 0 else 0
|
| 144 |
+
asai_percent = asai * 100
|
| 145 |
+
|
| 146 |
+
# Calculate ASUI (Average System Unavailability Index)
|
| 147 |
+
# ASUI = Customer Hours of Interruption / Total Customer Hours
|
| 148 |
+
asui = customer_hours_interrupted / total_customer_hours if total_customer_hours > 0 else 0
|
| 149 |
+
asui_percent = asui * 100
|
| 150 |
+
|
| 151 |
+
# Additional metrics
|
| 152 |
+
avg_outage_duration = unplanned_data['Duration_Minutes'].mean() if len(unplanned_data) > 0 else 0
|
| 153 |
+
max_outage_duration = unplanned_data['Duration_Minutes'].max() if len(unplanned_data) > 0 else 0
|
| 154 |
+
avg_customers_per_outage = unplanned_data['Customers_Affected'].mean() if len(unplanned_data) > 0 else 0
|
| 155 |
+
max_customers_affected = unplanned_data['Customers_Affected'].max() if len(unplanned_data) > 0 else 0
|
| 156 |
+
|
| 157 |
+
# Calculate ENS (Energy Not Served) - approximate
|
| 158 |
+
# Assuming average load per customer (can be adjusted)
|
| 159 |
+
avg_load_per_customer_kw = 2.0 # Typical residential load
|
| 160 |
+
ens_kwh = (total_customer_minutes / 60) * avg_load_per_customer_kw
|
| 161 |
+
|
| 162 |
+
return {
|
| 163 |
+
'SAIFI': saifi,
|
| 164 |
+
'SAIDI': saidi,
|
| 165 |
+
'CAIDI': caidi,
|
| 166 |
+
'MAIFI': maifi,
|
| 167 |
+
'ASAI': asai_percent,
|
| 168 |
+
'ASUI': asui_percent,
|
| 169 |
+
'ENS_kWh': ens_kwh,
|
| 170 |
+
'Total_Interruptions': total_interruptions,
|
| 171 |
+
'Total_Customer_Interruptions': int(total_customer_interruptions),
|
| 172 |
+
'Total_Customer_Minutes': total_customer_minutes,
|
| 173 |
+
'Momentary_Interruptions': len(momentary_data),
|
| 174 |
+
'Sustained_Interruptions': len(sustained_data),
|
| 175 |
+
'Avg_Outage_Duration': avg_outage_duration,
|
| 176 |
+
'Max_Outage_Duration': max_outage_duration,
|
| 177 |
+
'Avg_Customers_Per_Outage': avg_customers_per_outage,
|
| 178 |
+
'Max_Customers_Affected': int(max_customers_affected),
|
| 179 |
+
'Planned_Outages': len(planned_data) if not planned_data.empty else 0
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
def create_reliability_dataframe(metrics: Dict) -> pd.DataFrame:
|
| 183 |
+
"""Create a formatted DataFrame for reliability metrics."""
|
| 184 |
+
reliability_data = [
|
| 185 |
+
{
|
| 186 |
+
'Metric': 'SAIFI',
|
| 187 |
+
'Full Name': 'System Average Interruption Frequency Index',
|
| 188 |
+
'Formula': 'Σ(Ni) / NT',
|
| 189 |
+
'Value': f"{metrics.get('SAIFI', 0):.4f}",
|
| 190 |
+
'Unit': 'ครั้ง/ลูกค้า/ปี',
|
| 191 |
+
'Description': 'ความถี่เฉลี่ยของการขัดข้องต่อลูกค้าต่อปี',
|
| 192 |
+
'Benchmark': '< 1.5 (ดีมาก), 1.5-3.0 (ดี), > 3.0 (ต้องปรับปรุง)'
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
'Metric': 'SAIDI',
|
| 196 |
+
'Full Name': 'System Average Interruption Duration Index',
|
| 197 |
+
'Formula': 'Σ(Ni × Ti) / NT',
|
| 198 |
+
'Value': f"{metrics.get('SAIDI', 0):.2f}",
|
| 199 |
+
'Unit': 'นาที/ลูกค้า/ปี',
|
| 200 |
+
'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อลูกค้าต่อปี',
|
| 201 |
+
'Benchmark': '< 100 (ดีมาก), 100-300 (ดี), > 300 (ต้องปรับปรุง)'
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
'Metric': 'CAIDI',
|
| 205 |
+
'Full Name': 'Customer Average Interruption Duration Index',
|
| 206 |
+
'Formula': 'SAIDI / SAIFI',
|
| 207 |
+
'Value': f"{metrics.get('CAIDI', 0):.2f}",
|
| 208 |
+
'Unit': 'นาที/ครั้ง',
|
| 209 |
+
'Description': 'ระยะเวลาขัดข้องเฉลี่ยต่อครั้งที่เกิดขึ้น',
|
| 210 |
+
'Benchmark': '< 120 (ดี), 120-240 (ปานกลาง), > 240 (ต้องปรับปรุง)'
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
'Metric': 'MAIFI',
|
| 214 |
+
'Full Name': 'Momentary Average Interruption Frequency Index',
|
| 215 |
+
'Formula': 'Σ(Ni_momentary) / NT',
|
| 216 |
+
'Value': f"{metrics.get('MAIFI', 0):.4f}",
|
| 217 |
+
'Unit': 'ครั้ง/ลูกค้า/ปี',
|
| 218 |
+
'Description': 'ความถี่เฉลี่ยของการขัดข้องชั่วคราว (<5 นาที)',
|
| 219 |
+
'Benchmark': '< 5 (ดี), 5-10 (ปานกลาง), > 10 (ต้องปรับปรุง)'
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
'Metric': 'ASAI',
|
| 223 |
+
'Full Name': 'Average System Availability Index',
|
| 224 |
+
'Formula': '(NT×T - Σ(Ni×Ti)) / (NT×T) × 100',
|
| 225 |
+
'Value': f"{metrics.get('ASAI', 0):.4f}",
|
| 226 |
+
'Unit': '%',
|
| 227 |
+
'Description': 'ดัชนีความพร้อมใช้งานเฉลี่ยของระบบ',
|
| 228 |
+
'Benchmark': '> 99.95% (ดีมาก), 99.9-99.95% (ดี), < 99.9% (ต้องปรับปรุง)'
|
| 229 |
+
},
|
| 230 |
+
{
|
| 231 |
+
'Metric': 'ASUI',
|
| 232 |
+
'Full Name': 'Average System Unavailability Index',
|
| 233 |
+
'Formula': 'Σ(Ni×Ti) / (NT×T) × 100',
|
| 234 |
+
'Value': f"{metrics.get('ASUI', 0):.4f}",
|
| 235 |
+
'Unit': '%',
|
| 236 |
+
'Description': 'ดัชนีความไม่พร้อมใช้งานเฉลี่ยของระบบ',
|
| 237 |
+
'Benchmark': '< 0.05% (ดีมาก), 0.05-0.1% (ดี), > 0.1% (ต้องปรับปรุง)'
|
| 238 |
+
},
|
| 239 |
+
{
|
| 240 |
+
'Metric': 'ENS',
|
| 241 |
+
'Full Name': 'Energy Not Served',
|
| 242 |
+
'Formula': 'Σ(Ni×Ti×Load_avg) / 60',
|
| 243 |
+
'Value': f"{metrics.get('ENS_kWh', 0):.0f}",
|
| 244 |
+
'Unit': 'kWh',
|
| 245 |
+
'Description': 'พลังงานที่ไม่สามารถจ่ายให้ลูกค้าได้',
|
| 246 |
+
'Benchmark': 'ยิ่งน้อยยิ่งดี (ขึ้นกับพื้นที่และประเภทลูกค้า)'
|
| 247 |
+
}
|
| 248 |
+
]
|
| 249 |
+
|
| 250 |
+
return pd.DataFrame(reliability_data)
|
| 251 |
+
|
| 252 |
+
def generate_performance_summary(metrics: Dict, total_customers: float) -> str:
|
| 253 |
+
"""Generate performance summary with benchmarking."""
|
| 254 |
+
saifi = metrics.get('SAIFI', 0)
|
| 255 |
+
saidi = metrics.get('SAIDI', 0)
|
| 256 |
+
asai = metrics.get('ASAI', 0)
|
| 257 |
+
|
| 258 |
+
# Performance assessment
|
| 259 |
+
performance_level = "ดี"
|
| 260 |
+
if saifi > 3.0 or saidi > 300 or asai < 99.9:
|
| 261 |
+
performance_level = "ต้องปรับปรุง"
|
| 262 |
+
elif saifi < 1.5 and saidi < 100 and asai > 99.95:
|
| 263 |
+
performance_level = "ดีมาก"
|
| 264 |
+
|
| 265 |
+
summary = f"""
|
| 266 |
+
การประเมินประสิทธิภาพระบบไฟฟ้า: {performance_level}
|
| 267 |
+
|
| 268 |
+
ดัชนีความน่าเชื่อถือหลัก:
|
| 269 |
+
• SAIFI: {saifi:.4f} ครั้ง/ลูกค้า/ปี (เป้าหมาย < 1.5)
|
| 270 |
+
• SAIDI: {saidi:.2f} นาที/ลูกค้า/ปี (เป้าหมาย < 100)
|
| 271 |
+
• ความพร้อมใช้งาน (ASAI): {asai:.4f}% (เป้าหมาย > 99.95%)
|
| 272 |
+
|
| 273 |
+
สถิติเหตุการณ์:
|
| 274 |
+
• เหตุการณ์ขัดข้องทั้งหมด: {metrics.get('Total_Interruptions', 0):,} ครั้ง
|
| 275 |
+
• ลูกค้าที่ได้รับผลกระทบ: {metrics.get('Total_Customer_Interruptions', 0):,} ครั้ง
|
| 276 |
+
• เวลาขัดข้องเฉลี่ย: {metrics.get('Avg_Outage_Duration', 0):.1f} นาที
|
| 277 |
+
• เวลาขัดข้องสูงสุด: {metrics.get('Max_Outage_Duration', 0):.1f} นาที
|
| 278 |
+
"""
|
| 279 |
+
|
| 280 |
+
return summary.strip()
|
| 281 |
|
| 282 |
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:
|
| 283 |
+
"""Summarize overall outage data with enhanced GenAI and reliability metrics."""
|
| 284 |
+
|
| 285 |
# Basic statistics
|
| 286 |
total_events = len(df)
|
| 287 |
date_cols = ['OutageDateTime', 'FirstRestoDateTime', 'LastRestoDateTime', 'CreateEventDateTime', 'CloseEventDateTime']
|
|
|
|
| 294 |
|
| 295 |
# Calculate basic metrics
|
| 296 |
if 'OutageDateTime' in df_copy.columns:
|
| 297 |
+
date_range = f"{df_copy['OutageDateTime'].min().strftime('%d/%m/%Y')} ถึง {df_copy['OutageDateTime'].max().strftime('%d/%m/%Y')}" if pd.notna(df_copy['OutageDateTime'].min()) else "ไม่ระบุ"
|
| 298 |
else:
|
| 299 |
date_range = "ไม่ระบุ"
|
| 300 |
|
|
|
|
| 306 |
if 'AffectedCustomer' in df_copy.columns:
|
| 307 |
total_affected = pd.to_numeric(df_copy['AffectedCustomer'], errors='coerce').sum()
|
| 308 |
|
| 309 |
+
# Calculate reliability metrics if total_customers is provided
|
| 310 |
+
reliability_metrics = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
reliability_df = pd.DataFrame()
|
| 312 |
reliability_summary = ""
|
| 313 |
+
performance_summary = ""
|
| 314 |
+
|
| 315 |
if total_customers and total_customers > 0:
|
| 316 |
try:
|
| 317 |
+
reliability_metrics = calculate_reliability_indices(df_copy, total_customers)
|
| 318 |
+
reliability_df = create_reliability_dataframe(reliability_metrics)
|
| 319 |
+
performance_summary = generate_performance_summary(reliability_metrics, total_customers)
|
| 320 |
+
|
| 321 |
+
reliability_summary = f"""
|
| 322 |
+
ดัชนีความน่าเชื่อถือของระบบไฟฟ้า:
|
| 323 |
+
• SAIFI: {reliability_metrics.get('SAIFI', 0):.4f} ครั้ง/ลูกค้า/ปี
|
| 324 |
+
• SAIDI: {reliability_metrics.get('SAIDI', 0):.2f} นาที/ลูกค้า/ปี
|
| 325 |
+
• CAIDI: {reliability_metrics.get('CAIDI', 0):.2f} นาที/ครั้ง
|
| 326 |
+
• MAIFI: {reliability_metrics.get('MAIFI', 0):.4f} ครั้ง/ลูกค้า/ปี
|
| 327 |
+
• ความพร้อมใช้งาน (ASAI): {reliability_metrics.get('ASAI', 0):.4f}%
|
| 328 |
+
• พลังงานที่สูญเสีย (ENS): {reliability_metrics.get('ENS_kWh', 0):,.0f} kWh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
"""
|
|
|
|
|
|
|
|
|
|
| 330 |
except Exception as e:
|
| 331 |
reliability_summary = f"ไม่สามารถคำนวณดัชนีความน่าเชื่อถือได้: {str(e)}"
|
| 332 |
|
| 333 |
+
# Create comprehensive summary text for GenAI
|
| 334 |
+
summary_text = f"""
|
| 335 |
+
รายงานสรุปข้อมูลไฟฟ้าล้ม:
|
| 336 |
+
• จำนวนเหตุการณ์ทั้งหมด: {total_events:,} ครั้ง
|
| 337 |
+
• ช่วงเวลาที่เกิดเหต���การณ์: {date_range}
|
| 338 |
+
• ประเภทเหตุการณ์หลัก: {', '.join([f'{k}: {v}' for k, v in event_types.items()])}
|
| 339 |
+
• จำนวนลูกค้าที่ได้รับผลกระทบรวม: {int(total_affected) if not pd.isna(total_affected) else 'ไม่ระบุ':,}
|
| 340 |
+
• จำนวนลูกค้าทั้งหมดในระบบ: {int(total_customers) if total_customers else 'ไม่ระบุ':,}
|
| 341 |
+
|
| 342 |
+
{reliability_summary}
|
| 343 |
+
{performance_summary}
|
| 344 |
+
"""
|
| 345 |
+
|
| 346 |
+
# Use GenAI for comprehensive analysis
|
| 347 |
ai_summary = None
|
| 348 |
if use_hf and get_hf_token():
|
| 349 |
try:
|
| 350 |
+
instruction = "วิเคราะห์และสรุปข้อมูลความน่าเชื่อถือของระบบไฟฟ้าจากข้อมูลนี้ สรุป 2-3 ย่อหน้า (ไทย) ระบุ: 1) ประสิทธิภาพระบบโดยรวม 2) จุดที่ต้องปรับปรุง 3) ข้อเสนะแนะเชิงเทคนิค:"
|
| 351 |
+
prompt = f"{instruction}\n\n{summary_text}\n\nการวิเคราะห์และข้อเสนอแนะ:"
|
| 352 |
ai_summary = openai_summary(prompt, verbosity='recommend', model=model)
|
| 353 |
except Exception as e:
|
| 354 |
+
ai_summary = f"ไม่สามารถสร้างการวิเคราะห์ด้วย AI ได้: {str(e)}"
|
| 355 |
|
| 356 |
return {
|
| 357 |
'total_events': total_events,
|
| 358 |
'date_range': date_range,
|
| 359 |
'event_types': event_types,
|
| 360 |
'total_affected_customers': int(total_affected) if not pd.isna(total_affected) else None,
|
| 361 |
+
'total_customers_served': int(total_customers) if total_customers else None,
|
| 362 |
'basic_summary': summary_text.strip(),
|
| 363 |
'reliability_summary': reliability_summary.strip() if reliability_summary else None,
|
| 364 |
+
'performance_summary': performance_summary.strip() if performance_summary else None,
|
| 365 |
+
'reliability_metrics': reliability_metrics,
|
| 366 |
'reliability_df': reliability_df,
|
| 367 |
'ai_summary': ai_summary,
|
| 368 |
}
|