AI-OMS-Analyze / scripts /summary.py
bboat's picture
Update scripts/summary.py
1da28ef verified
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ₜ'
}
}