Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import numpy as np | |
| from config import CITIES_COORDS | |
| def apply_feature_engineering(df_input): | |
| df = df_input.copy() | |
| # 1. Nettoyage des types (Évite l'erreur TypeError str vs int) | |
| df['time'] = pd.to_datetime(df['time']) | |
| numeric_cols = [ | |
| 'temperature_2m_max', 'temperature_2m_min', 'temperature_2m_mean', | |
| 'precipitation_sum', 'wind_speed_10m_max', 'wind_gusts_10m_max', | |
| 'shortwave_radiation_sum', 'et0_fao_evapotranspiration', 'sunshine_duration', 'daylight_duration' | |
| ] | |
| for col in numeric_cols: | |
| if col in df.columns: | |
| df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0) | |
| # 2. Tri impératif pour les calculs de séries temporelles | |
| df = df.sort_values(['city', 'time']).reset_index(drop=True) | |
| # 3. Calendrier | |
| df['mois'] = df['time'].dt.month | |
| df['annee'] = df['time'].dt.year | |
| df['quarter'] = df['time'].dt.quarter | |
| df['day_of_year'] = df['time'].dt.dayofyear | |
| df['month_sin'] = np.sin(2 * np.pi * df['mois'] / 12) | |
| df['month_cos'] = np.cos(2 * np.pi * df['mois'] / 12) | |
| df['day_sin'] = np.sin(2 * np.pi * df['day_of_year'] / 365) | |
| df['day_cos'] = np.cos(2 * np.pi * df['day_of_year'] / 365) | |
| # 4. Physique et Indices | |
| df['amplitude_thermique'] = df['temperature_2m_max'] - df['temperature_2m_min'] | |
| df['ecart_ressenti'] = df['apparent_temperature_mean'] - df['temperature_2m_mean'] | |
| df['bilan_hydrique'] = df['precipitation_sum'] - df['et0_fao_evapotranspiration'] | |
| df['sunshine_ratio'] = df['sunshine_duration'] / (df['daylight_duration'] + 1e-6) | |
| df['gust_ratio'] = df['wind_gusts_10m_max'] / (df['wind_speed_10m_max'] + 1e-6) | |
| df['heat_stress'] = df['temperature_2m_mean'] * df['et0_fao_evapotranspiration'] | |
| df['temp_per_radiation'] = df['temperature_2m_mean'] / (df['shortwave_radiation_sum'] + 1e-6) | |
| df['is_dry_season'] = df['mois'].isin([11, 12, 1, 2, 3]).astype(int) | |
| df['is_weekend'] = df['time'].dt.dayofweek.isin([5, 6]).astype(int) | |
| df['is_no_rain'] = (df['precipitation_sum'] < 0.1).astype(int) | |
| df['is_no_wind'] = (df['wind_speed_10m_max'] < 5).astype(int) | |
| df['stagnation_index'] = ((df['wind_speed_10m_max'] < 15) & (df['precipitation_sum'] == 0)).astype(int) | |
| df['is_hot_day'] = (df['temperature_2m_max'] >= 35).astype(int) | |
| df['is_heavy_rain'] = (df['precipitation_sum'] >= 20).astype(int) | |
| # 5. Groupement par ville pour les Lags et Rolling | |
| # On utilise groupby().transform pour garder la taille originale du DF | |
| grouped = df.groupby('city') | |
| for lag in [1, 3, 7]: | |
| df[f'temp_lag{lag}'] = grouped['temperature_2m_mean'].shift(lag) | |
| df[f'wind_lag{lag}'] = grouped['wind_speed_10m_max'].shift(lag) | |
| df[f'precip_lag{lag}'] = grouped['precipitation_sum'].shift(lag) | |
| df[f'wind_dir_lag{lag}'] = grouped['wind_direction_10m_dominant'].shift(lag) | |
| df[f'sunshine_lag{lag}'] = grouped['sunshine_duration'].shift(lag) | |
| df['temp_roll7'] = grouped['temperature_2m_mean'].transform(lambda x: x.rolling(7, min_periods=1).mean()) | |
| df['precip_roll7'] = grouped['precipitation_sum'].transform(lambda x: x.rolling(7, min_periods=1).mean()) | |
| df['wind_roll7'] = grouped['wind_speed_10m_max'].transform(lambda x: x.rolling(7, min_periods=1).mean()) | |
| df['temp_roll30'] = grouped['temperature_2m_mean'].transform(lambda x: x.rolling(30, min_periods=1).mean()) | |
| df['temp_anomaly'] = df['temperature_2m_mean'] - df['temp_roll30'] | |
| df['precip_cumul7'] = grouped['precipitation_sum'].transform(lambda x: x.rolling(7, min_periods=1).sum()) | |
| df['precip_cumul3'] = grouped['precipitation_sum'].transform(lambda x: x.rolling(3, min_periods=1).sum()) | |
| def get_region_name(city): | |
| return CITIES_COORDS.get(city, {}).get('region', 'Littoral') | |
| df['region'] = df['city'].apply(get_region_name) | |
| def categorize_weather(code): | |
| if code == 0: return 'Ciel dégagé' | |
| elif code in [1, 2]: return 'Nuageux' | |
| elif code == 3: return 'Couvert' | |
| else: return 'Pluie/Bruine' | |
| df['weather_categorie'] = df['weather_code'].apply(categorize_weather) | |
| df['weather_encoded'] = df['weather_code'].astype(float) | |
| cols_to_fix = [c for c in df.columns if 'lag' in c or 'roll' in c] | |
| for col in cols_to_fix: | |
| df[col] = df.groupby('city')[col].ffill().bfill() | |
| return df.fillna(0) |