/
DiHASTRO
/
pp-heat-map
Обзор
Документация
Войти
/
DiHASTRO
/
pp-heat-map
Код
Запросы
3
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scripts/classify.py
137 строк
5 KB
Sm4rt1
Добавлена продвинутая классификация тепловых излучений
24 ноя 2025, 18:48
24 ноя 2025, 18:48
6479073
Код
Авторство
О чём код?
import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans from sklearn.neighbors import NearestNeighbors import folium import glob files = glob.glob("scripts/static/*.csv") dfs = [pd.read_csv(f) for f in files] data = pd.concat(dfs, ignore_index=True) print("Всего точек:", len(data)) data = data.dropna(subset=['bright_ti4', 'bright_ti5', 'frp']) data['daynight'] = data['daynight'].map({'D': 1, 'N': 0}) data['confidence'] = data['confidence'].map({'h': 3, 'n': 2, 'l': 1}).fillna(0) X = data[['bright_ti4', 'bright_ti5', 'frp', 'confidence', 'daynight']] scaler = StandardScaler() X_scaled = scaler.fit_transform(X) kmeans = KMeans(n_clusters=3, random_state=42) data['cluster_intensity'] = kmeans.fit_predict(X_scaled) coords = np.radians(data[['latitude', 'longitude']].values) nbrs = NearestNeighbors(radius=5/6371.0, metric='haversine').fit(coords) distances, indices = nbrs.radius_neighbors(coords) data['neighbor_count'] = [len(i) for i in indices] data['std_local_frp'] = [np.std(data.iloc[idx]['frp']) if len(idx) > 1 else 0 for idx in indices] data['std_local_ti4'] = [np.std(data.iloc[idx]['bright_ti4']) if len(idx) > 1 else 0 for idx in indices] data['mean_local_frp'] = [np.mean(data.iloc[idx]['frp']) if len(idx) > 1 else data.iloc[i]['frp'] for i, idx in enumerate(indices)] def classify_source(row): # --- 1. Промышленные факелы --- # стабильные, горячие источники с низкой изменчивостью FRP и Ti4 if row['frp'] > 10 and row['std_local_ti4'] < 6 and row['std_local_frp'] < 8 and row['neighbor_count'] >= 2: return 'industrial_flare' # --- 2. Пожары --- # высокая FRP, большая изменчивость FRP и Ti4, группа близких точек elif row['frp'] > 10 and row['neighbor_count'] > 5 and (row['std_local_frp'] >= 8 or row['std_local_ti4'] >= 8): return 'fire_cluster' # --- 3. Городское тепло --- # слабое излучение, часто ночью, но с высокой плотностью точек elif row['frp'] < 10 and row['daynight'] == 0 and row['neighbor_count'] > 5: return 'heat' # --- 4. Слабые/единичные источники --- elif row['frp'] < 4 and row['neighbor_count'] <= 2: return 'weak_source' # --- 5. Всё остальное --- else: return 'unknown' data['source_type'] = data.apply(classify_source, axis=1) def smooth_label(i, idx_list): if len(idx_list) < 3: return data.loc[i, 'source_type'] neighbor_types = data.iloc[idx_list]['source_type'] main_type = neighbor_types.mode()[0] current_type = data.loc[i, 'source_type'] if current_type != main_type and neighbor_types.value_counts().max() / len(neighbor_types) > 0.6: return main_type return current_type data['source_type_smoothed'] = [ smooth_label(i, idx) for i, idx in enumerate(indices) ] changed = np.sum(data['source_type_smoothed'] != data['source_type']) print(f"Исправлено точек: {changed} ({100*changed/len(data):.1f}%)") data['source_type'] = data['source_type_smoothed'] def resolve_unknown(row): if row['source_type'] != 'unknown': return row['source_type'] if row['frp'] > 35: return 'industrial_flare' elif row['frp'] > 10: return 'fire_cluster' elif row['daynight'] == 0 and row['neighbor_count'] > 2: return 'heat' elif row['frp'] < 3: return 'weak_source' else: return 'heat' data['source_type'] = data.apply(resolve_unknown, axis=1) print("\nТипы источников после коррекции:") print(data['source_type'].value_counts()) print("\nСредние по типам:") print(data.groupby('source_type')[['bright_ti4','frp','neighbor_count','std_local_frp']].mean()) m = folium.Map(location=[52.0, 52.9], zoom_start=6) color_map = { 'fire_cluster': 'red', 'industrial_flare': 'orange', 'heat': 'blue', 'weak_source': 'gray', } for _, row in data.iterrows(): radius = max(3, min(12, row['frp'] / 5)) popup_html = f""" <b>Источник:</b> {row['source_type']}<br> <b>FRP:</b> {row['frp']:.1f} МВт<br> <b>bright_ti4:</b> {row['bright_ti4']:.1f} K<br> <b>bright_ti5:</b> {row['bright_ti5']:.1f} K<br> <b>Confidence:</b> {row['confidence']}<br> <b>Day/Night:</b> {'Day' if row['daynight']==1 else 'Night'}<br> <b>Neighbors (5 км):</b> {row['neighbor_count']}<br> <b>Std FRP (локально):</b> {row['std_local_frp']:.2f}<br> <b>Std Ti4 (локально):</b> {row['std_local_ti4']:.2f}<br> """ folium.CircleMarker( [row['latitude'], row['longitude']], radius=radius, color=color_map.get(row['source_type'], 'black'), fill=True, fill_opacity=0.8, popup=folium.Popup(popup_html, max_width=300) ).add_to(m) m.save("scripts/static/source_types_refined.html")