Upload 2 files
Browse files- requirements.txt +12 -0
- tanzerultimate.py +478 -0
requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pandas
|
| 2 |
+
numpy
|
| 3 |
+
requests
|
| 4 |
+
matplotlib
|
| 5 |
+
seaborn
|
| 6 |
+
tqdm
|
| 7 |
+
scikit-learn
|
| 8 |
+
xgboost
|
| 9 |
+
lightgbm
|
| 10 |
+
catboost
|
| 11 |
+
tensorflow
|
| 12 |
+
openpyxl
|
tanzerultimate.py
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
import requests
|
| 4 |
+
import time
|
| 5 |
+
import os
|
| 6 |
+
import warnings
|
| 7 |
+
import sys
|
| 8 |
+
import re
|
| 9 |
+
import matplotlib.pyplot as plt
|
| 10 |
+
import matplotlib.dates as mdates
|
| 11 |
+
import seaborn as sns
|
| 12 |
+
from datetime import datetime, timedelta
|
| 13 |
+
from tqdm import tqdm
|
| 14 |
+
|
| 15 |
+
# --- GEREKLİ KÜTÜPHANELER ---
|
| 16 |
+
from sklearn.model_selection import train_test_split
|
| 17 |
+
from sklearn.preprocessing import StandardScaler
|
| 18 |
+
from sklearn.metrics import f1_score
|
| 19 |
+
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, HistGradientBoostingClassifier, \
|
| 20 |
+
AdaBoostClassifier
|
| 21 |
+
from sklearn.neighbors import KNeighborsClassifier
|
| 22 |
+
from sklearn.linear_model import SGDClassifier
|
| 23 |
+
from sklearn.neural_network import MLPClassifier
|
| 24 |
+
from sklearn.naive_bayes import GaussianNB
|
| 25 |
+
import xgboost as xgb
|
| 26 |
+
import lightgbm as lgb
|
| 27 |
+
import catboost as cb
|
| 28 |
+
|
| 29 |
+
# --- AYARLAR ---
|
| 30 |
+
pd.set_option('display.max_columns', None)
|
| 31 |
+
pd.set_option('display.width', 1000)
|
| 32 |
+
warnings.filterwarnings("ignore")
|
| 33 |
+
requests.packages.urllib3.disable_warnings()
|
| 34 |
+
|
| 35 |
+
# --- AKADEMİK RENK PALETİ TANIMI ---
|
| 36 |
+
# Bilimsel yayınlara uygun, ciddi ve net ayrım sağlayan renkler
|
| 37 |
+
ACADEMIC_COLORS = {
|
| 38 |
+
'dusuk': '#2E7D32', # Koyu Yeşil (Orman Yeşili)
|
| 39 |
+
'orta': '#F9A825', # Koyu Hardal Sarısı
|
| 40 |
+
'riskli': '#EF6C00', # Koyu Turuncu (Kiremit)
|
| 41 |
+
'yuksek': '#C62828', # Koyu Bordo
|
| 42 |
+
'tehlikeli': '#37474F' # Koyu Antrasit Mavi/Siyah
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
# DOSYA VE KLASÖR
|
| 46 |
+
MGM_DATA_FILE = "mgm.csv"
|
| 47 |
+
FAULT_FILE = "faults.csv"
|
| 48 |
+
MAIN_FOLDER = "ariza_grafikleri"
|
| 49 |
+
SUB_FOLDER = "TANZER_PROHIBRIT_RESULTS"
|
| 50 |
+
OUTPUT_DIR = os.path.join(MAIN_FOLDER, SUB_FOLDER)
|
| 51 |
+
|
| 52 |
+
if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR)
|
| 53 |
+
OUTPUT_EXCEL = os.path.join(OUTPUT_DIR, "PROHIBRIT_RISK_RAPORU.xlsx")
|
| 54 |
+
PLOT_BAR = os.path.join(OUTPUT_DIR, "PROHIBRIT_BAR_CHART.png")
|
| 55 |
+
PLOT_LINE = os.path.join(OUTPUT_DIR, "PROHIBRIT_LINE_CHART.png")
|
| 56 |
+
|
| 57 |
+
MGM_MAPPING = {
|
| 58 |
+
"Tarih": "Zaman",
|
| 59 |
+
"Sicaklik": "Sıcaklık",
|
| 60 |
+
"Nem": "Nispi Nem",
|
| 61 |
+
"Yagis": "Toplam Yağış OMGI mm",
|
| 62 |
+
"Ruzgar_Hizi": "Rüzgar Yönü ve Hızı",
|
| 63 |
+
"Basinc": "Deniz Seviyesine İndirgenmiş Basınç hPa"
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
print("========================================================================")
|
| 67 |
+
print(" TANZER PROHİBRİT MODEL (AKADEMİK SÜRÜM)")
|
| 68 |
+
print(" (11 Model Ensemble + Gelişmiş Bilimsel Görselleştirme)")
|
| 69 |
+
print("========================================================================\n")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# ---------------------------------------------------------
|
| 73 |
+
# 1. VERİ İŞLEME
|
| 74 |
+
# ---------------------------------------------------------
|
| 75 |
+
def clean_number(val):
|
| 76 |
+
if pd.isna(val) or val == "": return 0.0
|
| 77 |
+
try:
|
| 78 |
+
s = str(val).replace(',', '.').strip()
|
| 79 |
+
nums = re.findall(r"[-+]?\d*\.\d+|\d+", s)
|
| 80 |
+
if nums: return float(nums[0])
|
| 81 |
+
return 0.0
|
| 82 |
+
except:
|
| 83 |
+
return 0.0
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def convert_excel_date(val):
|
| 87 |
+
if pd.isna(val) or val == "": return pd.NaT
|
| 88 |
+
try:
|
| 89 |
+
s = str(val).replace(',', '.').strip()
|
| 90 |
+
if re.match(r'^\d+(\.\d+)?$', s):
|
| 91 |
+
serial = float(s)
|
| 92 |
+
if 30000 < serial < 60000:
|
| 93 |
+
return pd.Timestamp('1899-12-30') + pd.to_timedelta(serial, unit='D')
|
| 94 |
+
except:
|
| 95 |
+
pass
|
| 96 |
+
try:
|
| 97 |
+
return pd.to_datetime(val, dayfirst=True)
|
| 98 |
+
except:
|
| 99 |
+
pass
|
| 100 |
+
try:
|
| 101 |
+
return pd.to_datetime(val)
|
| 102 |
+
except:
|
| 103 |
+
return pd.NaT
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def clean_coord(val):
|
| 107 |
+
try:
|
| 108 |
+
s = str(val).replace(',', '.')
|
| 109 |
+
s = re.sub(r"[^0-9\.\-]", "", s)
|
| 110 |
+
f = float(s)
|
| 111 |
+
if -90 <= f <= 90: return f
|
| 112 |
+
except:
|
| 113 |
+
return None
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def calculate_features(df):
|
| 117 |
+
if 'Tarih' in df.columns: df = df.set_index('Tarih')
|
| 118 |
+
if not isinstance(df.index, pd.DatetimeIndex): df.index = pd.to_datetime(df.index, dayfirst=True, errors='coerce')
|
| 119 |
+
df = df[df.index.notnull()].sort_index()
|
| 120 |
+
|
| 121 |
+
if 'Yagis' in df.columns:
|
| 122 |
+
df['Yagis_7G'] = df['Yagis'].rolling('7d').sum().fillna(0)
|
| 123 |
+
else:
|
| 124 |
+
df['Yagis_7G'] = 0
|
| 125 |
+
|
| 126 |
+
if 'Basinc' in df.columns:
|
| 127 |
+
df['Basinc_Trend'] = df['Basinc'].diff(24).fillna(0)
|
| 128 |
+
df['Basinc_Stabilite'] = df['Basinc'].rolling('3d').std().fillna(0)
|
| 129 |
+
else:
|
| 130 |
+
df['Basinc_Trend'] = 0
|
| 131 |
+
df['Basinc_Stabilite'] = 0
|
| 132 |
+
|
| 133 |
+
if 'Sicaklik' in df.columns:
|
| 134 |
+
df['Sicaklik_Soku'] = df['Sicaklik'].diff(6).abs().fillna(0)
|
| 135 |
+
df['Donma_Indeksi'] = (df['Sicaklik'] < 0).astype(int) * df['Yagis_7G']
|
| 136 |
+
else:
|
| 137 |
+
df['Sicaklik_Soku'] = 0
|
| 138 |
+
df['Donma_Indeksi'] = 0
|
| 139 |
+
|
| 140 |
+
if 'Ruzgar_Hizi' in df.columns:
|
| 141 |
+
df['Ruzgar_Enerjisi'] = df['Ruzgar_Hizi'] ** 2
|
| 142 |
+
df['Firtina_Gucu'] = df['Ruzgar_Enerjisi'] * (df['Yagis_7G'] + 1).apply(np.log)
|
| 143 |
+
else:
|
| 144 |
+
df['Ruzgar_Enerjisi'] = 0
|
| 145 |
+
df['Firtina_Gucu'] = 0
|
| 146 |
+
|
| 147 |
+
return df.dropna()
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def get_risk_cat(score):
|
| 151 |
+
if score < 40:
|
| 152 |
+
return "Düşük Risk"
|
| 153 |
+
elif 40 <= score < 60:
|
| 154 |
+
return "Orta Risk"
|
| 155 |
+
elif 60 <= score < 70:
|
| 156 |
+
return "RİSKLİ"
|
| 157 |
+
elif 70 <= score < 80:
|
| 158 |
+
return "YÜKSEK RİSKLİ"
|
| 159 |
+
else:
|
| 160 |
+
return "TEHLİKELİ"
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
# ---------------------------------------------------------
|
| 164 |
+
# 2. TANZER PROHİBRİT MODEL (MEGA ENSEMBLE)
|
| 165 |
+
# ---------------------------------------------------------
|
| 166 |
+
class TanzerProhibitModel:
|
| 167 |
+
def __init__(self):
|
| 168 |
+
self.models = {}
|
| 169 |
+
self.weights = {}
|
| 170 |
+
self.model_performance = []
|
| 171 |
+
self.scaler = StandardScaler()
|
| 172 |
+
|
| 173 |
+
def train(self, X, y):
|
| 174 |
+
X_scaled = self.scaler.fit_transform(X)
|
| 175 |
+
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42, stratify=y)
|
| 176 |
+
|
| 177 |
+
print("\n--- PROHİBRİT MODEL EĞİTİM SÜRECİ ---")
|
| 178 |
+
|
| 179 |
+
models_to_train = {
|
| 180 |
+
"RandomForest": RandomForestClassifier(n_estimators=150, max_depth=12, class_weight='balanced', n_jobs=-1,
|
| 181 |
+
random_state=42),
|
| 182 |
+
"ExtraTrees": ExtraTreesClassifier(n_estimators=150, max_depth=12, class_weight='balanced', n_jobs=-1,
|
| 183 |
+
random_state=42),
|
| 184 |
+
"XGBoost": xgb.XGBClassifier(n_estimators=150, max_depth=6, learning_rate=0.1, n_jobs=-1,
|
| 185 |
+
eval_metric='logloss'),
|
| 186 |
+
"LightGBM": lgb.LGBMClassifier(n_estimators=150, learning_rate=0.1, class_weight='balanced', verbose=-1,
|
| 187 |
+
n_jobs=-1),
|
| 188 |
+
"CatBoost": cb.CatBoostClassifier(iterations=150, depth=6, learning_rate=0.1, auto_class_weights='Balanced',
|
| 189 |
+
verbose=0, thread_count=-1),
|
| 190 |
+
"HistGradient": HistGradientBoostingClassifier(learning_rate=0.1, max_iter=150, random_state=42),
|
| 191 |
+
"AdaBoost": AdaBoostClassifier(n_estimators=100, random_state=42),
|
| 192 |
+
"KNN": KNeighborsClassifier(n_neighbors=5, weights='distance', algorithm='kd_tree', leaf_size=40,
|
| 193 |
+
n_jobs=-1),
|
| 194 |
+
"FastSVM": SGDClassifier(loss='hinge', penalty='l2', alpha=0.0001, max_iter=1000, class_weight='balanced',
|
| 195 |
+
n_jobs=-1, random_state=42),
|
| 196 |
+
"NeuralNet": MLPClassifier(hidden_layer_sizes=(100, 50), max_iter=300, activation='relu', solver='adam',
|
| 197 |
+
early_stopping=True, random_state=42),
|
| 198 |
+
"NaiveBayes": GaussianNB()
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
pbar = tqdm(models_to_train.items(), desc="Model Eğitimi", unit="model", ncols=100,
|
| 202 |
+
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}")
|
| 203 |
+
|
| 204 |
+
for name, model in pbar:
|
| 205 |
+
try:
|
| 206 |
+
model.fit(X_train, y_train)
|
| 207 |
+
y_pred = model.predict(X_test)
|
| 208 |
+
score = f1_score(y_test, y_pred)
|
| 209 |
+
|
| 210 |
+
tqdm.write(f" 🔹 {name:<15}: {score:.4f} (F1 Score)")
|
| 211 |
+
|
| 212 |
+
status = "✅ Aktif" if score > 0.35 else "❌ Elendi"
|
| 213 |
+
self.model_performance.append({"Model": name, "F1 Score": score, "Durum": status})
|
| 214 |
+
|
| 215 |
+
if score > 0.35:
|
| 216 |
+
self.models[name] = model
|
| 217 |
+
self.weights[name] = score
|
| 218 |
+
except Exception as e:
|
| 219 |
+
tqdm.write(f" ❌ {name} Hatası: {e}")
|
| 220 |
+
self.model_performance.append({"Model": name, "F1 Score": 0.0, "Durum": "HATA"})
|
| 221 |
+
|
| 222 |
+
total = sum(self.weights.values())
|
| 223 |
+
if total > 0:
|
| 224 |
+
for k in self.weights: self.weights[k] /= total
|
| 225 |
+
else:
|
| 226 |
+
rf = RandomForestClassifier()
|
| 227 |
+
rf.fit(X_train, y_train)
|
| 228 |
+
self.models['RF'] = rf
|
| 229 |
+
self.weights['RF'] = 1.0
|
| 230 |
+
|
| 231 |
+
print("\n" + "=" * 50)
|
| 232 |
+
print(" 🏆 TANZER PROHİBRİT - PERFORMANS KARNESİ 🏆")
|
| 233 |
+
print("=" * 50)
|
| 234 |
+
df_perf = pd.DataFrame(self.model_performance).sort_values("F1 Score", ascending=False)
|
| 235 |
+
print(df_perf.to_string(index=False, formatters={'F1 Score': '{:.4f}'.format}))
|
| 236 |
+
print("=" * 50 + "\n")
|
| 237 |
+
|
| 238 |
+
def predict(self, df_features):
|
| 239 |
+
X = self.scaler.transform(df_features)
|
| 240 |
+
final_prob = np.zeros(len(X))
|
| 241 |
+
for name, model in self.models.items():
|
| 242 |
+
try:
|
| 243 |
+
if hasattr(model, "predict_proba"):
|
| 244 |
+
prob = model.predict_proba(X)[:, 1]
|
| 245 |
+
elif hasattr(model, "decision_function"):
|
| 246 |
+
d = model.decision_function(X)
|
| 247 |
+
prob = 1 / (1 + np.exp(-d))
|
| 248 |
+
else:
|
| 249 |
+
prob = model.predict(X)
|
| 250 |
+
final_prob += prob * self.weights[name]
|
| 251 |
+
except:
|
| 252 |
+
pass
|
| 253 |
+
return final_prob * 100
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
# ---------------------------------------------------------
|
| 257 |
+
# 3. ANA AKIŞ
|
| 258 |
+
# ---------------------------------------------------------
|
| 259 |
+
def main():
|
| 260 |
+
print("⏳ Veri Tabanı Yükleniyor...")
|
| 261 |
+
if not os.path.exists(MGM_DATA_FILE): return
|
| 262 |
+
try:
|
| 263 |
+
use_cols = list(MGM_MAPPING.values())
|
| 264 |
+
df_mgm = pd.read_csv(MGM_DATA_FILE, sep=None, engine='python', encoding='utf-8-sig',
|
| 265 |
+
usecols=lambda c: c.strip() in use_cols)
|
| 266 |
+
df_mgm.columns = df_mgm.columns.str.strip()
|
| 267 |
+
clean_df = pd.DataFrame()
|
| 268 |
+
target_date_col = MGM_MAPPING["Tarih"]
|
| 269 |
+
if target_date_col in df_mgm.columns:
|
| 270 |
+
clean_df["Tarih"] = pd.to_datetime(df_mgm[target_date_col], dayfirst=True, errors='coerce')
|
| 271 |
+
for kod, dosya in MGM_MAPPING.items():
|
| 272 |
+
if kod == "Tarih": continue
|
| 273 |
+
if dosya in df_mgm.columns:
|
| 274 |
+
clean_df[kod] = pd.to_numeric(df_mgm[dosya].astype(str).str.replace(',', '.'), errors='coerce').fillna(
|
| 275 |
+
0)
|
| 276 |
+
else:
|
| 277 |
+
clean_df[kod] = 0.0
|
| 278 |
+
clean_df = clean_df.dropna(subset=['Tarih']).sort_values('Tarih').reset_index(drop=True)
|
| 279 |
+
except:
|
| 280 |
+
return
|
| 281 |
+
|
| 282 |
+
if not os.path.exists(FAULT_FILE): return
|
| 283 |
+
try:
|
| 284 |
+
df_fault = pd.read_csv(FAULT_FILE, sep=None, engine='python')
|
| 285 |
+
target_col = [c for c in df_fault.columns if 'tarih' in c.lower() or 'date' in c.lower()][0]
|
| 286 |
+
df_fault['Tarih'] = df_fault[target_col].apply(convert_excel_date)
|
| 287 |
+
if 'Enlem' in df_fault.columns:
|
| 288 |
+
df_fault['Enlem'] = df_fault['Enlem'].apply(clean_coord)
|
| 289 |
+
df_fault['Boylam'] = df_fault['Boylam'].apply(clean_coord)
|
| 290 |
+
df_fault = df_fault.dropna(subset=['Tarih'])
|
| 291 |
+
except:
|
| 292 |
+
return
|
| 293 |
+
|
| 294 |
+
print("⏳ Veri Seti İşleniyor (Pencere: -7 / +5 Saat)...")
|
| 295 |
+
full_data = calculate_features(clean_df)
|
| 296 |
+
full_data['Ariza_Durumu'] = 0
|
| 297 |
+
|
| 298 |
+
for f_date in df_fault['Tarih']:
|
| 299 |
+
try:
|
| 300 |
+
start_risk = f_date - timedelta(days=7)
|
| 301 |
+
end_risk = f_date + timedelta(hours=5)
|
| 302 |
+
if start_risk < full_data.index.max() and end_risk > full_data.index.min():
|
| 303 |
+
full_data.loc[start_risk:end_risk, 'Ariza_Durumu'] = 1
|
| 304 |
+
except:
|
| 305 |
+
continue
|
| 306 |
+
|
| 307 |
+
pos = full_data[full_data['Ariza_Durumu'] == 1]
|
| 308 |
+
neg_pool = full_data[full_data['Ariza_Durumu'] == 0]
|
| 309 |
+
n_neg = min(len(pos) * 5, len(neg_pool))
|
| 310 |
+
|
| 311 |
+
if n_neg > 0:
|
| 312 |
+
neg = neg_pool.sample(n=n_neg, random_state=42)
|
| 313 |
+
train_set = pd.concat([pos, neg])
|
| 314 |
+
else:
|
| 315 |
+
return
|
| 316 |
+
|
| 317 |
+
features = ['Sicaklik', 'Nem', 'Yagis', 'Ruzgar_Hizi', 'Basinc',
|
| 318 |
+
'Yagis_7G', 'Basinc_Trend', 'Basinc_Stabilite',
|
| 319 |
+
'Sicaklik_Soku', 'Donma_Indeksi', 'Ruzgar_Enerjisi', 'Firtina_Gucu']
|
| 320 |
+
|
| 321 |
+
# EĞİTİM
|
| 322 |
+
print("🚀 TANZER PROHİBRİT MODEL EĞİTİLİYOR...")
|
| 323 |
+
ensemble = TanzerProhibitModel()
|
| 324 |
+
ensemble.train(train_set[features], train_set['Ariza_Durumu'])
|
| 325 |
+
print("✅ Eğitim Tamamlandı.")
|
| 326 |
+
|
| 327 |
+
# TAHMİN
|
| 328 |
+
print("\n⏳ 14 Hat İçin Analiz Başlıyor (Lütfen Bekleyiniz)...")
|
| 329 |
+
possible_names = ["Hat_Adı_2", "Hat Adı", "Hat_Adi", "HAT_ADI", "HAT ADI"]
|
| 330 |
+
hat_col = next((c for c in df_fault.columns if c in possible_names), df_fault.columns[0])
|
| 331 |
+
unique_lines = df_fault[[hat_col, 'Enlem', 'Boylam']].drop_duplicates(subset=[hat_col]).dropna().head(14)
|
| 332 |
+
|
| 333 |
+
results = []
|
| 334 |
+
line_data = []
|
| 335 |
+
session = requests.Session()
|
| 336 |
+
|
| 337 |
+
pbar = tqdm(unique_lines.iterrows(), total=len(unique_lines), unit="hat",
|
| 338 |
+
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]")
|
| 339 |
+
|
| 340 |
+
for _, row in pbar:
|
| 341 |
+
hat_adi = row[hat_col]
|
| 342 |
+
lat, lon = row['Enlem'], row['Boylam']
|
| 343 |
+
pbar.set_description(f"Analiz: {str(hat_adi)[:20]}")
|
| 344 |
+
|
| 345 |
+
try:
|
| 346 |
+
url = "https://api.open-meteo.com/v1/forecast"
|
| 347 |
+
params = {
|
| 348 |
+
"latitude": lat, "longitude": lon,
|
| 349 |
+
"hourly": "temperature_2m,relative_humidity_2m,rain,wind_speed_10m,surface_pressure",
|
| 350 |
+
"past_days": 7, "forecast_days": 3, "timezone": "auto"
|
| 351 |
+
}
|
| 352 |
+
r = session.get(url, params=params, timeout=10, verify=False)
|
| 353 |
+
if r.status_code == 200:
|
| 354 |
+
data = r.json()
|
| 355 |
+
df_api = pd.DataFrame(data['hourly'])
|
| 356 |
+
df_api['time'] = pd.to_datetime(df_api['time'])
|
| 357 |
+
df_api = df_api.rename(
|
| 358 |
+
columns={'time': 'Tarih', 'temperature_2m': 'Sicaklik', 'relative_humidity_2m': 'Nem',
|
| 359 |
+
'rain': 'Yagis', 'wind_speed_10m': 'Ruzgar_Hizi', 'surface_pressure': 'Basinc'})
|
| 360 |
+
|
| 361 |
+
df_proc = calculate_features(df_api)
|
| 362 |
+
df_proc.index = df_proc.index.tz_localize(None)
|
| 363 |
+
now = datetime.now()
|
| 364 |
+
future_df = df_proc[df_proc.index >= now].copy()
|
| 365 |
+
|
| 366 |
+
if not future_df.empty:
|
| 367 |
+
for c in features:
|
| 368 |
+
if c not in future_df.columns: future_df[c] = 0
|
| 369 |
+
|
| 370 |
+
risk = ensemble.predict(future_df[features])
|
| 371 |
+
future_df['Risk'] = risk
|
| 372 |
+
|
| 373 |
+
max_idx = future_df['Risk'].idxmax()
|
| 374 |
+
max_risk = future_df.loc[max_idx, 'Risk']
|
| 375 |
+
cat = get_risk_cat(max_risk)
|
| 376 |
+
|
| 377 |
+
tqdm.write(f"✅ {str(hat_adi)[:30]:<30} : %{max_risk:.1f} ({cat})")
|
| 378 |
+
|
| 379 |
+
results.append({"Hat": hat_adi, "Risk (%)": max_risk, "Kategori": cat, "Zaman": max_idx})
|
| 380 |
+
p_data = future_df[['Risk']].reset_index()
|
| 381 |
+
p_data['Hat'] = hat_adi
|
| 382 |
+
line_data.append(p_data)
|
| 383 |
+
else:
|
| 384 |
+
tqdm.write(f"❌ {str(hat_adi)[:30]} : API Hatası")
|
| 385 |
+
except:
|
| 386 |
+
tqdm.write(f"❌ {str(hat_adi)[:30]} : Bağlantı Hatası")
|
| 387 |
+
|
| 388 |
+
# --- AKADEMİK RAPORLAMA VE GÖRSELLEŞTİRME ---
|
| 389 |
+
if results:
|
| 390 |
+
df_res = pd.DataFrame(results).sort_values("Risk (%)", ascending=False)
|
| 391 |
+
df_res.to_excel(OUTPUT_EXCEL, index=False)
|
| 392 |
+
|
| 393 |
+
# Seaborn Akademik Tema Ayarı
|
| 394 |
+
sns.set_theme(style="whitegrid", font_scale=1.1, rc={"grid.linewidth": 0.6, "axes.linewidth": 1})
|
| 395 |
+
|
| 396 |
+
# --- GRAFİK 1: ÇUBUK (BAR) GRAFİĞİ ---
|
| 397 |
+
plt.figure(figsize=(14, 10))
|
| 398 |
+
|
| 399 |
+
# Renkleri skora göre akademik paletten seç
|
| 400 |
+
colors = [ACADEMIC_COLORS['dusuk'] if x < 40 else
|
| 401 |
+
ACADEMIC_COLORS['orta'] if x < 60 else
|
| 402 |
+
ACADEMIC_COLORS['riskli'] if x < 70 else
|
| 403 |
+
ACADEMIC_COLORS['yuksek'] if x < 80 else
|
| 404 |
+
ACADEMIC_COLORS['tehlikeli'] for x in df_res['Risk (%)']]
|
| 405 |
+
|
| 406 |
+
ax = sns.barplot(x='Risk (%)', y='Hat', data=df_res, palette=colors, edgecolor='.2', linewidth=0.8)
|
| 407 |
+
|
| 408 |
+
# Kritik Eşik Çizgisi (Daha belirgin)
|
| 409 |
+
plt.axvline(75, color=ACADEMIC_COLORS['yuksek'], linestyle='--', linewidth=2.5, label='Kritik Risk Eşiği (%75)')
|
| 410 |
+
|
| 411 |
+
# Değerleri çubukların ucuna yaz
|
| 412 |
+
for i, v in enumerate(df_res['Risk (%)']):
|
| 413 |
+
ax.text(v + 0.5, i, f"%{v:.1f}", fontweight='bold', va='center', fontsize=12, color='black')
|
| 414 |
+
|
| 415 |
+
plt.title('Enerji İletim Hatlarında Maksimum Risk Analizi\n(TANZER PROHİBRİT MODEL SONUÇLARI)',
|
| 416 |
+
fontweight='bold', fontsize=16, pad=20)
|
| 417 |
+
plt.xlabel('Hesaplanan Risk Skoru (%)', fontweight='bold', fontsize=12)
|
| 418 |
+
plt.ylabel('Hat Adı', fontweight='bold', fontsize=12)
|
| 419 |
+
plt.legend(loc='lower right', frameon=True)
|
| 420 |
+
plt.tight_layout()
|
| 421 |
+
plt.savefig(PLOT_BAR, dpi=300) # Yüksek çözünürlük
|
| 422 |
+
|
| 423 |
+
# --- GRAFİK 2: ÇİZGİ (LINE) GRAFİĞİ ---
|
| 424 |
+
if line_data:
|
| 425 |
+
all_lines = pd.concat(line_data)
|
| 426 |
+
plt.figure(figsize=(16, 10))
|
| 427 |
+
|
| 428 |
+
# Ana Çizgiler (Daha kalın ve profesyonel palet)
|
| 429 |
+
sns.lineplot(data=all_lines, x='Tarih', y='Risk', hue='Hat', palette='tab10', linewidth=3, alpha=0.9)
|
| 430 |
+
|
| 431 |
+
# Maksimum Noktaları İşaretle
|
| 432 |
+
for hat_name in df_res['Hat']:
|
| 433 |
+
hat_df = all_lines[all_lines['Hat'] == hat_name]
|
| 434 |
+
if not hat_df.empty:
|
| 435 |
+
max_row = hat_df.loc[hat_df['Risk'].idxmax()]
|
| 436 |
+
# Kırmızı nokta ve belirgin beyaz çerçeve
|
| 437 |
+
plt.scatter(max_row['Tarih'], max_row['Risk'], color=ACADEMIC_COLORS['yuksek'], s=120, zorder=5,
|
| 438 |
+
edgecolor='white', linewidth=2)
|
| 439 |
+
# Etiket kutusu
|
| 440 |
+
plt.annotate(f"%{max_row['Risk']:.0f}",
|
| 441 |
+
(max_row['Tarih'], max_row['Risk']),
|
| 442 |
+
textcoords="offset points", xytext=(0, 12), ha='center',
|
| 443 |
+
bbox=dict(boxstyle="round,pad=0.4", fc="white", ec=ACADEMIC_COLORS['yuksek'], lw=1.5),
|
| 444 |
+
fontsize=10, fontweight='bold')
|
| 445 |
+
|
| 446 |
+
# --- ARKA PLAN RİSK BÖLGELERİ (ÇOK DAHA BELİRGİN) ---
|
| 447 |
+
# Alpha değerleri 0.08'den 0.20-0.25 seviyesine çıkarıldı
|
| 448 |
+
plt.axhspan(0, 40, color=ACADEMIC_COLORS['dusuk'], alpha=0.20, label='Düşük Risk Bölgesi')
|
| 449 |
+
plt.axhspan(40, 60, color=ACADEMIC_COLORS['orta'], alpha=0.20, label='Orta Risk Bölgesi')
|
| 450 |
+
plt.axhspan(60, 70, color=ACADEMIC_COLORS['riskli'], alpha=0.25, label='Riskli Bölge')
|
| 451 |
+
plt.axhspan(70, 80, color=ACADEMIC_COLORS['yuksek'], alpha=0.25, label='Yüksek Risk Bölgesi')
|
| 452 |
+
plt.axhspan(80, 105, color=ACADEMIC_COLORS['tehlikeli'], alpha=0.30, label='Tehlikeli Bölge')
|
| 453 |
+
|
| 454 |
+
plt.ylim(0, 105)
|
| 455 |
+
plt.title('72 Saatlik Detaylı Zamansal Risk Değişimi', fontweight='bold', fontsize=18, pad=20)
|
| 456 |
+
plt.xlabel('Zaman (Gün/Saat)', fontweight='bold', fontsize=12)
|
| 457 |
+
plt.ylabel('Risk Yüzdesi (%)', fontweight='bold', fontsize=12)
|
| 458 |
+
|
| 459 |
+
# Tarih formatını iyileştir
|
| 460 |
+
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%m %H:%M'))
|
| 461 |
+
plt.xticks(rotation=45)
|
| 462 |
+
|
| 463 |
+
# Lejantı dışarı al ve düzenle
|
| 464 |
+
plt.legend(bbox_to_anchor=(1.02, 1), loc='upper left', title="Hatlar ve Risk Bölgeleri",
|
| 465 |
+
frameon=True, shadow=True, title_fontsize='12', fontsize='11')
|
| 466 |
+
|
| 467 |
+
plt.grid(True, linestyle='-', linewidth=0.8, alpha=0.7) # Gridleri belirginleştir
|
| 468 |
+
plt.tight_layout()
|
| 469 |
+
plt.savefig(PLOT_LINE, dpi=300) # Yüksek çözünürlük
|
| 470 |
+
|
| 471 |
+
print(f"\n✅ BAŞARILI! Akademik Rapor ve Yüksek Çözünürlüklü Grafikler Oluşturuldu.")
|
| 472 |
+
print(f" Çıktı Klasörü: {OUTPUT_DIR}")
|
| 473 |
+
else:
|
| 474 |
+
print("\n❌ Sonuç üretilemedi.")
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
if __name__ == "__main__":
|
| 478 |
+
main()
|