File size: 4,389 Bytes
dbbcfdf
 
f50ea8d
54e9f72
accb9cc
 
54e9f72
 
678ebbd
54e9f72
 
 
 
 
 
 
 
 
 
 
5dc672c
54e9f72
678ebbd
accb9cc
678ebbd
 
5dc672c
678ebbd
 
 
 
 
54e9f72
accb9cc
 
 
678ebbd
 
 
 
54e9f72
accb9cc
 
7170014
 
 
 
 
8fd92f9
54e9f72
 
 
7170014
 
54e9f72
 
 
 
 
678ebbd
54e9f72
 
 
 
 
678ebbd
54e9f72
 
d09b7ea
 
 
accb9cc
 
 
 
 
7170014
accb9cc
2db5e22
54e9f72
 
2db5e22
accb9cc
5dc672c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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)