All Tropical Cyclones#

../../../_images/e227d8d5236b1c589931c14eab5d7f18d293b1c096c2e9d53095fe598a5ef34c.png
../../../_images/50a405861a9068a0bbc4befc8a6c8a7860bec55b46d37b73010be7c2b6837983.png

Figure. Tropical cyclones (TCs) in the vicinity of Palau since 1950. Map showing all TC tracks in the vicinity of Palau (top) and annual storm count (bottom). Categorizes are colored following the Saffir Simpson scale for wind intensity (see text for details). Grey represents TCs where no wind information is available. The dashed black line represents a trend that is not statistically significant.

In this notebook we will analyze the historical tropical cyclones in the Vicinity of Palau, as well as:

  • The number of tropical cyclones per year

  • The trend in the number of tropical cyclones over time

  • The distribution of categories over time

  • The influence of El Niño Southern Oscillation

Setup#

First, we need to import all the necessary libraries. Some of them are specifically developed to handle the download and plotting of the data and are hosted at the indicators set-up repository in GitHub

Hide code cell source
import warnings
warnings.filterwarnings("ignore")
import sys
import os.path as op
import xarray as xr
import pandas as pd
from myst_nb import glue 
import numpy as np
import matplotlib.pyplot as plt

sys.path.append("../../../functions")
from tcs import Extract_Circle, get_ibtracs_category
from data_downloaders import download_ibtracs

sys.path.append("../../../../indicators_setup")
from ind_setup.plotting_tcs import Plot_TCs_HistoricalTracks_Category
from ind_setup.plotting import plot_bar_probs, plot_tc_categories_trend

from ind_setup.plotting import plot_bar_probs_ONI, add_oni_cat
from data_downloaders import  download_oni_index
lon_lat = [134.5, 5.5] #Palau location lon, lat
basin = 'WP'
r1 = 5 # Radius of the circular area in degrees

IBTrACS#

IBTrACS (International Best Track Archive for Climate Stewardship) is the most comprehensive global database of tropical cyclones. It compiles data from all major meteorological agencies worldwide, providing track, intensity, and metadata for storms from 1842 to present. It supports research on cyclone trends, risks, and climate variability.

update_data = False
path_data = "../../../data"
path_figs = "../../../matrix_cc/figures"
Hide code cell source
if update_data:
    url = 'https://www.ncei.noaa.gov/data/international-best-track-archive-for-climate-stewardship-ibtracs/v04r01/access/netcdf/IBTrACS.ALL.v04r01.nc'
    tcs = download_ibtracs(url, basin = basin)
    tcs.to_netcdf(f"{path_data}/tcs_{basin}.nc")
else:
    tcs = xr.load_dataset(f"{path_data}/tcs_{basin}.nc")

Analysis#

tcs = tcs.isel(storm = np.where(tcs.isel(date_time = 0).time.dt.year >= 1950)[0])    
Hide code cell source
# tcs = xr.open_dataset('/Users/laurac/Downloads/IBTrACS.ALL.v04r01.nc')
Hide code cell source
d_vns = {
    'longitude': 'lon',
    'latitude': 'lat',
    'time': 'time',
    'pressure': 'wmo_pres',
    'wind': 'wmo_wind',
}
tcs_sel, tcs_sel_params = Extract_Circle(tcs, lon_lat[0], lon_lat[1], r1, d_vns, fillwinds=True)
tcs_WP = get_ibtracs_category(tcs, d_vns)
/Users/laurac/Library/Mobile Documents/com~apple~CloudDocs/Projects/CC_indicators/CC_indicators/notebooks_historical/atmosphere/4_tropical_cyclones/../../../functions/tcs.py:31: RankWarning: Polyfit may be poorly conditioned
  linreg = np.polyfit(xfit[~mask], yfit[~mask], 2)

The static plot below shows all the TCs in the vicinity of Palau colored by its category.

tcs_sel_params['category'] = (('storm'), np.where(np.isnan(tcs_sel_params.category), -1, tcs_sel_params.category))
tcs_WP['category'] = (('storm'), np.where(np.isnan(tcs_WP.category), -1, tcs_WP.category))

Plotting#

lon1, lon2 = 90, 200
lat1, lat2 = -10, 70

# r1
fig, ax = Plot_TCs_HistoricalTracks_Category(
    tcs_sel, tcs_sel_params.category,
    lon1, lon2, lat1, lat2,
    lon_lat[0], lon_lat[1], r1,
)

plt.savefig(op.join(path_figs, 'F8_TCs_Historical.png'), dpi=300, bbox_inches='tight')
glue("tracks_fig", fig, display=False)
../../../_images/e227d8d5236b1c589931c14eab5d7f18d293b1c096c2e9d53095fe598a5ef34c.png

Plot of the TC count per month showing the seasonality of TC genesis in the area

import matplotlib.pyplot as plt
tcs_sel_params['month'] =  tcs_sel_params.dmin_date.dt.month
tcs_sel_params.to_dataframe().groupby('month').count().index_in.plot.bar(figsize = (12, 4), color = 'lightblue', alpha = .5)
plt.ylabel('Number of TCs', fontsize = 14)
plt.xlabel('Month', fontsize = 14)
Text(0.5, 0, 'Month')
../../../_images/2f65b6ff904f4b4701412690a245c35cef5cd99f437606ca92e09d3762359fed.png

Count TCs by category#

The bar plot below displays the TC count per category in the whole record and also to the record limited to 1979 and after.

u, c = np.unique(tcs_sel_params.category, return_counts=True)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, figsize=(10, 4))
ax.grid(zorder = -1)
tcs_sel_params.category.plot.hist(bins=range(7), ax=ax, color='plum', alpha=0.5, edgecolor= None, width = .8, linewidth=1, label = 'All TCs')
tcs_sel_params.where(tcs_sel_params.dmin_date.dt.year >=1979, 
                     drop = True).category.plot.hist(bins=range(7), ax=ax, 
                                                     color='darkmagenta', alpha=0.5, edgecolor='crimson', width = .8, linewidth=1, label = 'TCs after 1979')
ax.set_xlabel('Category')
ax.set_ylabel('Counts')

ax.legend()
<matplotlib.legend.Legend at 0x18554d5e0>
../../../_images/463343221b4589d4ef1188921af8a789c9d7bffa8f2a6802541f203e522fd7c6.png

The plot below shows the trend of the number of TCs per year over time.

time = tcs_sel_params.dmin_date.dt.year.values
u, cu = np.unique(time, return_counts=True)
plot_bar_probs(x = u, y = cu, figsize= (15, 4), trendline = True,
               y_label =  'Number TCs')
(<Figure size 1500x400 with 1 Axes>, <Axes: ylabel='Number TCs'>)
../../../_images/1d6ad67785c2fce7960d89f8f70af5f473e09f3be67fca7bf725fb4e147154f2.png

The following bar plot displays the TC count per year and per category, as well as the trend.
The grey color represent those TCs in the record where no pressure or wind information is available to determine the corresponding category.

fig = plot_tc_categories_trend(tcs_sel_params, trendline_plot = False)
plt.savefig(op.join(path_figs, 'F8_TCs_Historical_bars_category.png'), dpi=300, bbox_inches='tight')
glue("time_cat_fig", fig, display=False)
../../../_images/207dcc5d9717ae4c5eaa919005a3dc9ccfcf954fbe8f8492dd81fe71f776905d.png
fig = plot_tc_categories_trend(tcs_sel_params, trendline_plot = True)
plt.savefig(op.join(path_figs, 'F8_TCs_Historical_bars_category.png'), dpi=300, bbox_inches='tight')
glue("time_cat_fig", fig, display=False)
../../../_images/50a405861a9068a0bbc4befc8a6c8a7860bec55b46d37b73010be7c2b6837983.png

To showcase the spatial distribution of the TC tracks for the different categories, a map is shown below representing all the TCs in the record for each category.

for category in np.arange(-1, 6, 1):

    tcs_cat = tcs_sel.where(tcs_sel_params.category == category, drop = True)
    tcs_cat_params = tcs_sel_params.where(tcs_sel_params.category == category, drop = True)

    # r1
    fig, ax = Plot_TCs_HistoricalTracks_Category(
        tcs_cat, tcs_cat_params.category,
        lon1, lon2, lat1, lat2,
        lon_lat[0], lon_lat[1], r1,
    )
    ax.set_title(f'Historical TCs Category {category}', fontsize=15)
../../../_images/de339d25900d44e193ee53e1cb35c7c853e74ffe7416a0d5d8a7b5ab1b3666bb.png ../../../_images/2cca591a7c28a0c56620d87d0f42d4fbbda49d977a908afa88b5fd32ca425de4.png ../../../_images/e0d3e73e15db405b2348ca861196c9cb5e0f5013cdcef49ce7fbfae321c16349.png ../../../_images/f495ce4d357c45bc290fdec679f45dcd58b71a89d05ba67d23548fd0db19c6e4.png ../../../_images/4e970140f9b16279112eefb020670676f44d2625d1e6d43ee58a000cfe0fe48b.png ../../../_images/9356350bf05d1f94cf9487e6f7c40d31cd44263bca4656b30455f99e1b8c49f2.png ../../../_images/90a01d92fb162b2965a58557d8c797a798a7525d75caeb924d5160f8d6f8f5b9.png

Generate table#

Table sumarizing different metrics of the data analyzed in the plots above
Number of TCs for each category

df_tcs = tcs_sel_params.to_dataframe()
df_tcs['year'] = df_tcs.dmin_date.dt.year
df_t = df_tcs.groupby('category').count()[['pressure_min']]
# fig = plot_df_table(df_t, figsize = (300, 220))

mean_tcs_per_year = df_tcs.groupby(df_tcs['dmin_date'].dt.year)['pressure_min'].count()

df_sev = df_tcs.loc[df_tcs['category'] >=3]
mean_tcs_per_year_sev = df_sev.groupby(df_sev['dmin_date'].dt.year)['pressure_min'].count()

print('Mean TCs per year: ', np.nanmean(mean_tcs_per_year))
print('Mean number of severe TCs per year: ', np.round(np.nanmean(mean_tcs_per_year_sev), 2))
Mean TCs per year:  2.780821917808219
Mean number of severe TCs per year:  1.12

ONI index#

The Oceanic Niño Index (ONI) is the standard measure used to monitor El Niño and La Niña events. It is based on sea surface temperature anomalies in the central equatorial Pacific (Niño 3.4 region) averaged over 3-month periods.

https://origin.cpc.ncep.noaa.gov/products/analysis_monitoring/ensostuff/ONI_v5.php

p_data = 'https://psl.noaa.gov/data/correlation/oni.data'
if update_data:
    df1 = download_oni_index(p_data)
    df1.to_pickle(op.join(path_data, 'oni_index.pkl'))
else:
    df1 = pd.read_pickle(op.join(path_data, 'oni_index.pkl'))

oni = df1
lims = [-.5, .5]
df1 = add_oni_cat(df1, lims = lims)
import pandas as pd
tcs_g = pd.DataFrame(tcs_sel.isel(date_time = 0).time.values)
tcs_g.index = tcs_g[0]
tcs_g.index = pd.DatetimeIndex(tcs_g.index).to_period('M').to_timestamp() + pd.offsets.MonthBegin(0)
tcs_g['oni_cat'] = oni.oni_cat
print('Number of La Niña years:', len(oni.loc[oni.oni_cat == -1].index.year.unique()))
print('Number of El Niño years:', len(oni.loc[oni.oni_cat == 1].index.year.unique()))
print('Number of Neutral years:', len(oni.loc[oni.oni_cat == 0].index.year.unique()))
Number of La Niña years: 25
Number of El Niño years: 22
Number of Neutral years: 28
tcs_sel_params['oni_cat'] = (('storm'), tcs_g['oni_cat'].values)
tcs_sel['oni_cat'] = (('storm'), tcs_g['oni_cat'].values)
# oni['ONI_cat'] = np.where(oni.ONI < lims[0], -1, np.where(oni.ONI > lims[1], 1, 0))
tcs_sel_params['oni_cat'] = (('storm'), tcs_sel.oni_cat.values)
oni_perc_cat = oni.groupby('oni_cat').size() / oni.shape[0] * 100
oni_perc_cat
oni_cat
-1    33.333333
 0    37.333333
 1    29.333333
dtype: float64
tcs_perc_cat = tcs_sel_params.to_dataframe().groupby('oni_cat').size() * 100 / tcs_sel_params.to_dataframe().shape[0]
tcs_perc_cat
oni_cat
-1.0    30.383481
 0.0    43.952802
 1.0    25.073746
dtype: float64
#Relavice probability
tcs_perc_cat / oni_perc_cat
oni_cat
-1.0    0.911504
 0.0    1.177307
 1.0    0.854787
dtype: float64
time = tcs_sel_params.dmin_date.dt.year.values
u, cu = np.unique(time, return_counts=True)
tc_c = pd.DataFrame(cu, index = u)
time_sev = tcs_sel_params.where(tcs_sel_params.category >= 3, drop = True).dmin_date.dt.year.values
u_sev, cu_sev = np.unique(time_sev, return_counts=True)
tc_c_sev = pd.DataFrame(cu_sev, index = u_sev)
oni_y = oni.groupby(oni.index.year).min()
oni_y['tc_counts'] = tc_c
oni_y['tc_counts_sev'] = tc_c_sev
oni_y['oni_cat'] = oni_y.oni_cat.values

The following bar plot represents the TC counts over time. The color of the bar represents wether it is a El Niño or La Niña year.

ax = plot_bar_probs_ONI(oni_y, 'tc_counts', y_label= 'TC counts - ALL');
../../../_images/b66ee4f7b7c615379a41fc976e320b688cf7b1f510278154ff40845ffedafb27.png

The following bar plot represents the severe TC counts over time. The color of the bar represents wether it is a El Niño or La Niña year.

ALL TCs#

The following plots represent all the TCs in the record for the three different phases of the El Niño Southern Oscillation: “El Niño”, “Neutral” and “La Niña”

names_cat = ['La Niña', 'Neutral', 'El Niño']

for ic, category in enumerate([-1, 0, 1]):

    tcs_cat = tcs_sel.where(tcs_sel_params.oni_cat == category, drop = True)
    tcs_cat_params = tcs_sel_params.where(tcs_sel_params.oni_cat == category, drop = True)

    fig, ax = Plot_TCs_HistoricalTracks_Category(
        tcs_cat, tcs_cat_params.category,
        lon1, lon2, lat1, lat2,
        lon_lat[0], lon_lat[1], r1,
    )
    ax.set_title(f'Historical TCs: {names_cat[ic]}', fontsize=15)

    plt.savefig(op.join(path_figs, f'F9_TCs_{names_cat[ic]}.png'), dpi=300, bbox_inches='tight')
../../../_images/cf6b18a62cc53ce086d42a3a9c59f312f3c852b3b96c437eb90f6cce57561e6f.png ../../../_images/7bf093b674b142cacfaf095b705a0e65481341f736e4ffb08d2b8bf66bc2b538.png ../../../_images/544e41bd0673bc7db04926ad052eded4cb405d24af2d51101a887a3d668b4371.png

Generate table#

Table sumarizing different metrics of the data analyzed in the plots above

from ind_setup.tables import style_matrix, table_tcs_32a, table_tcs_32b
style_matrix(table_tcs_32a(tcs_sel_params, oni), title = 'Key TC Metrics: Vicinity of Palau')
Key TC Metrics: Vicinity of Palau
Metric Value
Total number of tracks 339.000
Tropical Storms per year 4.644
Standard deviation of storms per year 2.343
Maximum number of storms in a year 1971 11.000
Minimum number of storms in a year 1998 1.000
Major Hurricanes (Category 3+) per year 0.123
Standard deviation of major hurricanes per year 0.331
Maximum number of major hurricanes in a year 1964 2.000
Minimum number of major hurricanes in a year 1959 1.000
EL NIÑO
Total number of storm per year 3.864
Standard deviation of storms per year 1.786
Major Hurricanes (Category 3+) per year 0.000
Standard deviation of severe storms per year
LA NIÑA
Total number of storm per year 4.120
Standard deviation of storms per year 2.552
Major Hurricanes (Category 3+) per year 0.160
Standard deviation of severe storms per year 0.471
NEUTRAL
Total number of storm per year 5.321
Standard deviation of storms per year 2.361
Major Hurricanes (Category 3+) per year 0.179
Standard deviation of severe storms per year 0.000
style_matrix(table_tcs_32b(tcs_WP, oni), title = 'Key TC Metrics: West Pacific Basin')
Key TC Metrics: West Pacific Basin
Metric Value
Total number of tracks 2664.000
Tropical Storms per year 34.597
Standard deviation of storms per year 7.797
Maximum number of storms in a year 1950 47.000
Minimum number of storms in a year 1982 9.000
Major Hurricanes (Category 3+) per year 6.039
Standard deviation of major hurricanes per year 2.597
Maximum number of major hurricanes in a year 2015 14.000
Minimum number of major hurricanes in a year 1998 1.000
EL NIÑO
Total number of storm per year 32.864
Standard deviation of storms per year 6.405
Major Hurricanes (Category 3+) per year 8.091
Standard deviation of major hurricanes per year 2.575
LA NIÑA
Total number of storm per year 34.920
Standard deviation of storms per year 7.740
Major Hurricanes (Category 3+) per year 5.040
Standard deviation of major hurricanes per year 2.341
NEUTRAL
Total number of storm per year 36.464
Standard deviation of storms per year 7.655
Major Hurricanes (Category 3+) per year 5.750
Standard deviation of major hurricanes per year 1.953