euler314 commited on
Commit
1ca7717
·
verified ·
1 Parent(s): 57cb1ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -21
app.py CHANGED
@@ -23,7 +23,7 @@ from sklearn.manifold import TSNE
23
  from sklearn.cluster import DBSCAN
24
  from scipy.interpolate import interp1d
25
 
26
- # Import tropycal for IBTrACS processing (for typhoon option updates)
27
  import tropycal.tracks as tracks
28
 
29
  # ------------------ Argument Parsing ------------------
@@ -189,6 +189,46 @@ def classify_enso_phases(oni_value):
189
  else:
190
  return 'Neutral'
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  # ------------------ IBTrACS Data Loading (for typhoon options) ------------------
193
  def load_ibtracs_data():
194
  ibtracs_data = {}
@@ -403,7 +443,6 @@ def categorize_typhoon_by_standard(wind_speed, standard):
403
 
404
  # ------------------ Animation Functions Using Processed CSV ------------------
405
  def generate_track_video_from_csv(year, storm_id, standard):
406
- # Filter processed CSV data for the storm ID
407
  storm_df = typhoon_data[typhoon_data['SID'] == storm_id].copy()
408
  if storm_df.empty:
409
  print("No data found for storm:", storm_id)
@@ -419,20 +458,17 @@ def generate_track_video_from_csv(year, storm_id, standard):
419
  storm_name = storm_df['NAME'].iloc[0]
420
  season = storm_df['SEASON'].iloc[0]
421
 
422
- # Set up map boundaries
423
  min_lat, max_lat = np.min(lats), np.max(lats)
424
  min_lon, max_lon = np.min(lons), np.max(lons)
425
  lat_padding = max((max_lat - min_lat) * 0.3, 5)
426
  lon_padding = max((max_lon - min_lon) * 0.3, 5)
427
 
428
- # Create a larger figure with custom central longitude for better regional focus
429
  fig = plt.figure(figsize=(12, 9), dpi=100)
430
  ax = plt.axes([0.05, 0.05, 0.60, 0.90],
431
  projection=ccrs.PlateCarree(central_longitude=-25))
432
  ax.set_extent([min_lon - lon_padding, max_lon + lon_padding, min_lat - lat_padding, max_lat + lat_padding],
433
  crs=ccrs.PlateCarree())
434
 
435
- # Add map features
436
  ax.add_feature(cfeature.LAND, facecolor='lightgray')
437
  ax.add_feature(cfeature.OCEAN, facecolor='lightblue')
438
  ax.add_feature(cfeature.COASTLINE, edgecolor='black')
@@ -440,18 +476,17 @@ def generate_track_video_from_csv(year, storm_id, standard):
440
  ax.gridlines(draw_labels=True, linestyle='--', color='gray', alpha=0.5)
441
  ax.set_title(f"{year} {storm_name} - {season}", fontsize=16)
442
 
443
- # Plot track and state marker
444
  line, = ax.plot([], [], 'b-', linewidth=2, transform=ccrs.PlateCarree())
445
  point, = ax.plot([], [], 'o', markersize=10, transform=ccrs.PlateCarree())
446
-
447
- # Dynamic text elements
448
- date_text = ax.text(0.02, 0.02, '', transform=ax.transAxes, fontsize=12, bbox=dict(facecolor='white', alpha=0.8))
449
  state_text = fig.text(0.70, 0.60, '', fontsize=14, verticalalignment='top',
450
- bbox=dict(facecolor='white', alpha=0.8, boxstyle='round,pad=0.5'))
451
 
452
- # Persistent legend for color mapping (placed on right)
453
  legend_elements = [plt.Line2D([0], [0], marker='o', color='w', label=f"{cat}",
454
- markerfacecolor=details['hex'], markersize=10)
455
  for cat, details in (atlantic_standard if standard=='atlantic' else taiwan_standard).items()]
456
  ax.legend(handles=legend_elements, title="Storm Categories", loc='upper right', fontsize=10)
457
 
@@ -463,16 +498,13 @@ def generate_track_video_from_csv(year, storm_id, standard):
463
  return line, point, date_text, state_text
464
 
465
  def update(frame):
466
- # Update the track line
467
  line.set_data(lons[:frame+1], lats[:frame+1])
468
- # Update the marker position using lists to avoid deprecation warning
469
  point.set_data([lons[frame]], [lats[frame]])
470
  wind_speed = winds[frame] if frame < len(winds) else np.nan
471
  category, color = categorize_typhoon_by_standard(wind_speed, standard)
472
  point.set_color(color)
473
  dt_str = pd.to_datetime(times[frame]).strftime('%Y-%m-%d %H:%M')
474
  date_text.set_text(dt_str)
475
- # Update state information dynamically in the sidebar
476
  state_info = (f"Name: {storm_name}\n"
477
  f"Date: {dt_str}\n"
478
  f"Wind: {wind_speed:.1f} kt\n"
@@ -542,7 +574,7 @@ def update_typhoon_options(year, basin):
542
 
543
  def update_typhoon_options_anim(year, basin):
544
  try:
545
- # For animation, use the processed CSV data for all storms in the given year
546
  data = typhoon_data.copy()
547
  data['Year'] = pd.to_datetime(data['ISO_TIME']).dt.year
548
  season_data = data[data['Year'] == int(year)]
@@ -625,8 +657,8 @@ def update_route_clusters(start_year, start_month, end_year, end_month, enso_val
625
  name=f"Cluster {label}"
626
  ))
627
  fig_tsne.update_layout(title="t-SNE of WP Storm Routes")
628
- fig_routes = go.Figure()
629
- fig_stats = make_subplots(rows=2, cols=1)
630
  info = "TSNE clustering complete."
631
  return fig_tsne, fig_routes, fig_stats, info
632
 
@@ -645,7 +677,7 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
645
  - **Wind Analysis**: Examine wind speed vs ONI relationships
646
  - **Pressure Analysis**: Analyze pressure vs ONI relationships
647
  - **Longitude Analysis**: Study typhoon generation longitude vs ONI
648
- - **Path Animation**: Watch animated tropical cyclone paths with dynamic state display and a legend (using processed CSV data)
649
  - **TSNE Cluster**: Perform t-SNE clustering on WP storm routes with mean routes and region analysis
650
 
651
  Select a tab above to begin your analysis.
@@ -715,11 +747,11 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
715
  path_video = gr.Video(label="Tropical Cyclone Path Animation", format="mp4", interactive=False, elem_id="path_video")
716
  animation_info = gr.Markdown("""
717
  ### Animation Instructions
718
- 1. Select a year and basin from the dropdowns. (This animation uses the processed CSV data.)
719
  2. Choose a tropical cyclone from the populated list.
720
  3. Select a classification standard (Atlantic or Taiwan).
721
  4. Click "Generate Animation".
722
- 5. The animation displays the storm track along with a dynamic sidebar that updates the state (name, date, wind, category) and includes a persistent legend.
723
  """)
724
  year_dropdown.change(fn=update_typhoon_options_anim, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
725
  basin_dropdown.change(fn=update_typhoon_options_anim, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
 
23
  from sklearn.cluster import DBSCAN
24
  from scipy.interpolate import interp1d
25
 
26
+ # Import tropycal for IBTrACS processing (for typhoon options)
27
  import tropycal.tracks as tracks
28
 
29
  # ------------------ Argument Parsing ------------------
 
189
  else:
190
  return 'Neutral'
191
 
192
+ # ------------------ Regression Functions ------------------
193
+ def perform_wind_regression(start_year, start_month, end_year, end_month):
194
+ start_date = datetime(start_year, start_month, 1)
195
+ end_date = datetime(end_year, end_month, 28)
196
+ data = merged_data[(merged_data['ISO_TIME'] >= start_date) & (merged_data['ISO_TIME'] <= end_date)].dropna(subset=['USA_WIND', 'ONI'])
197
+ data['severe_typhoon'] = (data['USA_WIND'] >= 64).astype(int)
198
+ X = sm.add_constant(data['ONI'])
199
+ y = data['severe_typhoon']
200
+ model = sm.Logit(y, X).fit(disp=0)
201
+ beta_1 = model.params['ONI']
202
+ exp_beta_1 = np.exp(beta_1)
203
+ p_value = model.pvalues['ONI']
204
+ return f"Wind Regression: β1={beta_1:.4f}, Odds Ratio={exp_beta_1:.4f}, P-value={p_value:.4f}"
205
+
206
+ def perform_pressure_regression(start_year, start_month, end_year, end_month):
207
+ start_date = datetime(start_year, start_month, 1)
208
+ end_date = datetime(end_year, end_month, 28)
209
+ data = merged_data[(merged_data['ISO_TIME'] >= start_date) & (merged_data['ISO_TIME'] <= end_date)].dropna(subset=['USA_PRES', 'ONI'])
210
+ data['intense_typhoon'] = (data['USA_PRES'] <= 950).astype(int)
211
+ X = sm.add_constant(data['ONI'])
212
+ y = data['intense_typhoon']
213
+ model = sm.Logit(y, X).fit(disp=0)
214
+ beta_1 = model.params['ONI']
215
+ exp_beta_1 = np.exp(beta_1)
216
+ p_value = model.pvalues['ONI']
217
+ return f"Pressure Regression: β1={beta_1:.4f}, Odds Ratio={exp_beta_1:.4f}, P-value={p_value:.4f}"
218
+
219
+ def perform_longitude_regression(start_year, start_month, end_year, end_month):
220
+ start_date = datetime(start_year, start_month, 1)
221
+ end_date = datetime(end_year, end_month, 28)
222
+ data = merged_data[(merged_data['ISO_TIME'] >= start_date) & (merged_data['ISO_TIME'] <= end_date)].dropna(subset=['LON', 'ONI'])
223
+ data['western_typhoon'] = (data['LON'] <= 140).astype(int)
224
+ X = sm.add_constant(data['ONI'])
225
+ y = data['western_typhoon']
226
+ model = sm.Logit(y, X).fit(disp=0)
227
+ beta_1 = model.params['ONI']
228
+ exp_beta_1 = np.exp(beta_1)
229
+ p_value = model.pvalues['ONI']
230
+ return f"Longitude Regression: β1={beta_1:.4f}, Odds Ratio={exp_beta_1:.4f}, P-value={p_value:.4f}"
231
+
232
  # ------------------ IBTrACS Data Loading (for typhoon options) ------------------
233
  def load_ibtracs_data():
234
  ibtracs_data = {}
 
443
 
444
  # ------------------ Animation Functions Using Processed CSV ------------------
445
  def generate_track_video_from_csv(year, storm_id, standard):
 
446
  storm_df = typhoon_data[typhoon_data['SID'] == storm_id].copy()
447
  if storm_df.empty:
448
  print("No data found for storm:", storm_id)
 
458
  storm_name = storm_df['NAME'].iloc[0]
459
  season = storm_df['SEASON'].iloc[0]
460
 
 
461
  min_lat, max_lat = np.min(lats), np.max(lats)
462
  min_lon, max_lon = np.min(lons), np.max(lons)
463
  lat_padding = max((max_lat - min_lat) * 0.3, 5)
464
  lon_padding = max((max_lon - min_lon) * 0.3, 5)
465
 
 
466
  fig = plt.figure(figsize=(12, 9), dpi=100)
467
  ax = plt.axes([0.05, 0.05, 0.60, 0.90],
468
  projection=ccrs.PlateCarree(central_longitude=-25))
469
  ax.set_extent([min_lon - lon_padding, max_lon + lon_padding, min_lat - lat_padding, max_lat + lat_padding],
470
  crs=ccrs.PlateCarree())
471
 
 
472
  ax.add_feature(cfeature.LAND, facecolor='lightgray')
473
  ax.add_feature(cfeature.OCEAN, facecolor='lightblue')
474
  ax.add_feature(cfeature.COASTLINE, edgecolor='black')
 
476
  ax.gridlines(draw_labels=True, linestyle='--', color='gray', alpha=0.5)
477
  ax.set_title(f"{year} {storm_name} - {season}", fontsize=16)
478
 
 
479
  line, = ax.plot([], [], 'b-', linewidth=2, transform=ccrs.PlateCarree())
480
  point, = ax.plot([], [], 'o', markersize=10, transform=ccrs.PlateCarree())
481
+ date_text = ax.text(0.02, 0.02, '', transform=ax.transAxes, fontsize=12,
482
+ bbox=dict(facecolor='white', alpha=0.8))
483
+ # Dynamic state display at right side
484
  state_text = fig.text(0.70, 0.60, '', fontsize=14, verticalalignment='top',
485
+ bbox=dict(facecolor='white', alpha=0.8, boxstyle='round,pad=0.5'))
486
 
487
+ # Persistent legend for category colors
488
  legend_elements = [plt.Line2D([0], [0], marker='o', color='w', label=f"{cat}",
489
+ markerfacecolor=details['hex'], markersize=10)
490
  for cat, details in (atlantic_standard if standard=='atlantic' else taiwan_standard).items()]
491
  ax.legend(handles=legend_elements, title="Storm Categories", loc='upper right', fontsize=10)
492
 
 
498
  return line, point, date_text, state_text
499
 
500
  def update(frame):
 
501
  line.set_data(lons[:frame+1], lats[:frame+1])
 
502
  point.set_data([lons[frame]], [lats[frame]])
503
  wind_speed = winds[frame] if frame < len(winds) else np.nan
504
  category, color = categorize_typhoon_by_standard(wind_speed, standard)
505
  point.set_color(color)
506
  dt_str = pd.to_datetime(times[frame]).strftime('%Y-%m-%d %H:%M')
507
  date_text.set_text(dt_str)
 
508
  state_info = (f"Name: {storm_name}\n"
509
  f"Date: {dt_str}\n"
510
  f"Wind: {wind_speed:.1f} kt\n"
 
574
 
575
  def update_typhoon_options_anim(year, basin):
576
  try:
577
+ # For animation, use the processed CSV data (without filtering by a specific prefix)
578
  data = typhoon_data.copy()
579
  data['Year'] = pd.to_datetime(data['ISO_TIME']).dt.year
580
  season_data = data[data['Year'] == int(year)]
 
657
  name=f"Cluster {label}"
658
  ))
659
  fig_tsne.update_layout(title="t-SNE of WP Storm Routes")
660
+ fig_routes = go.Figure() # Placeholder
661
+ fig_stats = make_subplots(rows=2, cols=1) # Placeholder
662
  info = "TSNE clustering complete."
663
  return fig_tsne, fig_routes, fig_stats, info
664
 
 
677
  - **Wind Analysis**: Examine wind speed vs ONI relationships
678
  - **Pressure Analysis**: Analyze pressure vs ONI relationships
679
  - **Longitude Analysis**: Study typhoon generation longitude vs ONI
680
+ - **Path Animation**: Watch animated tropical cyclone paths with a dynamic state display and persistent legend (using processed CSV data)
681
  - **TSNE Cluster**: Perform t-SNE clustering on WP storm routes with mean routes and region analysis
682
 
683
  Select a tab above to begin your analysis.
 
747
  path_video = gr.Video(label="Tropical Cyclone Path Animation", format="mp4", interactive=False, elem_id="path_video")
748
  animation_info = gr.Markdown("""
749
  ### Animation Instructions
750
+ 1. Select a year and basin from the dropdowns. (This animation uses processed CSV data.)
751
  2. Choose a tropical cyclone from the populated list.
752
  3. Select a classification standard (Atlantic or Taiwan).
753
  4. Click "Generate Animation".
754
+ 5. The animation displays the storm track along with a dynamic sidebar that shows the current state (name, date, wind, category) and a persistent legend for colors.
755
  """)
756
  year_dropdown.change(fn=update_typhoon_options_anim, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)
757
  basin_dropdown.change(fn=update_typhoon_options_anim, inputs=[year_dropdown, basin_dropdown], outputs=typhoon_dropdown)