alidenewade commited on
Commit
5ac174d
·
verified ·
1 Parent(s): 622eec2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -32
app.py CHANGED
@@ -236,46 +236,101 @@ def predict_crime_level(crime_felony, crime_misd, crime_viol, sr311_total, dob_p
236
  else:
237
  return "Predicted Crime Level: High (basic fallback)", {"Low": 0.1, "Medium": 0.3, "High": 0.6}
238
 
239
- def forecast_time_series(geoid):
240
  """Forecasts crime for a specific GEOID."""
 
 
241
  if panel_df is None or 'DUMMY' in panel_df['GEOID'].tolist():
242
  fig, ax = plt.subplots()
243
  ax.text(0.5, 0.5, "Data not loaded", ha='center', va='center')
244
  return fig, "Data not loaded."
245
 
246
  if geoid not in panel_df['GEOID'].unique():
247
- return None, f"GEOID {geoid} not found in the dataset."
 
 
 
248
 
249
  tract_data = panel_df[panel_df['GEOID'] == geoid].set_index('month')['crime_total'].asfreq('MS')
250
 
251
  if len(tract_data) < 24: # Need enough data to forecast
252
- return None, f"Not enough historical data for GEOID {geoid} to create a forecast."
 
 
 
 
253
 
254
- # Simple SARIMAX model for demonstration
255
- model_ts = SARIMAX(tract_data, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
256
- results = model_ts.fit(disp=False)
257
-
258
- forecast = results.get_forecast(steps=12)
259
- forecast_mean = forecast.predicted_mean
260
- forecast_ci = forecast.conf_int()
 
261
 
262
- fig, ax = plt.subplots(figsize=(12, 6))
263
- tract_data.plot(ax=ax, label='Historical')
264
- forecast_mean.plot(ax=ax, label='Forecast')
265
- ax.fill_between(forecast_ci.index,
266
- forecast_ci.iloc[:, 0],
267
- forecast_ci.iloc[:, 1], color='k', alpha=.25)
268
- ax.set_title(f'Crime Forecast for Census Tract {geoid}')
269
- ax.set_xlabel('Date')
270
- ax.set_ylabel('Crime Total')
271
- ax.legend()
272
- ax.grid(True)
273
- plt.tight_layout()
274
-
275
- metrics_text = f"Forecast for GEOID: {geoid}\n"
276
- metrics_text += "Mean Absolute Error (on test set) would be calculated here in a full implementation."
277
-
278
- return fig, metrics_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
  # --- Gradio App Layout ---
281
  with gr.Blocks() as demo:
@@ -372,20 +427,42 @@ with gr.Blocks() as demo:
372
 
373
  with gr.TabItem("Time Series Forecasting"):
374
  gr.Markdown("## Forecast Future Crime Counts")
375
- gr.Markdown("Enter a Census Tract GEOID to forecast the total crime count for the next 12 months.")
376
  with gr.Row():
377
  with gr.Column():
378
- geoid_input = gr.Textbox(label="Enter GEOID", placeholder="e.g., 36005000100")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  forecast_button = gr.Button("Generate Forecast")
380
  with gr.Column():
381
- forecast_metrics = gr.Textbox(label="Forecast Metrics", interactive=False)
382
 
383
  forecast_plot = gr.Plot()
384
 
385
  forecast_button.click(
386
  fn=forecast_time_series,
387
- inputs=[geoid_input],
388
- outputs=[forecast_plot, forecast_metrics]
389
  )
390
 
391
  if __name__ == "__main__":
 
236
  else:
237
  return "Predicted Crime Level: High (basic fallback)", {"Low": 0.1, "Medium": 0.3, "High": 0.6}
238
 
239
+ def forecast_time_series(geoid, selected_metric):
240
  """Forecasts crime for a specific GEOID."""
241
+ print(f"DEBUG: forecast_time_series called with GEOID={geoid}, metric={selected_metric}")
242
+
243
  if panel_df is None or 'DUMMY' in panel_df['GEOID'].tolist():
244
  fig, ax = plt.subplots()
245
  ax.text(0.5, 0.5, "Data not loaded", ha='center', va='center')
246
  return fig, "Data not loaded."
247
 
248
  if geoid not in panel_df['GEOID'].unique():
249
+ empty_fig, ax = plt.subplots(figsize=(12, 6))
250
+ ax.text(0.5, 0.5, f"GEOID {geoid} not found in the dataset.", ha='center', va='center')
251
+ ax.set_title("GEOID Not Found")
252
+ return empty_fig, f"GEOID {geoid} not found in the dataset."
253
 
254
  tract_data = panel_df[panel_df['GEOID'] == geoid].set_index('month')['crime_total'].asfreq('MS')
255
 
256
  if len(tract_data) < 24: # Need enough data to forecast
257
+ empty_fig, ax = plt.subplots(figsize=(12, 6))
258
+ ax.text(0.5, 0.5, f"Not enough historical data for GEOID {geoid}\n(need at least 24 months)",
259
+ ha='center', va='center')
260
+ ax.set_title("Insufficient Data")
261
+ return empty_fig, f"Not enough historical data for GEOID {geoid} to create a forecast."
262
 
263
+ try:
264
+ # Simple SARIMAX model for demonstration
265
+ model_ts = SARIMAX(tract_data, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
266
+ results = model_ts.fit(disp=False)
267
+
268
+ forecast = results.get_forecast(steps=12)
269
+ forecast_mean = forecast.predicted_mean
270
+ forecast_ci = forecast.conf_int()
271
 
272
+ fig, ax = plt.subplots(figsize=(12, 6))
273
+ tract_data.plot(ax=ax, label='Historical', color='blue')
274
+ forecast_mean.plot(ax=ax, label='Forecast', color='red')
275
+ ax.fill_between(forecast_ci.index,
276
+ forecast_ci.iloc[:, 0],
277
+ forecast_ci.iloc[:, 1], color='red', alpha=.25, label='Confidence Interval')
278
+ ax.set_title(f'Crime Forecast for Census Tract {geoid}')
279
+ ax.set_xlabel('Date')
280
+ ax.set_ylabel('Crime Total')
281
+ ax.legend()
282
+ ax.grid(True)
283
+ plt.tight_layout()
284
+
285
+ # Calculate different metrics based on selection
286
+ # For demonstration, we'll use in-sample fit statistics
287
+ metrics_text = f"Forecast Results for GEOID: {geoid}\n"
288
+ metrics_text += f"Selected Metric: {selected_metric}\n"
289
+ metrics_text += "="*50 + "\n\n"
290
+
291
+ if selected_metric == "Mean Absolute Error (MAE)":
292
+ # Calculate MAE on fitted values vs actual
293
+ fitted_values = results.fittedvalues
294
+ mae = np.mean(np.abs(tract_data - fitted_values))
295
+ metrics_text += f"In-Sample MAE: {mae:.2f}\n"
296
+ metrics_text += "Lower MAE indicates better model fit.\n"
297
+
298
+ elif selected_metric == "Root Mean Square Error (RMSE)":
299
+ fitted_values = results.fittedvalues
300
+ rmse = np.sqrt(np.mean((tract_data - fitted_values)**2))
301
+ metrics_text += f"In-Sample RMSE: {rmse:.2f}\n"
302
+ metrics_text += "Lower RMSE indicates better model fit.\n"
303
+
304
+ elif selected_metric == "Mean Absolute Percentage Error (MAPE)":
305
+ fitted_values = results.fittedvalues
306
+ mape = np.mean(np.abs((tract_data - fitted_values) / tract_data)) * 100
307
+ metrics_text += f"In-Sample MAPE: {mape:.2f}%\n"
308
+ metrics_text += "Lower MAPE indicates better model fit.\n"
309
+
310
+ elif selected_metric == "Akaike Information Criterion (AIC)":
311
+ aic = results.aic
312
+ metrics_text += f"AIC: {aic:.2f}\n"
313
+ metrics_text += "Lower AIC indicates better model quality.\n"
314
+
315
+ elif selected_metric == "Bayesian Information Criterion (BIC)":
316
+ bic = results.bic
317
+ metrics_text += f"BIC: {bic:.2f}\n"
318
+ metrics_text += "Lower BIC indicates better model quality.\n"
319
+
320
+ metrics_text += f"\nForecast Summary:\n"
321
+ metrics_text += f"• Historical data points: {len(tract_data)}\n"
322
+ metrics_text += f"• Forecast horizon: 12 months\n"
323
+ metrics_text += f"• Average historical crime: {tract_data.mean():.2f}\n"
324
+ metrics_text += f"• Average forecast: {forecast_mean.mean():.2f}\n"
325
+
326
+ return fig, metrics_text
327
+
328
+ except Exception as e:
329
+ print(f"DEBUG: Error in forecasting: {e}")
330
+ error_fig, ax = plt.subplots(figsize=(12, 6))
331
+ ax.text(0.5, 0.5, f"Error in forecasting:\n{str(e)}", ha='center', va='center')
332
+ ax.set_title("Forecasting Error")
333
+ return error_fig, f"Error in forecasting for GEOID {geoid}: {str(e)}"
334
 
335
  # --- Gradio App Layout ---
336
  with gr.Blocks() as demo:
 
427
 
428
  with gr.TabItem("Time Series Forecasting"):
429
  gr.Markdown("## Forecast Future Crime Counts")
430
+ gr.Markdown("Select a Census Tract GEOID to forecast the total crime count for the next 12 months.")
431
  with gr.Row():
432
  with gr.Column():
433
+ # Create list of available GEOIDs for dropdown
434
+ available_geoids = sorted(panel_df['GEOID'].unique().tolist()) if 'DUMMY' not in panel_df['GEOID'].tolist() else ['36005000100', '36005000200']
435
+
436
+ geoid_dropdown = gr.Dropdown(
437
+ label="Select GEOID",
438
+ choices=available_geoids,
439
+ value=available_geoids[0] if available_geoids else None,
440
+ allow_custom_value=True,
441
+ filterable=True,
442
+ info="Type to search or select from list"
443
+ )
444
+
445
+ forecast_metrics_dropdown = gr.Dropdown(
446
+ label="Forecast Evaluation Metric",
447
+ choices=["Mean Absolute Error (MAE)",
448
+ "Root Mean Square Error (RMSE)",
449
+ "Mean Absolute Percentage Error (MAPE)",
450
+ "Akaike Information Criterion (AIC)",
451
+ "Bayesian Information Criterion (BIC)"],
452
+ value="Mean Absolute Error (MAE)",
453
+ info="Select metric to display in forecast evaluation"
454
+ )
455
+
456
  forecast_button = gr.Button("Generate Forecast")
457
  with gr.Column():
458
+ forecast_metrics_output = gr.Textbox(label="Forecast Metrics", interactive=False, lines=5)
459
 
460
  forecast_plot = gr.Plot()
461
 
462
  forecast_button.click(
463
  fn=forecast_time_series,
464
+ inputs=[geoid_dropdown, forecast_metrics_dropdown],
465
+ outputs=[forecast_plot, forecast_metrics_output]
466
  )
467
 
468
  if __name__ == "__main__":