Skip to article frontmatterSkip to article content

ATM 509 Final Project Figures - Evan Belkin

ATM 509 Final Project Figures - Evan Belkin

Imports

import pandas as pd
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
from metpy.plots import USCOUNTIES
import cartopy.crs as ccrs
from scipy.interpolate import griddata
import cartopy.feature as cfeature
from matplotlib.colors import ListedColormap, BoundaryNorm
import pickle 
import cmweather
import matplotlib.dates as mdates
from matplotlib.dates import DateFormatter, AutoDateLocator,HourLocator,DayLocator
from matplotlib.ticker import FuncFormatter
from herbie import Herbie
from matplotlib.colors import Normalize
from datetime import datetime, timedelta

Loading in Data:

monthly_percentiles = pd.read_pickle("../data/509_Cases.pkl")

Figure #1: Extract 90th percentile column and create a 4x3 plot.

def plot_lat_lon_12panel(dataframe):
    # Define month names
    month_names = ["January", "February", "March", "April", "May", "June", 
                   "July", "August", "September", "October", "November", "December"]

    # Ensure month column is integer for sorting
    dataframe['Month'] = dataframe['Month'].astype(int)
    months = sorted(dataframe['Month'].unique())

    # Set up the figure with 12 subplots (4 rows x 3 columns)
    fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(20, 14),
                             subplot_kw={'projection': ccrs.PlateCarree()},
                             dpi=150)
    axes = axes.flatten()

    # Define fixed color bins
    bounds = np.arange(5, 50, 5)
    cmap = plt.get_cmap('NWSRef', len(bounds)-1) 
    norm = BoundaryNorm(bounds, cmap.N)

    for i, month in enumerate(months):
        ax = axes[i]
        month_data = dataframe[dataframe['Month'] == month]

        latitudes = month_data['center_lat'].values
        longitudes = month_data['center_lon'].values
        variable = month_data['90'].values    ## Extract 90th percentile! 

        ax.set_extent([-81, -67, 38.8, 48], crs=ccrs.PlateCarree()) 

        # Map features
        ax.add_feature(cfeature.STATES, linestyle='--', linewidth=3)
        ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.5)
        ax.add_feature(cfeature.LAND, edgecolor='gray')
        ax.add_feature(USCOUNTIES.with_scale('20m'), edgecolor='gray', linewidth=0.4)
        
        # Scatter plot with fixed color bins
        sc = ax.scatter(longitudes, latitudes, c=variable, s=2,
                        cmap=cmap, norm=norm, transform=ccrs.PlateCarree())

        # Month label
        ax.text(-76.5, 47.0, month_names[month-1],
                fontsize=24, fontweight='bold',
                transform=ccrs.PlateCarree(),
                bbox=dict(facecolor='white', edgecolor='none', alpha=0.7))

    # Add a shared colorbar
    cbar_ax = fig.add_axes([0.92, 0.1, 0.015, 0.8])
    cbar = fig.colorbar(sc, cax=cbar_ax, extend='both', ticks=bounds)
    cbar.set_label('90th Percentile (mm)', fontsize=24)
    cbar.ax.tick_params(labelsize=24)

    # Layout
    plt.suptitle("Monthly 90th Percentile of Measurable Precipitation", fontsize=36)
    plt.tight_layout(rect=[0, 0, 0.9, 1], pad=1.5, w_pad=0.3, h_pad=0.3)
    #plt.savefig('evan.png')
    plt.show()

plot_lat_lon_12panel(monthly_percentiles)

Figure #2: JFK Airport Example Event

time_series = pd.read_pickle("../data/509_JFK.pkl")
def custom_hour_formatter(x, pos):
    dt = mdates.num2date(x)
    if dt.hour == 0:
        return dt.strftime('%H %d %b')  
    else:
        return dt.strftime('%H')     

rolling_24h = time_series['24h_rolling_sum']
time = time_series['Datetime']
precipitation = time_series['Precipitation']
event_rolling24h = time_series['event_rolling_24h']

fig, ax = plt.subplots(figsize=(14, 6))

ax.axhline(y=25.2380, c='red', alpha=0.7, linewidth=2, label='June 90th percentile')

ax.plot(time, rolling_24h, marker='o', linestyle='-', label='24 h rolling precip', color='b')
ax.plot(time, precipitation, marker='o', linestyle='-', label='Hourly precip', color='g')

ax.set_xlabel('Time (UTC)', fontsize=15)
ax.set_ylabel('Precip (mm)', fontsize=15)

ax.xaxis.set_major_locator(mdates.HourLocator(byhour=[0, 6, 12, 18]))
ax.xaxis.set_minor_locator(mdates.HourLocator(interval=1))
ax.xaxis.set_major_formatter(FuncFormatter(custom_hour_formatter))


specific_time = pd.Timestamp('2018-04-16 00:00:00')
ax.axvline(x=specific_time, color='purple', linewidth=5.0)

specific_time = pd.Timestamp('2018-04-16 05:00:00')
ax.axvline(x=specific_time, color='orange', linewidth=3.5, zorder=1)

specific_time = pd.Timestamp('2018-04-16 17:00:00')
ax.axvline(x=specific_time, color='purple', linewidth=5.0)

plt.ylim(-1,80)
ax.tick_params(axis='x', pad=8, size=10) 
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.grid()

Figure #3A: Topography. Here is the code! Elev data is data from Herbie! :)

H = Herbie(
    date= datetime(2020, 12, 24, 6, 0, 0), # initialization date and time: year, month, day, hour, miunte, second
    model="hrrr",
    product="sfc",
    fxx=6,
)

elev = H.xarray(':HGT:surface').orog
fig = plt.figure(figsize=(10, 10), layout='constrained')
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
ax.set_title('Topography of the Northeast United States', fontsize=16)

plot_bounds = [-81, -67, 38.8, 48]
ax.set_extent(plot_bounds)

# Define levels using full 0–1200 scale
levels = np.arange(100, 1201, 50)

# Mask values < 60 so they are not plotted
masked_elev = elev.where(elev >= 100)

elev_cb = ax.contourf(
    elev.longitude, elev.latitude, masked_elev,
    levels=levels, cmap='gist_earth', extend='max'
)

cbar = plt.colorbar(
    elev_cb, shrink=0.6,
    ticks=np.arange(100, 1201, 300),
    extend='max'
)
cbar.ax.tick_params(labelsize=12)
cbar.set_label('Elevation (m)', fontsize=12)
ax.add_feature(cfeature.STATES, linestyle='--', linewidth=3)
# Add county boundaries

# Add red stars and labels
ax.plot(-73.7562, 42.6526, marker='*', color='red', markersize=15,
        transform=ccrs.PlateCarree(), label='Albany, NY')
ax.plot(-75.85, 43.65, marker='*', color='red', markersize=15,
        transform=ccrs.PlateCarree(), label='Tug Hill Plateau')

ax.text(-73.5562, 42.4526, 'Albany, NY', transform=ccrs.PlateCarree(),
        fontsize=14, color='red', weight='bold')
ax.text(-75.65, 43.45, 'Tug Hill Plateau', transform=ccrs.PlateCarree(),
        fontsize=14, color='red', weight='bold')


plt.show()

Figure #3B: Total # of Cases.

latitudes = monthly_percentiles['center_lat'].values
longitudes = monthly_percentiles['center_lon'].values
cases = monthly_percentiles['Cases'].values

# Define bounds for discrete colorbar
bounds = np.arange(250, 525, 25)  # 250–500 in steps of 25
cmap = plt.get_cmap('NWSRef', len(bounds) - 1)
norm = plt.matplotlib.colors.BoundaryNorm(bounds, cmap.N)

# Plot
fig, ax = plt.subplots(figsize=(10, 8), subplot_kw={'projection': ccrs.PlateCarree()})
ax.set_extent([-81, -67, 38.8, 48], crs=ccrs.PlateCarree()) 

ax.add_feature(cfeature.STATES, linestyle='--')
ax.add_feature(cfeature.BORDERS, linestyle=':')
ax.add_feature(cfeature.LAND, edgecolor='gray')

# Counties (optional, if USCOUNTIES is available)
ax.add_feature(USCOUNTIES.with_scale('20m'), edgecolor='gray')

# Scatter plot
sc = ax.scatter(longitudes, latitudes, c=cases, s=1,
                cmap=cmap, norm=norm, transform=ccrs.PlateCarree())

# Colorbar with fixed bounds
cbar = plt.colorbar(sc, ax=ax, boundaries=bounds, ticks=bounds,
                    shrink=0.65, extend='max')
cbar.set_label("Cases")

plt.title("Total Number of Cases", fontsize=14)
plt.show()

Figure #4: Event Duration by Month

duration = pd.read_pickle("../data/509_duration.pkl")
duration
def plot_lat_lon_12panel(dataframe):
    # Define month names
    month_names = ["January", "February", "March", "April", "May", "June", 
                   "July", "August", "September", "October", "November", "December"]

    # Ensure month column is integer for sorting
    dataframe['month'] = dataframe['month'].astype(int)
    months = sorted(dataframe['month'].unique())

    # Set up the figure with 12 subplots (4 rows x 3 columns)
    fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(20, 14),
                             subplot_kw={'projection': ccrs.PlateCarree()},
                             dpi=150)
    axes = axes.flatten()

    # Define fixed color bins
    bounds = np.arange(8, 40, 4)
    cmap = plt.get_cmap('NWSRef', len(bounds)-1) 
    norm = BoundaryNorm(bounds, cmap.N)

    for i, month in enumerate(months):
        ax = axes[i]
        month_data = dataframe[dataframe['month'] == month]

        latitudes = month_data['center_lat'].values
        longitudes = month_data['center_lon'].values
        variable = month_data['duration'].values    ## Extract 90th percentile! 

        ax.set_extent([-81, -67, 38.8, 48], crs=ccrs.PlateCarree()) 

        # Map features
        ax.add_feature(cfeature.STATES, linestyle='--', linewidth=3)
        ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.5)
        ax.add_feature(cfeature.LAND, edgecolor='gray')
        ax.add_feature(USCOUNTIES.with_scale('20m'), edgecolor='gray', linewidth=0.4)
        
        # Scatter plot with fixed color bins
        sc = ax.scatter(longitudes, latitudes, c=variable, s=2,
                        cmap=cmap, norm=norm, transform=ccrs.PlateCarree())

        # Month label
        ax.text(-76.5, 47.0, month_names[month-1],
                fontsize=24, fontweight='bold',
                transform=ccrs.PlateCarree(),
                bbox=dict(facecolor='white', edgecolor='none', alpha=0.7))

    # Add a shared colorbar
    cbar_ax = fig.add_axes([0.92, 0.1, 0.015, 0.8])
    cbar = fig.colorbar(sc, cax=cbar_ax, extend='max', ticks=bounds)
    cbar.set_label('Monthly Mean Event Duration (h)', fontsize=24)
    cbar.ax.tick_params(labelsize=24)

    # Layout
    plt.suptitle("Monthly Mean Event Duration", fontsize=36)
    plt.tight_layout(rect=[0, 0, 0.9, 1], pad=1.5, w_pad=0.3, h_pad=0.3)
    #plt.savefig('evan.png')
    plt.show()
plot_lat_lon_12panel(duration)

Figure 5: Event Duration Plots

fall = pd.read_pickle("../data/509_fall.pkl")
winter = pd.read_pickle("../data/509_winter.pkl")
spring = pd.read_pickle("../data/509_spring.pkl")
summer = pd.read_pickle("../data/509_summer.pkl")

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Font sizes
title_fontsize = 18
label_fontsize = 16
tick_label_size = 14
supertitle_fontsize = 20

# Define a helper function to plot each season
def plot_season(ax, data, title):
    # Plot non-AR events in default color
    non_ar = data[data['AR_association'] == False]
    ax.scatter(non_ar['duration'], non_ar['avgrate_1h'], label='Non-AR', alpha=0.7)
    
    # Plot AR-associated events in red
    ar = data[data['AR_association'] == True]
    ax.scatter(ar['duration'], ar['avgrate_1h'], color='red', label='AR-associated', alpha=0.7)
    
    ax.set_xlabel('Event Duration', fontsize=label_fontsize)
    ax.set_ylabel('Event Average 1 h Precip Rate', fontsize=label_fontsize)
    ax.set_title(title, fontsize=title_fontsize)
    ax.set_ylim(0, 20)
    ax.set_xlim(0, 70)
    ax.grid(True)
    ax.legend(fontsize=12)
    ax.tick_params(axis='both', labelsize=tick_label_size)

# Plot each season
plot_season(axes[0, 0], fall, 'FALL')
plot_season(axes[0, 1], winter, 'WINTER')
plot_season(axes[1, 0], spring, 'SPRING')
plot_season(axes[1, 1], summer, 'SUMMER')

# Set the main title

fig.suptitle('Event Duration vs. Average 1 h Precipitation Rate by Season – Albany, NY',
             fontsize=supertitle_fontsize)


# Adjust layout
plt.tight_layout(rect=[0, 0, 1, 0.95])

# Show plot
plt.show()

Figure 6: Seasonal Breakdown of Events:

seasonal_stats = pd.read_pickle("../data/509_seasonal_stats.pkl")
# Calculate non-AR event counts
non_ar_count = seasonal_stats['Total_Count'] - seasonal_stats['True_Count']

# Plot
plt.figure(figsize=(10, 6))
plt.bar(seasonal_stats.index, seasonal_stats['True_Count'], label='AR-Associated Events', color='orange')
plt.bar(seasonal_stats.index, non_ar_count, bottom=seasonal_stats['True_Count'], label='Non-AR Events', color='steelblue')

# Labels and formatting
plt.xlabel('Season', fontsize=14)
plt.ylabel('Number of Events', fontsize=14)
plt.title(f'Seasonal Breakdown of Events – Albany, NY', fontsize=16)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.legend(fontsize=12)
plt.grid(axis='y')
plt.ylim(0,150)
plt.tight_layout()
plt.show()

Figure 7: AR Event Footprint

percent_AR_seasonal_df = pd.read_pickle("../data/509_percents.pkl")
point_lon, point_lat = -73.819, 42.735   ###ALBANY


seasons = ['DJF', 'MAM', 'JJA', 'SON']
norm = Normalize(vmin=10, vmax=80)

fig, axs = plt.subplots(2, 2, figsize=(16, 12), subplot_kw={'projection': ccrs.PlateCarree()})
axs = axs.flatten()

# Loop over each season and plot
for i, season in enumerate(seasons):
    ax = axs[i]
    season_df = percent_AR_seasonal_df[percent_AR_seasonal_df['season'] == season]
    
    scatter = ax.scatter(
        season_df['lon'],
        season_df['lat'],
        c=season_df['percent_AR'],
        cmap='viridis',
        norm=norm,
        s=35,
        transform=ccrs.PlateCarree()
    )

    ax.plot(
        point_lon, point_lat,
        marker='*',
        color='red',
        alpha=0.5,
        markersize=12,
        markeredgecolor='black',
        transform=ccrs.PlateCarree(),
        zorder=5
    )

    #ax.set_title(season, fontsize=18)
    ax.text(-75.5, 46.2, season,
        fontsize=24, fontweight='bold',
        transform=ccrs.PlateCarree(),
        bbox=dict(facecolor='white', edgecolor='none', alpha=0.7))
    
    ax.add_feature(cfeature.STATES, linestyle='--', edgecolor='black', linewidth=0.8)
    ax.add_feature(cfeature.BORDERS, linestyle=':', edgecolor='black', linewidth=0.8)
    ax.add_feature(cfeature.LAND, edgecolor='gray')  
    ax.add_feature(USCOUNTIES.with_scale('20m'), edgecolor='gray', linewidth=0.5)

cbar_ax = fig.add_axes([0.92, 0.15, 0.03, 0.8])  # [left, bottom, width, height]
cbar = fig.colorbar(scatter, cax=cbar_ax, extend='both', label='% AR Events')
cbar.set_label('% AR Events', fontsize=18)
cbar.ax.tick_params(labelsize=18)  # Set tick labels' font size


#fig.suptitle('Seasonal Percent of Events Associated with Atmospheric Rivers', fontsize=18)
#fig.suptitle('Seasonal AR Frequency During Albany Extreme Precip Events', fontsize=18)
fig.subplots_adjust(wspace=0.01, hspace=0.00001, right=0.9, top=0.92)  

plt.show()