Timeseries (Bar)

Timeseries (Bar)#

# --- Packages ---
## General Packages
import pandas as pd
import xarray as xr
import numpy as np
import os
import ipynbname

## GeoCAT
import geocat.comp as gccomp
import geocat.viz as gv
import geocat.viz.util as gvutil

## Visualization
import cmaps  
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import shapely.geometry as sgeom

## MatPlotLib
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.patches as mpatches
import matplotlib.dates as mdates

from matplotlib.colors import ListedColormap, BoundaryNorm
from matplotlib.ticker import MultipleLocator
def get_filename():
    try:
        # Will fail when __file__ is undefined (e.g., in notebooks)
        filename = os.path.splitext(os.path.basename(__file__))[0]
    except NameError:
        try:
            # Will fail during non-interactive builds
            import ipynbname
            nb_path = ipynbname.path()
            filename = os.path.splitext(os.path.basename(str(nb_path)))[0]
        except Exception:
            # Fallback during Jupyter Book builds or other headless execution
            filename = "template_timeseries_bar"
    return filename

fnFIG = get_filename() + ".png"
print(f"Figure filename: {fnFIG}")
Figure filename: template_timeseries_bar.png
# READ CSV file
ystr = 1950
yend = 2024

# File path
file_path = "../data/nino34_ersst5.csv"

# Read CSV
df = pd.read_csv(
    file_path,
    skiprows=1,                     # Skip the header row with long description
    names=["Date", "Nino34"],       # Assign custom column names
    parse_dates=["Date"],           # Parse the 'Date' column as datetime
    na_values=-9999.000             # Treat -9999.000 as missing
)

df.set_index("Date", inplace=True)
dat = df["Nino34"].loc[f"{ystr}-01-01" : f"{yend}-12-31"]

# DJF mean
dat_3mo = dat.rolling(window=3, center=True).mean()
dat_DJF = dat_3mo[dat_3mo.index.month == 1]
dat_DJF.index = dat_DJF.index.year

dat_DJF
Date
1950         NaN
1951   -1.216667
1952    0.143333
1953    0.016667
1954    0.373333
          ...   
2020    0.496667
2021   -1.050000
2022   -0.963333
2023   -0.676667
2024    1.786667
Name: Nino34, Length: 75, dtype: float64
# --- FIGURE PLOT ---
# Layout setting
fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(8, 12))
col = cmaps.amwg  # Use the AMWG colormap

# --- Plot 1 ---
ip = 0
axes[ip].bar(dat_DJF.index, dat_DJF)
axes[ip].set_title("(a) Default Bar Plot")

# --- Plot 2 ---
ip = 1
axes[ip].axhline(0, color="gray", linestyle="-")
positive_color = col.colors[14-2] # Red (adjust index as needed)
negative_color = col.colors[4-2]  # Blue (adjust index as needed)
bar_colors = [positive_color if val >= 0 else negative_color for val in dat_DJF]
axes[ip].bar(dat_DJF.index, dat_DJF, color=bar_colors, label="Nino 3.4 index (DJF)")

# Generate tick locations
xtick_major = np.arange(ystr, yend + 1, 10)
ytick_major = np.arange(-3, 3.1, 1)

# Apply to the plot
gvutil.set_axes_limits_and_ticks(
    axes[ip],
    xlim=(xtick_major[0], xtick_major[-1]),
    ylim=(ytick_major[0], ytick_major[-1]),
    xticks=xtick_major,
    yticks=ytick_major,
    xticklabels=xtick_major,
    yticklabels=ytick_major
)
# Add minor ticks manually
axes[ip].xaxis.set_minor_locator(MultipleLocator(2))  # Minor ticks every 2 years
axes[ip].tick_params(axis="x", which="minor", length=4)



gvutil.set_titles_and_labels(axes[ip],
    maintitle="",
    lefttitle="(b) Customized Bar Plot 1",
    righttitle="°C",
    ylabel="Anomalies",
    xlabel="Year",
    maintitlefontsize=14,  # Adjust main title font size
    lefttitlefontsize=12,  # Set left title font size
    righttitlefontsize=12,  # Set left title font size
    labelfontsize=10  # Set y-label font size
)

axes[ip].grid(True, which="both", axis="both")

# --- Plot 3 ---

def setup_axis(ax, ystr, yend, ymin, ymax, y_major, y_minor):
    # Make grid go below the bars
    ax.set_axisbelow(True)

    # X-Axis
    #ax.xaxis.set_major_locator(MultipleLocator(10))
    ax.xaxis.set_minor_locator(MultipleLocator(1))
    ax.set_xlim(ystr,yend)

    # Y-Axis
    ax.set_ylim(ymin, ymax)
    ax.yaxis.set_minor_locator(MultipleLocator(y_minor))

    # Customized grid setting
    ax.tick_params(axis="both", which="major", length=7)
    ax.tick_params(axis="both", which="minor", length=4)
    ax.grid(visible=True, which="major", linestyle="-", linewidth=0.7, alpha=0.7)
    ax.grid(visible=True, which="minor", linestyle="--", linewidth=0.4, alpha=0.5)

ip = 2
positive_color = col.colors[16-2] # Red (adjust index as needed)
negative_color = col.colors[6-2]  # Blue (adjust index as needed)
positive_color_edge = col.colors[14-2] # Red (adjust index as needed)
negative_color_edge = col.colors[4-2]  # Blue (adjust index as needed)

bar_colors = [positive_color if val >= 0 else negative_color for val in dat_DJF]
edge_colors = [positive_color_edge if val >= 0 else negative_color_edge for val in dat_DJF]

axes[ip].bar(dat_DJF.index, dat_DJF, color=bar_colors, edgecolor=edge_colors,
             linewidth=1, label="Nino 3.4 index (DJF)")
setup_axis(axes[ip], ystr, yend+1, -3., 3., 1, 0.2)

gvutil.set_titles_and_labels(axes[ip],
    maintitle="",
    lefttitle="(c) Customized Bar Plot 2",
    righttitle="°C",
    ylabel="Anomalies",
    xlabel="Year",
    maintitlefontsize=14,  # Adjust main title font size
    lefttitlefontsize=12,  # Set left title font size
    righttitlefontsize=12,  # Set left title font size
    labelfontsize=10  # Set y-label font size
)
axes[ip].axhline(0, color="gray", linestyle="-")
#axes[ip].legend()

# Apply the overall figure title
fig.suptitle("Nino 3.4 Index in DJF", fontsize=16, fontweight="bold")

# --- OUTPUT ---
plt.tight_layout()
plt.savefig(fnFIG, dpi=300, bbox_inches="tight")
plt.show()
../_images/4ac0174d0f78a15d05fe3b522c857f97f3939e045b03b156253bbc64684f9709.png