Skip to main content Skills Marketplace Découvrez et explorez les compétences IA créées par la communauté.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Copier le promptAfficher les détails du prompt Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill climate-trendsLa commande reste sur une seule ligne. Faites défiler horizontalement pour la vérifier avant de la copier.
Vous préférez une copie locale ? Téléchargez les fichiers actuellement disponibles dans SkillsMP.
Télécharger Zip Téléchargement... Métiers associés SOC
Basé sur la classification professionnelle SOC
name climate-trends description Use this Skill for climate trend analysis: Mann-Kendall test, Sen's slope, extreme indices (RX1day, R10mm), percentile thresholds, IPCC-style figures.
tags ["earth-science","climate","trend-analysis","xclim","extreme-events"] version 1.0.0 authors [{"name":"Rosetta Skills Contributors","github":"@xjtulyc"}] license MIT platforms ["claude-code","codex","gemini-cli","cursor"] dependencies {"python":["xclim>=0.45","scipy>=1.11","matplotlib>=3.7","numpy>=1.24","xarray>=2023.6","pandas>=2.0","cartopy>=0.22"]} last_updated 2026-03-17 status stable
Climate Trend Analysis
One-line summary : Compute climate trends (Mann-Kendall, Sen's slope), extreme precipitation/temperature indices, and produce IPCC AR6-style figures using xclim, scipy, and xarray.
When to Use This Skill
When testing for statistically significant trends in temperature or precipitation
When computing ETCCDI climate extreme indices (RX1day, R10mm, TX90p, etc.)
When creating IPCC AR6-style climate change figures
When analyzing historical climate data (ERA5, CMIP6, station data)
When quantifying changes in extreme events across decades
When comparing model projections with observed trends
Trigger keywords : Mann-Kendall trend test, Sen's slope, climate extremes, ETCCDI, RX1day, R10mm, TX90p, precipitation trend, temperature trend, IPCC figure
Background & Key Concepts
Mann-Kendall Trend Test
Non-parametric test for monotonic trends in time series:
$$
S = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \text{sgn}(x_j - x_i)
$$
Under $H_0$ (no trend), $S \sim N(0, V(S))$. Reject $H_0$ if $|Z| > z_{\alpha/2}$.
Sen's Slope Estimator
Robust, non-parametric trend magnitude:
$$
\hat{\beta} = \text{median}\left{ \frac{x_j - x_i}{j - i} : i < j \right}
$$
ETCCDI Climate Extreme Indices
Index Definition Unit RX1day Max 1-day precipitation mm RX5day Max 5-day precipitation mm R10mm Annual days with precip ≥ 10mm days R20mm Annual days with precip ≥ 20mm days TX90p Days with Tmax > 90th percentile % TN10p Days with Tmin < 10th percentile % WSDI Warm spell duration index days CDD
Environment Setup
Install Dependencies pip install xclim>=0.45 scipy>=1.11 matplotlib>=3.7 numpy>=1.24 \
xarray>=2023.6 pandas>=2.0 cartopy>=0.22
Verify Installation import xclim
import xarray as xr
import scipy
import numpy as np
print (f"xclim: {xclim.__version__} " )
print (f"xarray: {xr.__version__} " )
print (f"scipy: {scipy.__version__} " )
Core Workflow
Step 1: Load and Prepare Climate Data import numpy as np
import xarray as xr
import pandas as pd
import matplotlib.pyplot as plt
def create_synthetic_climate_dataset (n_years=60 , seed=42 ):
"""
Create a synthetic daily climate dataset (temperature + precipitation).
Replace with: xr.open_dataset('era5_data.nc') or similar.
"""
rng = np.random.default_rng(seed)
dates = pd.date_range("1965-01-01" , periods=n_years*365 , freq="D" )
years = dates.year
doy = dates.day_of_year
trend_temp = 0.02 * (years - 1965 )
seasonal_temp = 15 * np.sin(2 * np.pi * (doy - 90 ) / 365 )
noise_temp = rng.normal(0 , 2 , len (dates))
tas = 15 + trend_temp + seasonal_temp + noise_temp
trend_pr = 0.5 * (years - 1965 ) / n_years
seasonal_pr = 2 + 2 * np.cos(2 * np.pi * (doy - 200 ) / 365 )
pr = np.maximum(0 , seasonal_pr + trend_pr + rng.exponential(3 , len (dates)) - 3 )
ds = xr.Dataset({
"tas" : xr.DataArray(tas, dims=["time" ],
attrs={"units" : "degC" , "long_name" : "Near-surface air temperature" }),
"pr" : xr.DataArray(pr, dims=["time" ],
attrs={"units" : "mm/day" , "long_name" : "Precipitation" }),
}, coords={"time" : dates})
return ds
ds = create_synthetic_climate_dataset()
print (f"Dataset: {len (ds.time)} days ({ds.time.dt.year.min ().item()} –{ds.time.dt.year.max ().item()} )" )
print (f"Temperature range: {float (ds.tas.min ()):.1 f} – {float (ds.tas.max ()):.1 f} °C" )
print (f"Max daily precip: {float (ds.pr.max ()):.1 f} mm/day" )
tas_annual = ds.tas.resample(time="YE" ).mean()
pr_annual = ds.pr.resample(time="YE" ).sum ()
years = tas_annual.time.dt.year.values
print (f"\nAnnual temp: {float (tas_annual.mean()):.2 f} ± {float (tas_annual.std()):.2 f} °C" )
print (f"Annual precip: {float (pr_annual.mean()):.0 f} ± {float (pr_annual.std()):.0 f} mm/year" )
Step 2: Mann-Kendall Trend Test and Sen's Slope import numpy as np
import scipy.stats as stats
from scipy.stats import kendalltau
import matplotlib.pyplot as plt
def mann_kendall_test (series ):
"""
Mann-Kendall trend test and Sen's slope estimator.
Returns
-------
dict with: tau, p_value, sen_slope, confidence_interval
"""
x = np.asarray(series)
n = len (x)
tau, p_value = kendalltau(np.arange(n), x)
slopes = []
for i in range (n):
for j in range (i+1 , n):
slopes.append((x[j] - x[i]) / (j - i))
sen_slope = np.median(slopes)
C_alpha = 1.96 * np.sqrt(n*(n-1 )*(2 *n+5 )/18 )
M1 = int ((len (slopes) - C_alpha) / 2 )
M2 = int ((len (slopes) + C_alpha) / 2 )
slopes_sorted = sorted (slopes)
ci_lo = slopes_sorted[max (0 , M1)]
ci_hi = slopes_sorted[min (len (slopes)-1 , M2)]
return {
"tau" : tau,
"p_value" : p_value,
"sen_slope" : sen_slope,
"ci_95" : (ci_lo, ci_hi),
"significant" : p_value < 0.05 ,
}
tas_annual_np = float (tas_annual.mean()) + np.arange(len (tas_annual)) * 0.025 + \
np.random.normal(0 , 0.5 , len (tas_annual))
result_temp = mann_kendall_test(tas_annual_np)
print ("Temperature trend analysis:" )
print (f" Kendall τ = {result_temp['tau' ]:.4 f} , p = {result_temp['p_value' ]:.4 f} " )
print (f" Sen's slope = {result_temp['sen_slope' ]:.4 f} °C/year (95% CI: {result_temp['ci_95' ][0 ]:.4 f} – {result_temp['ci_95' ][1 ]:.4 f} )" )
print (f" Significant trend: {result_temp['significant' ]} " )
years_arr = np.arange(1965 , 1965 + len (tas_annual_np))
trend_line = result_temp['sen_slope' ] * (years_arr - years_arr.mean()) + np.mean(tas_annual_np)
fig, ax = plt.subplots(figsize=(10 , 5 ))
ax.bar(years_arr, tas_annual_np, color="salmon" , alpha=0.6 , label="Annual mean T" )
ax.plot(years_arr, trend_line, 'r-' , linewidth=2.5 ,
label=f"Sen's slope: {result_temp['sen_slope' ]*10 :.2 f} °C/decade" )
ax.set_xlabel("Year" ); ax.set_ylabel("Temperature (°C)" )
ax.set_title(f"Annual Temperature Trend (τ={result_temp['tau' ]:.3 f} , p={result_temp['p_value' ]:.3 f} )" )
ax.legend()
plt.tight_layout()
plt.savefig("temperature_trend.png" , dpi=150 )
plt.show()
Step 3: ETCCDI Extreme Indices with xclim import xclim
import xarray as xr
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
ds = create_synthetic_climate_dataset()
tas_K = ds.tas + 273.15
tas_K.attrs["units" ] = "K"
pr_kgm2s = ds.pr / 86400
pr_kgm2s.attrs["units" ] = "kg m-2 s-1"
print ("Computing ETCCDI indices..." )
rx1day = xclim.indices.max_1day_precipitation_amount(pr=pr_kgm2s, freq="YS" )
print (f"RX1day range: {float (rx1day.min ()):.1 f} – {float (rx1day.max ()):.1 f} mm" )
r10mm = xclim.indices.days_over_precip_thresh(
pr=pr_kgm2s,
per=xr.DataArray([10 /86400 ], dims=["quantile" ],
attrs={"units" : "kg m-2 s-1" }),
freq="YS"
)
print (f"R10mm (days ≥ 10mm/day): {float (r10mm.mean()):.1 f} days/year" )
tasmax = tas_K + np.abs (np.random.normal(3 , 1 , len (tas_K)))
tasmax_da = xr.DataArray(tasmax, dims=["time" ],
coords={"time" : ds.time},
attrs={"units" : "K" })
t90p = xclim.indices.tg90p(tas=tas_K, t90=tas_K.quantile(0.90 ).values, freq="YS" )
fig, axes = plt.subplots(2 , 2 , figsize=(12 , 8 ))
time_years = rx1day.time.dt.year.values
for ax, data, label in zip (axes.flat,
[rx1day*86400 , r10mm, t90p, (tas_K - 273.15 ).resample(time="YE" ).mean()],
["RX1day (mm)" , "R10mm (days)" , "TX90p (%)" , "Annual mean T (°C)" ]):
arr = data.values.flatten()
ax.bar(time_years[:len (arr)], arr, alpha=0.7 , color="steelblue" )
if len (arr) > 3 :
slope, intercept, *_ = np.polyfit(range (len (arr)), arr, 1 ), None , None
slope = np.polyfit(range (len (arr)), arr, 1 )[0 ]
trend = np.polyfit(range (len (arr)), arr, 1 )
ax.plot(time_years[:len (arr)], np.polyval(trend, range (len (arr))),
'r--' , linewidth=1.5 , label=f"Trend: {slope:.3 f} /yr" )
ax.set_ylabel(label)
ax.legend(fontsize=8 )
plt.suptitle("ETCCDI Climate Extreme Indices (1965–2024)" )
plt.tight_layout()
plt.savefig("etccdi_indices.png" , dpi=150 )
plt.show()
Advanced Usage
IPCC AR6-Style Warming Stripe Figure import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
def warming_stripes (annual_temps, start_year=1965 , reference_period=(1981 , 2010 ) ):
"""
Create IPCC AR6-style warming stripes.
"""
temps = np.array(annual_temps)
years = np.arange(start_year, start_year + len (temps))
ref_mask = (years >= reference_period[0 ]) & (years <= reference_period[1 ])
ref_mean = temps[ref_mask].mean()
anomaly = temps - ref_mean
cmap = mcolors.LinearSegmentedColormap.from_list(
"warming_stripes" ,
["#08306b" , "#2171b5" , "#6baed6" , "#c6dbef" , "#ffffff" ,
"#fcbba1" , "#fb6a4a" , "#cb181d" , "#67000d" ]
)
vmin, vmax = -np.abs (anomaly).max (), np.abs (anomaly).max ()
norm = mcolors.TwoSlopeNorm(vmin=vmin, vcenter=0 , vmax=vmax)
fig, ax = plt.subplots(figsize=(12 , 3 ))
for i, (year, anom) in enumerate (zip (years, anomaly)):
ax.axvspan(year - 0.5 , year + 0.5 , color=cmap(norm(anom)))
ax.set_xlim(years[0 ] - 0.5 , years[-1 ] + 0.5 )
ax.axis("off" )
ax.set_title(f"Temperature Anomaly ({start_year} –{start_year+len (temps)-1 } )" ,
fontsize=14 , fontweight="bold" , pad=10 )
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
cbar = plt.colorbar(sm, ax=ax, orientation='horizontal' ,
fraction=0.04 , pad=0.01 , shrink=0.5 )
cbar.set_label("Temperature anomaly (°C)" , fontsize=10 )
plt.tight_layout()
plt.savefig("warming_stripes.png" , dpi=200 , bbox_inches="tight" )
plt.show()
return anomaly
rng = np.random.default_rng(42 )
n = 60
simulated_temps = (15 + 0.025 * np.arange(n) +
rng.normal(0 , 0.5 , n))
anomalies = warming_stripes(simulated_temps)
print (f"Max anomaly: {anomalies.max ():.2 f} °C" )
Troubleshooting
Error: xclim.core.utils.ValidationError: units Cause : Input DataArray units attribute missing or wrong.
pr_kgm2s.attrs["units" ] = "kg m-2 s-1"
tas_K.attrs["units" ] = "K"
Issue: Mann-Kendall returns p-value near 1.0 for obvious trend Cause : Short time series (< 10 points) or too much variability.
tau, p = stats.kendalltau(x, y, method='auto' )
Version Compatibility Package Tested versions Known issues xclim 0.45, 0.48 Index API may change between major versions xarray 2023.6, 2024.3 None
External Resources
Official Documentation
Key Papers
Zhang, X. et al. (2011). Indices for monitoring changes in extremes based on daily temperature and precipitation data . WIREs Climate Change.
Examples
Example 1: Precipitation Frequency Analysis
import numpy as np, pandas as pd, matplotlib.pyplot as plt
from scipy import stats
rng = np.random.default_rng(42 )
n_years = 60
years = np.arange(1965 , 1965 + n_years)
annual_max = stats.genextreme.rvs(c=0.1 , loc=50 , scale=15 , size=n_years, random_state=42 )
annual_max += 0.3 * (years - 1965 ) / n_years * annual_max
shape, loc, scale = stats.genextreme.fit(annual_max)
print (f"GEV fit: shape={shape:.3 f} , loc={loc:.1 f} , scale={scale:.1 f} " )
return_periods = [2 , 5 , 10 , 25 , 50 , 100 ]
exceedance_probs = [1 /rp for rp in return_periods]
return_levels = stats.genextreme.isf(exceedance_probs, shape, loc, scale)
fig, axes = plt.subplots(1 , 2 , figsize=(12 , 5 ))
axes[0 ].bar(years, annual_max, alpha=0.7 , color="steelblue" )
slope, intercept, *_ = stats.linregress(years, annual_max)
axes[0 ].plot(years, slope*years + intercept, 'r--' , linewidth=2 ,
label=f"Trend: {slope:.2 f} mm/yr" )
axes[0 ].set_xlabel("Year" ); axes[0 ].set_ylabel("Annual max 1-day precip (mm)" )
axes[0 ].set_title("Annual Maximum Precipitation" ); axes[0 ].legend()
axes[1 ].semilogx(return_periods, return_levels, 'bs-' , linewidth=2 )
axes[1 ].set_xlabel("Return period (years)" )
axes[1 ].set_ylabel("Return level (mm)" )
axes[1 ].set_title("Precipitation Frequency Curve (GEV)" )
axes[1 ].grid(True , which="both" , alpha=0.3 )
plt.tight_layout()
plt.savefig("precip_frequency.png" , dpi=150 )
plt.show()
for rp, rl in zip (return_periods, return_levels):
print (f" {rp:3d} -year return level: {rl:.1 f} mm" )
Interpreting these results : A 100-year return level of X mm means that, on average, this precipitation amount is exceeded once every 100 years. Positive trend in annual maxima suggests increasing extreme precipitation.
Last updated: 2026-03-17 | Maintainer: @xjtulyc
Issues: GitHub Issues