ArthurGamaJorge commited on
Commit
a2f70bd
·
1 Parent(s): 37886e5

Corrigir previsão de casos

Browse files
Files changed (1) hide show
  1. predict.py +61 -40
predict.py CHANGED
@@ -9,7 +9,6 @@ import tensorflow as tf
9
  import matplotlib.pyplot as plt
10
  import base64
11
  from io import BytesIO
12
- import math
13
 
14
  warnings.filterwarnings('ignore')
15
  plt.style.use('seaborn-v0_8-darkgrid')
@@ -32,15 +31,13 @@ class DenguePredictor:
32
  self.load_assets()
33
 
34
  def load_assets(self):
35
- AI_ASSETS_DIR = self.project_root / "models"
36
  INFERENCE_PATH = self.project_root / "data" / "inference_data.parquet"
37
  SCALER_DIR = AI_ASSETS_DIR / "scalers"
38
  MODEL_PATH = AI_ASSETS_DIR / "checkpoints" / "model_checkpoint_best.keras"
39
 
40
-
41
  self.scaler_dir = SCALER_DIR
42
 
43
- # Carregar dados atualizados
44
  df_master = pd.read_parquet(INFERENCE_PATH)
45
  df_master['codigo_ibge'] = df_master['codigo_ibge'].astype(int)
46
  df_master['data_semana_iso'] = pd.to_datetime(
@@ -51,7 +48,6 @@ class DenguePredictor:
51
  self.df_master = df_master
52
  self.municipios = df_master[['codigo_ibge', 'municipio']].drop_duplicates().sort_values('codigo_ibge')
53
 
54
- # Carregar modelo LSTM
55
  self.model = tf.keras.models.load_model(MODEL_PATH)
56
 
57
  def plot_to_base64(self):
@@ -70,7 +66,6 @@ class DenguePredictor:
70
  return scaler.inverse_transform(dummy)[:, 0]
71
 
72
  def get_scalers(self, ibge_code: int):
73
- """Carrega scalers do município apenas uma vez."""
74
  if ibge_code not in self.scaler_cache:
75
  dynamic_scaler = joblib.load(self.scaler_dir / "dynamics" / f"{ibge_code}_dynamic.pkl")
76
  static_scaler = joblib.load(self.scaler_dir / "statics" / f"{ibge_code}_static.pkl")
@@ -78,70 +73,97 @@ class DenguePredictor:
78
  return self.scaler_cache[ibge_code]["dynamic"], self.scaler_cache[ibge_code]["static"]
79
 
80
  def predict(self, ibge_code: int, weeks_to_predict: int):
81
- # Carregar scalers
82
  scaler_dyn, scaler_static = self.get_scalers(ibge_code)
83
-
84
- # Obter dados do município
85
  df_mun = self.df_master[self.df_master['codigo_ibge'] == ibge_code].copy().reset_index(drop=True)
86
  if df_mun.empty:
87
  raise ValueError(f"Não há dados para o município {ibge_code}")
88
- municipio_name = self.municipios[self.municipios['codigo_ibge'] == ibge_code].iloc[0]['municipio']
89
 
 
90
  dynamic_features = list(self.feature_names_pt.keys())
91
- # Pegar últimas 'sequence_length' semanas para iniciar a previsão
92
- last_sequence = df_mun[dynamic_features].iloc[-self.sequence_length:].copy()
93
-
94
- # Substituir NaNs da sequência inicial por zeros apenas para o scaler (não para salvar)
95
- # Isso é só para evitar erro do scaler; depois o modelo vai gerar previsão real
96
- last_sequence_filled = last_sequence.fillna(0)
97
- dynamic_sequence_scaled = scaler_dyn.transform(last_sequence_filled)
98
 
99
- static_data = df_mun[["latitude", "longitude"]].iloc[0].values.reshape(1, -1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  static_input = scaler_static.transform(static_data)
101
-
102
- # Lookup de clima por (ano, semana)
103
  climate_lookup = {(row['ano'], row['semana']): row for _, row in df_mun.iterrows()}
104
-
105
  predictions = []
106
- last_date = df_mun.iloc[-1]['data_semana_iso']
 
107
 
108
  for i in range(weeks_to_predict):
109
- dynamic_input = np.array([dynamic_sequence_scaled], dtype=np.float32)
110
  pred_scaled = self.model.predict([dynamic_input, static_input], verbose=0)[0][0]
 
 
 
 
111
 
112
- pred_real = self.inverse_transform_cases(scaler_dyn, np.array([pred_scaled]))[0]
113
-
114
- future_date = last_date + timedelta(weeks=i + 1)
115
  predictions.append({
116
  "date": future_date.strftime('%Y-%m-%d'),
117
  "predicted_cases": max(0, round(pred_real))
118
  })
119
-
 
120
  future_year, future_week = future_date.year, future_date.isocalendar()[1]
121
  if (future_year, future_week) in climate_lookup:
122
  future_climate = climate_lookup[(future_year, future_week)]
123
  else:
124
- future_climate = df_mun[dynamic_features[1:]].tail(4).mean(axis=0, skipna=True)
125
-
126
 
127
  new_row = np.zeros(len(dynamic_features), dtype=np.float32)
128
- new_row[0] = pred_scaled # numero_casos previsto
129
  for j, feat in enumerate(dynamic_features[1:], start=1):
130
  new_row[j] = future_climate[feat]
131
-
132
- # 6) Escala a linha futura e atualiza sequência
133
  new_row_scaled = scaler_dyn.transform(new_row.reshape(1, -1))[0]
134
- dynamic_sequence_scaled = np.vstack([dynamic_sequence_scaled[1:], new_row_scaled])
135
 
 
136
  # Histórico das últimas 52 semanas
137
- historic_data = [
138
- {"date": row['data_semana_iso'].strftime('%Y-%m-%d'),
139
- "cases": int(row["numero_casos"]) if not pd.isna(row["numero_casos"]) else 0}
140
- for _, row in df_mun.tail(52).iterrows()
141
- ]
142
 
143
 
144
- # Análise de lag
145
  df_analysis = df_mun[dynamic_features].rename(columns=self.feature_names_pt)
146
  max_lag = 12
147
  cases_col_name = 'Nº de Casos de Dengue'
@@ -157,7 +179,6 @@ class DenguePredictor:
157
  corrs.append(corr)
158
  lag_correlations[col] = corrs
159
 
160
-
161
  plt.figure(figsize=(10, 6), facecolor='#18181b')
162
  ax = plt.gca()
163
  ax.set_facecolor('#18181b')
 
9
  import matplotlib.pyplot as plt
10
  import base64
11
  from io import BytesIO
 
12
 
13
  warnings.filterwarnings('ignore')
14
  plt.style.use('seaborn-v0_8-darkgrid')
 
31
  self.load_assets()
32
 
33
  def load_assets(self):
34
+ AI_ASSETS_DIR = self.project_root / "models"
35
  INFERENCE_PATH = self.project_root / "data" / "inference_data.parquet"
36
  SCALER_DIR = AI_ASSETS_DIR / "scalers"
37
  MODEL_PATH = AI_ASSETS_DIR / "checkpoints" / "model_checkpoint_best.keras"
38
 
 
39
  self.scaler_dir = SCALER_DIR
40
 
 
41
  df_master = pd.read_parquet(INFERENCE_PATH)
42
  df_master['codigo_ibge'] = df_master['codigo_ibge'].astype(int)
43
  df_master['data_semana_iso'] = pd.to_datetime(
 
48
  self.df_master = df_master
49
  self.municipios = df_master[['codigo_ibge', 'municipio']].drop_duplicates().sort_values('codigo_ibge')
50
 
 
51
  self.model = tf.keras.models.load_model(MODEL_PATH)
52
 
53
  def plot_to_base64(self):
 
66
  return scaler.inverse_transform(dummy)[:, 0]
67
 
68
  def get_scalers(self, ibge_code: int):
 
69
  if ibge_code not in self.scaler_cache:
70
  dynamic_scaler = joblib.load(self.scaler_dir / "dynamics" / f"{ibge_code}_dynamic.pkl")
71
  static_scaler = joblib.load(self.scaler_dir / "statics" / f"{ibge_code}_static.pkl")
 
73
  return self.scaler_cache[ibge_code]["dynamic"], self.scaler_cache[ibge_code]["static"]
74
 
75
  def predict(self, ibge_code: int, weeks_to_predict: int):
 
76
  scaler_dyn, scaler_static = self.get_scalers(ibge_code)
 
 
77
  df_mun = self.df_master[self.df_master['codigo_ibge'] == ibge_code].copy().reset_index(drop=True)
78
  if df_mun.empty:
79
  raise ValueError(f"Não há dados para o município {ibge_code}")
 
80
 
81
+ municipio_name = self.municipios[self.municipios['codigo_ibge'] == ibge_code].iloc[0]['municipio']
82
  dynamic_features = list(self.feature_names_pt.keys())
 
 
 
 
 
 
 
83
 
84
+ # --- LÓGICA DE PRÉ-PREDIÇÃO PARA O PRIMEIRO NaN ---
85
+ # Verifica se a última semana no dataframe tem casos NaN (nosso cenário da semana 32)
86
+ if pd.isna(df_mun['numero_casos'].iloc[-1]):
87
+ print("INFO: Última semana com NaN detectada. Realizando predição inicial para preenchê-la.")
88
+
89
+ # 1. Pega as 12 semanas ANTERIORES à semana com NaN. Esta sequência está completa.
90
+ # Ex: se a linha 100 é a semana 32 (com NaN), pegamos da 88 a 99 (12 semanas)
91
+ initial_sequence_df = df_mun.iloc[-self.sequence_length-1:-1]
92
+
93
+ # Garante que os dados de CLIMA desta sequência inicial estão preenchidos (apenas por segurança)
94
+ for col in dynamic_features[1:]: # Ignora 'numero_casos' aqui pois já sabemos que está preenchido
95
+ if initial_sequence_df[col].isna().any():
96
+ col_mean = initial_sequence_df[col].mean(skipna=True)
97
+ initial_sequence_df.loc[:, col] = initial_sequence_df[col].fillna(col_mean)
98
+
99
+ # 2. Prepara os dados de entrada para o modelo
100
+ dynamic_input_scaled = scaler_dyn.transform(initial_sequence_df[dynamic_features])
101
+ dynamic_input = np.array([dynamic_input_scaled], dtype=np.float32)
102
+
103
+ # Os dados estáticos são da própria semana que queremos prever (a com NaN)
104
+ static_input = scaler_static.transform(df_mun.iloc[-1][["latitude", "longitude"]].values.reshape(1, -1))
105
+
106
+ # 3. Faz a predição da semana que continha o NaN
107
+ pred_scaled_initial = self.model.predict([dynamic_input, static_input], verbose=0)[0][0]
108
+ pred_real_initial = self.inverse_transform_cases(scaler_dyn, np.array([pred_scaled_initial]))[0]
109
+
110
+ predicted_cases_initial = max(0, round(pred_real_initial))
111
+
112
+ # 4. ATUALIZA o dataframe em memória com o valor previsto, substituindo o NaN
113
+ df_mun.iloc[-1, df_mun.columns.get_loc('numero_casos')] = predicted_cases_initial
114
+ print(f"INFO: Valor previsto para {df_mun.iloc[-1]['data_semana_iso'].date()}: {predicted_cases_initial} casos")
115
+
116
+ # --- FIM DA LÓGICA DE PRÉ-PREDIÇÃO ---
117
+
118
+ # Agora, o loop de predição principal começa a partir de um dataframe completo
119
+
120
+ # Pega a sequência final ATUALIZADA para iniciar o loop de predições futuras
121
+ last_sequence_scaled = scaler_dyn.transform(df_mun[dynamic_features].iloc[-self.sequence_length:])
122
+
123
+ static_data = df_mun[["latitude", "longitude"]].iloc[-1].values.reshape(1, -1)
124
  static_input = scaler_static.transform(static_data)
125
+
 
126
  climate_lookup = {(row['ano'], row['semana']): row for _, row in df_mun.iterrows()}
 
127
  predictions = []
128
+ # A última data REAL no dataframe original
129
+ last_real_date = df_mun.iloc[-1]['data_semana_iso']
130
 
131
  for i in range(weeks_to_predict):
132
+ dynamic_input = np.array([last_sequence_scaled], dtype=np.float32)
133
  pred_scaled = self.model.predict([dynamic_input, static_input], verbose=0)[0][0]
134
+
135
+ pred_real = 0
136
+ if pd.notna(pred_scaled) and np.isfinite(pred_scaled):
137
+ pred_real = self.inverse_transform_cases(scaler_dyn, np.array([pred_scaled]))[0]
138
 
139
+ future_date = last_real_date + timedelta(weeks=i + 1)
 
 
140
  predictions.append({
141
  "date": future_date.strftime('%Y-%m-%d'),
142
  "predicted_cases": max(0, round(pred_real))
143
  })
144
+
145
+ # Monta a próxima linha para a sequência rolante
146
  future_year, future_week = future_date.year, future_date.isocalendar()[1]
147
  if (future_year, future_week) in climate_lookup:
148
  future_climate = climate_lookup[(future_year, future_week)]
149
  else:
150
+ # Se não houver clima futuro, usa a média das últimas 4 semanas
151
+ future_climate = df_mun[dynamic_features[1:]].tail(4).mean(axis=0, skipna=True)
152
 
153
  new_row = np.zeros(len(dynamic_features), dtype=np.float32)
154
+ new_row[0] = pred_scaled
155
  for j, feat in enumerate(dynamic_features[1:], start=1):
156
  new_row[j] = future_climate[feat]
157
+
 
158
  new_row_scaled = scaler_dyn.transform(new_row.reshape(1, -1))[0]
159
+ last_sequence_scaled = np.vstack([last_sequence_scaled[1:], new_row_scaled])
160
 
161
+ # ... O resto da sua função (geração de gráficos, etc.) continua igual ...
162
  # Histórico das últimas 52 semanas
163
+ historic_data = [ {"date": row['data_semana_iso'].strftime('%Y-%m-%d'), "cases": int(row["numero_casos"]) if not pd.isna(row["numero_casos"]) else None} for _, row in df_mun.tail(52).iterrows() ]
 
 
 
 
164
 
165
 
166
+ # Lag analysis
167
  df_analysis = df_mun[dynamic_features].rename(columns=self.feature_names_pt)
168
  max_lag = 12
169
  cases_col_name = 'Nº de Casos de Dengue'
 
179
  corrs.append(corr)
180
  lag_correlations[col] = corrs
181
 
 
182
  plt.figure(figsize=(10, 6), facecolor='#18181b')
183
  ax = plt.gca()
184
  ax.set_facecolor('#18181b')