|
# Install required packages |
|
!pip install -q requests matplotlib |
|
|
|
import requests |
|
import os |
|
import matplotlib.pyplot as plt |
|
import matplotlib.font_manager as fm |
|
fm._load_fontmanager(try_read_cache=False) |
|
from pathlib import Path |
|
import warnings |
|
import numpy as np |
|
|
|
# Set the figure DPI to 300 |
|
plt.rcParams['figure.dpi'] = 300 |
|
|
|
# Suppress font-related warnings |
|
warnings.filterwarnings("ignore", |
|
message="findfont: Font family .* not found", |
|
category=UserWarning, |
|
module="matplotlib.font_manager") |
|
|
|
# Download the xkcd font file |
|
font_url = "https://github.com/ipython/xkcd-font/raw/master/xkcd-script/font/xkcd-script.ttf" |
|
font_path = "/tmp/xkcd-script.ttf" |
|
|
|
# Download the font |
|
response = requests.get(font_url) |
|
with open(font_path, "wb") as f: |
|
f.write(response.content) |
|
|
|
# Update matplotlib's font cache |
|
fm.fontManager.addfont(font_path) |
|
|
|
# Use XKCD style for a fun illustration |
|
plt.xkcd() |
|
|
|
# Increase the global font size |
|
plt.rcParams.update({ |
|
'font.size': 16, # Increase base font size |
|
'axes.titlesize': 18, # Title font size |
|
'axes.labelsize': 14, # Axis label font size |
|
'xtick.labelsize': 14, # X-tick label size |
|
'ytick.labelsize': 14, # Y-tick label size |
|
'legend.fontsize': 16, # Legend font size |
|
'figure.titlesize': 24 # Figure title size |
|
}) |
|
|
|
# Sample data (replace with your actual data) |
|
obs_precip = [10, 5, 18, 12, 7, 15, 20] |
|
sim_precip = [8, 3, 22, 15, 5, 12, 25] |
|
|
|
# ---------------------------- |
|
# Step 1: Linear Scaling (LS) |
|
# ---------------------------- |
|
def scaling_factor(obs, sim): |
|
return sum(obs) / sum(sim) |
|
|
|
ls_factor = scaling_factor(obs_precip, sim_precip) |
|
# LS adjusted precipitation is the simulated data scaled by LS factor |
|
adjusted_precip = [p * ls_factor for p in sim_precip] |
|
|
|
# ---------------------------- |
|
# Step 2: Quantile Mapping (EQM) |
|
# ---------------------------- |
|
# Compute cumulative distribution function (CDF) |
|
def cdf(data): |
|
n = len(data) |
|
return [(i + 1) / n for i in range(n)] |
|
|
|
# Compute CDFs for observed and LS-adjusted data |
|
obs_sorted = sorted(obs_precip) |
|
adjusted_sorted = sorted(adjusted_precip) |
|
obs_cdf = cdf(obs_sorted) |
|
adj_cdf = cdf(adjusted_sorted) |
|
|
|
# EQM: bias correction via quantile mapping |
|
def bias_correct(obs_val, obs_precip, adjusted_precip): |
|
obs_sorted = sorted(obs_precip) |
|
adj_sorted = sorted(adjusted_precip) |
|
obs_percentile = obs_sorted.index(obs_val) / len(obs_sorted) |
|
# Ensure index is within bounds |
|
adj_index = min(int(obs_percentile * len(adj_sorted)), len(adj_sorted) - 1) |
|
return adj_sorted[adj_index] |
|
|
|
# Compute bias-corrected precipitation using EQM |
|
corr_precip_all = [bias_correct(val, obs_precip, adjusted_precip) for val in obs_precip] |
|
|
|
# ---------------------------- |
|
# Step 3: Deep Learning (DL) Correction |
|
# ---------------------------- |
|
# Simulated DL adjustment: for extreme events (e.g., precipitation > 15), apply an additional factor. |
|
def apply_deep_learning_adjustment(precip_data, threshold=15, adjustment_factor=1.1): |
|
corrected_data = [] |
|
for val in precip_data: |
|
if val > threshold: |
|
corrected_data.append(val * adjustment_factor) |
|
else: |
|
corrected_data.append(val) |
|
return corrected_data |
|
|
|
# Apply DL correction on the EQM corrected values |
|
dl_corrected_precip_all = apply_deep_learning_adjustment(corr_precip_all, threshold=15, adjustment_factor=1.1) |
|
|
|
# Compute DL adjustment differences: DL correction minus EQM correction |
|
dl_diff = [dl - eqm for dl, eqm in zip(dl_corrected_precip_all, corr_precip_all)] |
|
|
|
# ---------------------------- |
|
# Step 4: Define a Sample Day for Detailed Comparison |
|
# ---------------------------- |
|
day_to_correct = 5 # change index if needed |
|
|
|
# ---------------------------- |
|
# Step 5: Composite Plot: 2 Rows × 4 Columns (Figures 1 to 8) |
|
# ---------------------------- |
|
|
|
# Increase figure size to give more room for text |
|
fig, axes = plt.subplots(2, 4, figsize=(24, 12)) |
|
|
|
# --- Figure 1: Original Precipitation Data --- |
|
ax = axes[0, 0] |
|
width = 0.35 |
|
x = np.arange(len(obs_precip)) |
|
ax.bar(x - width/2, obs_precip, width, label='Observed', color='blue') |
|
ax.bar(x + width/2, sim_precip, width, label='Satellite', color='orange') |
|
ax.set_title('1: Precipitation Data') |
|
ax.set_xlabel('Day') |
|
ax.set_ylabel('Precip (mm)') |
|
ax.legend() |
|
|
|
# --- Figure 2: LS – Mean Values Before Scaling --- |
|
ax = axes[0, 1] |
|
ax.bar(['Observed Mean', 'Satellite Mean'], |
|
[np.mean(obs_precip), np.mean(sim_precip)], |
|
color=['blue', 'orange']) |
|
ax.set_title('2: Mean Values Before Scaling (LS)') |
|
ax.set_ylabel('Precip (mm)') |
|
|
|
# --- Figure 3: LS – Linear Scaling Applied --- |
|
ax = axes[0, 2] |
|
ax.bar(range(len(adjusted_precip)), adjusted_precip, color='green', label='LS Adjusted') |
|
ax.set_title('3: Linear Scaling Applied') |
|
ax.set_xlabel('Day') |
|
ax.set_ylabel('Precip (mm)') |
|
ax.legend() |
|
|
|
# --- Figure 4: LS/EQM – CDFs Comparison --- |
|
ax = axes[0, 3] |
|
# Plot observed and LS-adjusted sorted values vs. their CDF |
|
ax.plot(obs_sorted, np.linspace(0, 1, len(obs_sorted), endpoint=False), |
|
marker='.', markersize=12, linestyle='none', label='Observed CDF', color='blue') |
|
ax.plot(adjusted_sorted, np.linspace(0, 1, len(adjusted_sorted), endpoint=False), |
|
marker='.', markersize=12, linestyle='none', label='LS Adjusted CDF', color='red') |
|
ax.set_title('4: CDFs Comparison (LS/EQM)') |
|
ax.set_xlabel('Precip (mm)') |
|
ax.set_ylabel('Probability') |
|
ax.legend() |
|
|
|
# --- Figure 5: EQM – Quantile Mapping Example --- |
|
ax = axes[1, 0] |
|
# Mark the observed value and the EQM-corrected value for the sample day |
|
ax.axvline(x=obs_precip[day_to_correct], color='red', linestyle='--', linewidth=2, |
|
label=f'Observed ({obs_precip[day_to_correct]})') |
|
ax.axvline(x=corr_precip_all[day_to_correct], color='black', linewidth=2, |
|
label='Bias-Corrected (EQM)') |
|
ax.axhline(y=obs_cdf[day_to_correct], color='red', linestyle='--', linewidth=2) |
|
# Plot original simulated value for reference |
|
ax.bar(day_to_correct, sim_precip[day_to_correct], color='orange', alpha=0.5, |
|
label='Original Satellite') |
|
ax.text(day_to_correct + 0.1, sim_precip[day_to_correct] + 0.005, |
|
f'{sim_precip[day_to_correct]:.1f}', color='orange', fontsize=14, ha='center', va='bottom') |
|
ax.set_title(f'5: Quantile Mapping Example (Day {day_to_correct})') |
|
ax.set_xlabel('Precip (mm)') |
|
ax.set_ylabel('CDF Value') |
|
ax.legend() |
|
|
|
# --- Figure 6: DL – DL Adjustment Differences --- |
|
ax = axes[1, 1] |
|
ax.bar(range(len(dl_diff)), dl_diff, color='purple') |
|
ax.set_title('6: DL Adjustment Differences\n(DL Correction minus EQM)') |
|
ax.set_xlabel('Day') |
|
ax.set_ylabel('Difference (mm)') |
|
|
|
# --- Figure 7: DL – Scatter Plot of EQM vs. DL Corrected Values --- |
|
ax = axes[1, 2] |
|
ax.scatter(corr_precip_all, dl_corrected_precip_all, color='magenta', s=100) # Increased marker size |
|
ax.plot([min(corr_precip_all), max(corr_precip_all)], |
|
[min(corr_precip_all), max(corr_precip_all)], |
|
color='gray', linestyle='--', linewidth=2) # thicker diagonal reference line |
|
ax.set_title('7: EQM vs. DL Corrected Values') |
|
ax.set_xlabel('Bias-Corrected (EQM)') |
|
ax.set_ylabel('DL Corrected') |
|
|
|
# --- Figure 8: Final Corrected Data with DL --- |
|
ax = axes[1, 3] |
|
ax.bar(x - width/2, obs_precip, width, label='Observed', color='blue') |
|
ax.bar(x + width/2, dl_corrected_precip_all, width, label='Bias-Corrected + DL', color='red') |
|
ax.set_title('8: Final Corrected Data') |
|
ax.set_xlabel('Day') |
|
ax.set_ylabel('Precip (mm)') |
|
ax.legend() |
|
|
|
# Add more space between subplots to prevent text overlap |
|
plt.subplots_adjust(wspace=0.3, hspace=0.4) |
|
|
|
plt.tight_layout() |
|
plt.show() |