From 1cd21d3f1502a6d734c9d49ede14bcef1d768853 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:13:25 +0000 Subject: [PATCH] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- carbonplan_forest_risks/__init__.py | 2 +- carbonplan_forest_risks/collect.py | 12 +- carbonplan_forest_risks/fit/growth.py | 25 +- carbonplan_forest_risks/fit/hurdle.py | 13 +- carbonplan_forest_risks/fit/interp.py | 12 +- carbonplan_forest_risks/load/cmip.py | 81 +- carbonplan_forest_risks/load/fia.py | 354 ++-- carbonplan_forest_risks/load/mask.py | 8 +- carbonplan_forest_risks/load/mtbs.py | 8 +- carbonplan_forest_risks/load/nftd.py | 31 +- carbonplan_forest_risks/load/nlcd.py | 35 +- carbonplan_forest_risks/load/terraclim.py | 80 +- carbonplan_forest_risks/load/tiff.py | 34 +- carbonplan_forest_risks/plot/carto.py | 58 +- carbonplan_forest_risks/plot/fire.py | 197 +- carbonplan_forest_risks/plot/line.py | 30 +- carbonplan_forest_risks/plot/paper.py | 54 +- carbonplan_forest_risks/plot/xy.py | 36 +- carbonplan_forest_risks/prepare.py | 116 +- carbonplan_forest_risks/preprocess/fia.py | 271 +-- carbonplan_forest_risks/setup/loading.py | 14 +- carbonplan_forest_risks/setup/plotting.py | 6 +- carbonplan_forest_risks/utils.py | 10 +- notebooks/biomass/biomass_model.ipynb | 510 ++--- notebooks/biomass/biomass_results.ipynb | 410 ++-- notebooks/drought/drought_model.ipynb | 466 ++--- notebooks/drought/drought_results.ipynb | 290 +-- notebooks/fire/figure_foresttype.ipynb | 624 +++--- notebooks/fire/fire_model.ipynb | 1130 +++++------ notebooks/fire/fire_model_redux.ipynb | 1730 ++++++++--------- notebooks/fire/fire_model_supersections.ipynb | 526 ++--- notebooks/insects/insects_model.ipynb | 468 ++--- notebooks/insects/insects_results.ipynb | 308 +-- .../change-factor-calculations.ipynb | 288 +-- notebooks/paper/Figure-1/Figure-1.ipynb | 736 +++---- notebooks/paper/Figure-2/Figure-2.ipynb | 702 +++---- notebooks/paper/Figure-3/Figure-3.ipynb | 656 +++---- notebooks/paper/Figure-4/Figure-4.ipynb | 578 +++--- .../Supplementary-Figure-01.ipynb | 504 ++--- .../Supplementary-Figure-02.ipynb | 156 +- .../Supplementary-Figure-03.ipynb | 368 ++-- .../Supplementary-Figure-04.ipynb | 208 +- .../Supplementary-Figure-05.ipynb | 512 ++--- .../Supplementary-Figure-06.ipynb | 172 +- .../Supplementary-Figure-07.ipynb | 212 +- .../Supplementary-Figure-08.ipynb | 828 ++++---- .../Supplementary-Figure-09.ipynb | 266 +-- .../Supplementary-Figure-10.ipynb | 340 ++-- .../Supplementary-Figure-11.ipynb | 454 ++--- notebooks/share/fire_risks_counties.ipynb | 432 ++-- scripts/article_prep.py | 178 +- scripts/biomass.py | 84 +- scripts/convert.py | 38 +- scripts/drought.py | 48 +- scripts/export.py | 64 +- scripts/fire.py | 171 +- scripts/insects.py | 48 +- scripts/package_insects_drought.py | 34 +- scripts/regrid.py | 51 +- scripts/regrid_bill.py | 24 +- scripts/stats.py | 78 +- scripts/website_prep.py | 36 +- tests/test_forests.py | 2 +- 63 files changed, 8206 insertions(+), 8011 deletions(-) diff --git a/carbonplan_forest_risks/__init__.py b/carbonplan_forest_risks/__init__.py index 916b2ff..052df2e 100644 --- a/carbonplan_forest_risks/__init__.py +++ b/carbonplan_forest_risks/__init__.py @@ -5,5 +5,5 @@ try: version = get_distribution(__name__).version except DistributionNotFound: # pragma: no cover - version = '0.0.0' # pragma: no cover + version = "0.0.0" # pragma: no cover __version__ = version diff --git a/carbonplan_forest_risks/collect.py b/carbonplan_forest_risks/collect.py index 1d55c67..11b316d 100644 --- a/carbonplan_forest_risks/collect.py +++ b/carbonplan_forest_risks/collect.py @@ -4,11 +4,11 @@ def fire(y, src, inds=None): da = xr.Dataset() - da['x'] = src.x - da['y'] = src.y - da['time'] = src.time - da['lat'] = (['y', 'x'], src['lat']) - da['lon'] = (['y', 'x'], src['lon']) + da["x"] = src.x + da["y"] = src.y + da["time"] = src.time + da["lat"] = (["y", "x"], src["lat"]) + da["lon"] = (["y", "x"], src["lon"]) shape = (len(src.time), len(src.y), len(src.x)) if inds is None: @@ -17,6 +17,6 @@ def fire(y, src, inds=None): yfull = np.zeros(shape).flatten() yfull[inds] = y yfull = yfull.reshape(shape) - da['prediction'] = (['time', 'y', 'x'], yfull) + da["prediction"] = (["time", "y", "x"], yfull) return da diff --git a/carbonplan_forest_risks/fit/growth.py b/carbonplan_forest_risks/fit/growth.py index 475ba22..128b9c6 100644 --- a/carbonplan_forest_risks/fit/growth.py +++ b/carbonplan_forest_risks/fit/growth.py @@ -14,13 +14,13 @@ def logistic(x, f, p): ) -def growth(x, y, f, noise='gamma', init=None): +def growth(x, y, f, noise="gamma", init=None): def loglik(x, y, f, p): a, b, c, w0, w1, scale = p _mu = logistic(x, f, [a, b, c, w0, w1]) - if noise == 'gamma': + if noise == "gamma": return -np.sum(gamma.logpdf(y, np.maximum(_mu / scale, 1e-12), scale=scale)) - if noise == 'normal': + if noise == "normal": return -np.sum(norm.logpdf(y, loc=_mu, scale=scale)) fx = lambda p: loglik(x, y, f, p) @@ -33,14 +33,16 @@ def loglik(x, y, f, p): bounds = Bounds(lb, ub) if init is None: init = [np.nanmean(y), 0.1, 10, 0, 0, np.nanstd(y)] - options_trust = {'maxiter': 10000} + options_trust = {"maxiter": 10000} with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=UserWarning) - result = minimize(fx, init, bounds=bounds, method='trust-constr', options=options_trust) + warnings.filterwarnings("ignore", category=UserWarning) + result = minimize( + fx, init, bounds=bounds, method="trust-constr", options=options_trust + ) if result.success is False: - print('optimization failed') + print("optimization failed") return GrowthModel(result, noise, x, y, f) @@ -63,12 +65,15 @@ def r2(self, x, f, y): def predict(self, x, f, percentile=None): if percentile is not None: - f = [np.nanpercentile(f[0], percentile[0]), np.nanpercentile(f[1], percentile[1])] + f = [ + np.nanpercentile(f[0], percentile[0]), + np.nanpercentile(f[1], percentile[1]), + ] return logistic(x, f, self.p) def sample(self, x, f): mu = logistic(x, f, self.p) - if self.noise == 'gamma': + if self.noise == "gamma": return np.random.gamma(mu / self.scale, scale=self.scale) - if self.noise == 'normal': + if self.noise == "normal": return np.random.normal(mu, scale=self.scale) diff --git a/carbonplan_forest_risks/fit/hurdle.py b/carbonplan_forest_risks/fit/hurdle.py index 4a3683e..211fd13 100644 --- a/carbonplan_forest_risks/fit/hurdle.py +++ b/carbonplan_forest_risks/fit/hurdle.py @@ -9,11 +9,16 @@ def hurdle(x, y, log=True, max_iter=1000): x, y = remove_nans(x, y) n_obs = len(x) - clf = LogisticRegression(fit_intercept=True, penalty='none', max_iter=max_iter) + clf = LogisticRegression(fit_intercept=True, penalty="none", max_iter=max_iter) if log: reg = TweedieRegressor( - fit_intercept=True, power=0, link='log', alpha=0, tol=1e-8, max_iter=max_iter + fit_intercept=True, + power=0, + link="log", + alpha=0, + tol=1e-8, + max_iter=max_iter, ) else: reg = LinearRegression(fit_intercept=True) @@ -31,7 +36,9 @@ def __init__(self, clf, reg, n_obs, log=None, x=None, y=None): self.log = log self.n_obs = n_obs if x is not None and y is not None: - self.train_r2 = np.corrcoef(self.predict_linear(x)[y > 0], y[y > 0])[0, 1] ** 2 + self.train_r2 = ( + np.corrcoef(self.predict_linear(x)[y > 0], y[y > 0])[0, 1] ** 2 + ) self.train_roc = roc_auc_score(y > 0, self.predict_prob(x)) def __repr__(self): diff --git a/carbonplan_forest_risks/fit/interp.py b/carbonplan_forest_risks/fit/interp.py index fc60a3b..c5e4f88 100644 --- a/carbonplan_forest_risks/fit/interp.py +++ b/carbonplan_forest_risks/fit/interp.py @@ -8,11 +8,11 @@ def score(y_true, y_pred): m = ~np.isnan(y_pred) if len(y_pred) != m.sum(): - print(f'found {len(m) - m.sum()} nans in y_pred') + print(f"found {len(m) - m.sum()} nans in y_pred") return r2_score(y_true[m], y_pred[m]) -def interp(df, mask, var='biomass', spacing=4000): +def interp(df, mask, var="biomass", spacing=4000): """ Grid a set of lat/lon points to a grid defined by mask @@ -38,7 +38,7 @@ def interp(df, mask, var='biomass', spacing=4000): # extract the projection and grid info region = [mask.x.data[0], mask.x.data[-1], mask.y.data[-1], mask.y.data[0]] - projection = pyproj.Proj(mask.attrs['crs']) + projection = pyproj.Proj(mask.attrs["crs"]) coordinates = (df.lon.values, df.lat.values) @@ -54,8 +54,8 @@ def interp(df, mask, var='biomass', spacing=4000): # fit the gridder chain = vd.Chain( [ - ('mean', vd.BlockReduce(np.mean, spacing=spacing * 0.25, region=region)), - ('nearest', vd.ScipyGridder(method='linear')), + ("mean", vd.BlockReduce(np.mean, spacing=spacing * 0.25, region=region)), + ("nearest", vd.ScipyGridder(method="linear")), ] ) @@ -64,7 +64,7 @@ def interp(df, mask, var='biomass', spacing=4000): # fit_score = score(test[1][0], y_pred) # make the grid - grid = chain.grid(spacing=spacing, region=region, data_names=[var], dims=('y', 'x')) + grid = chain.grid(spacing=spacing, region=region, data_names=[var], dims=("y", "x")) grid = vd.distance_mask( proj_coords, maxdist=4 * spacing, diff --git a/carbonplan_forest_risks/load/cmip.py b/carbonplan_forest_risks/load/cmip.py index d7dfb85..1fe335e 100644 --- a/carbonplan_forest_risks/load/cmip.py +++ b/carbonplan_forest_risks/load/cmip.py @@ -12,74 +12,79 @@ from .. import setup, utils members = { - 'CanESM5-CanOE': 'r3i1p2f1', - 'MIROC-ES2L': 'r1i1p1f2', - 'ACCESS-CM2': 'r1i1p1f1', - 'ACCESS-ESM1-5': 'r10i1p1f1', - 'MRI-ESM2-0': 'r1i1p1f1', - 'MPI-ESM1-2-LR': 'r10i1p1f1', + "CanESM5-CanOE": "r3i1p2f1", + "MIROC-ES2L": "r1i1p1f2", + "ACCESS-CM2": "r1i1p1f1", + "ACCESS-ESM1-5": "r10i1p1f1", + "MRI-ESM2-0": "r1i1p1f1", + "MPI-ESM1-2-LR": "r10i1p1f1", } @retry(stop=stop_after_attempt(5)) def cmip( - store='az', + store="az", df=None, tlim=None, model=None, scenario=None, coarsen=None, - variables=['ppt', 'tmean'], + variables=["ppt", "tmean"], mask=None, member=None, - method='bias-corrected', - sampling='annual', + method="bias-corrected", + sampling="annual", historical=False, remove_nans=False, ): with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=ResourceWarning) - warnings.simplefilter('ignore', category=FutureWarning) - warnings.simplefilter('ignore', category=RuntimeWarning) + warnings.simplefilter("ignore", category=ResourceWarning) + warnings.simplefilter("ignore", category=FutureWarning) + warnings.simplefilter("ignore", category=RuntimeWarning) if scenario is None: - raise ValueError('must specify scenario') + raise ValueError("must specify scenario") if model is None: - raise ValueError('must specify model') + raise ValueError("must specify model") if member is None: member = members[model] - path = setup.loading(store) - prefix = f'cmip6/{method}/conus/4000m/{sampling}/{model}.{scenario}.{member}.zarr' + prefix = ( + f"cmip6/{method}/conus/4000m/{sampling}/{model}.{scenario}.{member}.zarr" + ) - if store == 'az': + if store == "az": mapper = zarr.storage.ABSStore( - 'carbonplan-downscaling', prefix=prefix, account_name='carbonplan' + "carbonplan-downscaling", prefix=prefix, account_name="carbonplan" ) else: - mapper = fsspec.get_mapper((path / 'carbonplan-downscaling' / prefix).as_uri()) + mapper = fsspec.get_mapper( + (path / "carbonplan-downscaling" / prefix).as_uri() + ) ds = xr.open_zarr(mapper, consolidated=True) if historical: - prefix = f'cmip6/{method}/conus/4000m/{sampling}/{model}.historical.{member}.zarr' + prefix = f"cmip6/{method}/conus/4000m/{sampling}/{model}.historical.{member}.zarr" - if store == 'az': + if store == "az": mapper = zarr.storage.ABSStore( - 'carbonplan-downscaling', prefix=prefix, account_name='carbonplan' + "carbonplan-downscaling", prefix=prefix, account_name="carbonplan" ) else: - mapper = fsspec.get_mapper((path / 'carbonplan-downscaling' / prefix).as_uri()) + mapper = fsspec.get_mapper( + (path / "carbonplan-downscaling" / prefix).as_uri() + ) ds_historical = xr.open_zarr(mapper, consolidated=True) - ds = xr.concat([ds_historical, ds], 'time') + ds = xr.concat([ds_historical, ds], "time") - ds['cwd'] = ds['def'] - ds['pdsi'] = ds['pdsi'].clip(-16, 16) + ds["cwd"] = ds["def"] + ds["pdsi"] = ds["pdsi"].clip(-16, 16) X = xr.Dataset() keys = variables @@ -99,29 +104,31 @@ def cmip( if coarsen: X_coarse = xr.Dataset() for key in keys: - X_coarse[key] = X[key].coarsen(x=coarsen, y=coarsen, boundary='trim').mean() + X_coarse[key] = ( + X[key].coarsen(x=coarsen, y=coarsen, boundary="trim").mean() + ) X = X_coarse if df is not None: t = Affine(*utils.albers_conus_transform(4000)) p1 = Proj(utils.albers_conus_crs()) - p2 = Proj(proj='latlong', datum='WGS84') - x, y = transform(p2, p1, df['lon'].values, df['lat'].values) + p2 = Proj(proj="latlong", datum="WGS84") + x, y = transform(p2, p1, df["lon"].values, df["lat"].values) rc = rowcol(t, x, y) - ind_r = xr.DataArray(rc[0], dims=['c']) - ind_c = xr.DataArray(rc[1], dims=['c']) + ind_r = xr.DataArray(rc[0], dims=["c"]) + ind_c = xr.DataArray(rc[1], dims=["c"]) base = X[keys].isel(y=ind_r, x=ind_c).load() for key in keys: - df[key + '_mean'] = base[key].mean('time').values - df[key + '_min'] = base[key].min('time').values - df[key + '_max'] = base[key].max('time').values + df[key + "_mean"] = base[key].mean("time").values + df[key + "_min"] = base[key].min("time").values + df[key + "_max"] = base[key].max("time").values if remove_nans: for key in keys: - df = df[~np.isnan(df[key + '_mean'])] + df = df[~np.isnan(df[key + "_mean"])] df = df.reset_index(drop=True) return df - X = X.drop(['x', 'y']) + X = X.drop(["x", "y"]) X.load(retries=10) return X diff --git a/carbonplan_forest_risks/load/fia.py b/carbonplan_forest_risks/load/fia.py index 4b369a1..6ae986d 100644 --- a/carbonplan_forest_risks/load/fia.py +++ b/carbonplan_forest_risks/load/fia.py @@ -179,59 +179,59 @@ } conus_states = [ - 'AL', - 'AZ', - 'AR', - 'CA', - 'CO', - 'CT', - 'DE', - 'FL', - 'GA', - 'IA', - 'ID', - 'IL', - 'IN', - 'KS', - 'KY', - 'LA', - 'ME', - 'MA', - 'MD', - 'MI', - 'MN', - 'MO', - 'MS', - 'MT', - 'NC', - 'ND', - 'NE', - 'NH', - 'NJ', - 'NM', - 'NV', - 'NY', - 'OH', - 'OK', - 'OR', - 'PA', - 'RI', - 'SC', - 'SD', - 'TN', - 'TX', - 'UT', - 'VT', - 'VA', - 'WA', - 'WV', - 'WI', - 'WY', + "AL", + "AZ", + "AR", + "CA", + "CO", + "CT", + "DE", + "FL", + "GA", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "ME", + "MA", + "MD", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VT", + "VA", + "WA", + "WV", + "WI", + "WY", ] -def fia(store='az', states='conus', clean=True, group_repeats=False): - if states == 'conus': +def fia(store="az", states="conus", clean=True, group_repeats=False): + if states == "conus": states = conus_states load_state = fia_state_grouped if group_repeats is True else fia_state @@ -245,7 +245,7 @@ def fia(store='az', states='conus', clean=True, group_repeats=False): if group_repeats: # TODO this is to drop columns added due to missing data # should maybe handle more cleanly - remove = ['disturb_animal', 'disturb_fire', 'disturb_disease'] + remove = ["disturb_animal", "disturb_fire", "disturb_disease"] for var in remove: if var in df.columns: df = df.drop(columns=[var]) @@ -257,82 +257,82 @@ def fia(store='az', states='conus', clean=True, group_repeats=False): def fia_state(store, state, clean): path = setup.loading(store) df = pd.read_parquet( - path / f'carbonplan-data/processed/fia-states/long/{state.lower()}.parquet' + path / f"carbonplan-data/processed/fia-states/long/{state.lower()}.parquet" ) if clean: inds = ( - (df['adj_ag_biomass'] > 0) - & (df['STDAGE'] < 999) - & (df['STDAGE'] > 0) - & (~np.isnan(df['FLDTYPCD'])) - & (df['FLDTYPCD'] != 999) - & (df['FLDTYPCD'] != 950) - & (df['FLDTYPCD'] <= 983) - & (df['DSTRBCD1'] == 0) - & (df['COND_STATUS_CD'] == 1) - & (df['CONDPROP_UNADJ'] > 0.3) - & (df['MEASYEAR'] < 9999) - & (df['MEASYEAR'] > 2000) - & (df['INVYR'] < 9999) - & (df['INVYR'] > 2000) + (df["adj_ag_biomass"] > 0) + & (df["STDAGE"] < 999) + & (df["STDAGE"] > 0) + & (~np.isnan(df["FLDTYPCD"])) + & (df["FLDTYPCD"] != 999) + & (df["FLDTYPCD"] != 950) + & (df["FLDTYPCD"] <= 983) + & (df["DSTRBCD1"] == 0) + & (df["COND_STATUS_CD"] == 1) + & (df["CONDPROP_UNADJ"] > 0.3) + & (df["MEASYEAR"] < 9999) + & (df["MEASYEAR"] > 2000) + & (df["INVYR"] < 9999) + & (df["INVYR"] > 2000) ) df = df[inds] else: inds = ( - (df['MEASYEAR'] < 9999) - & (df['INVYR'] < 9999) - & (df['FLDTYPCD'] != 999) - & (df['FLDTYPCD'] != 950) - & (df['FLDTYPCD'] <= 983) + (df["MEASYEAR"] < 9999) + & (df["INVYR"] < 9999) + & (df["FLDTYPCD"] != 999) + & (df["FLDTYPCD"] != 950) + & (df["FLDTYPCD"] <= 983) ) df = df[inds] df = df.rename( columns={ - 'LAT': 'lat', - 'LON': 'lon', - 'adj_ag_biomass': 'biomass', - 'adj_pop_mort': 'mort', - 'STDAGE': 'age', - 'ACTUALHT': 'height', - 'MEASYEAR': 'year', - 'INVYR': 'inventory_year', - 'FLDTYPCD': 'type_code', - 'PHYSCLCD': 'physiographic_code', - 'ALSTKCD': 'all_stocking_code', - 'GSSTKCD': 'grow_stocking_code', - 'ELEV': 'elevation', - 'SLOPE': 'slope', - 'ASPECT': 'aspect', - 'OWNCD': 'owner', - 'PLT_CN': 'plot_cn', + "LAT": "lat", + "LON": "lon", + "adj_ag_biomass": "biomass", + "adj_pop_mort": "mort", + "STDAGE": "age", + "ACTUALHT": "height", + "MEASYEAR": "year", + "INVYR": "inventory_year", + "FLDTYPCD": "type_code", + "PHYSCLCD": "physiographic_code", + "ALSTKCD": "all_stocking_code", + "GSSTKCD": "grow_stocking_code", + "ELEV": "elevation", + "SLOPE": "slope", + "ASPECT": "aspect", + "OWNCD": "owner", + "PLT_CN": "plot_cn", } ).filter( [ - 'lat', - 'lon', - 'age', - 'biomass', - 'year', - 'inventory_year', - 'type_code', - 'elevation', - 'slope', - 'aspect', - 'mort', - 'owner', - 'plot_cn', - 'height', - 'physiographic_code', - 'all_stocking_code', - 'grow_stocking_code', + "lat", + "lon", + "age", + "biomass", + "year", + "inventory_year", + "type_code", + "elevation", + "slope", + "aspect", + "mort", + "owner", + "plot_cn", + "height", + "physiographic_code", + "all_stocking_code", + "grow_stocking_code", ] ) - df['type_code'] = df['type_code'].map(forest_type_remap) + df["type_code"] = df["type_code"].map(forest_type_remap) - df['state'] = state.upper() + df["state"] = state.upper() return df.reset_index(drop=True) @@ -345,61 +345,67 @@ def fia_state_grouped(store, state, clean): """ path = setup.loading(store) state_long = pd.read_parquet( - path / f'carbonplan-data/processed/fia-states/long/{state.lower()}.parquet' + path / f"carbonplan-data/processed/fia-states/long/{state.lower()}.parquet" ) state_long = state_long.rename( columns={ - 'LAT': 'lat', - 'LON': 'lon', - 'FLDTYPCD': 'type_code', - 'MEASYEAR': 'year', - 'INVYR': 'inventory_year', - 'STDAGE': 'age', - 'ELEV': 'elevation', - 'SLOPE': 'slope', - 'ACTUALHT': 'height', - 'PHYSCLCD': 'physiographic_code', - 'ALSTKCD': 'all_stocking_code', - 'GSSTKCD': 'grow_stocking_code', - 'ASPECT': 'aspect', - 'OWNCD': 'owner', - 'CONDPROP_UNADJ': 'condprop', - 'adj_pop_mort': 'mort', - 'adj_removal': 'removal', - 'adj_balive': 'balive', + "LAT": "lat", + "LON": "lon", + "FLDTYPCD": "type_code", + "MEASYEAR": "year", + "INVYR": "inventory_year", + "STDAGE": "age", + "ELEV": "elevation", + "SLOPE": "slope", + "ACTUALHT": "height", + "PHYSCLCD": "physiographic_code", + "ALSTKCD": "all_stocking_code", + "GSSTKCD": "grow_stocking_code", + "ASPECT": "aspect", + "OWNCD": "owner", + "CONDPROP_UNADJ": "condprop", + "adj_pop_mort": "mort", + "adj_removal": "removal", + "adj_balive": "balive", } ) - state_long = state_long.sort_values(['plt_uid', 'CONDID', 'year']) - state_long['wide_idx'] = state_long.groupby(['plt_uid', 'CONDID']).cumcount() + state_long = state_long.sort_values(["plt_uid", "CONDID", "year"]) + state_long["wide_idx"] = state_long.groupby(["plt_uid", "CONDID"]).cumcount() tmp = [] - missing_vars = [] # append missing vars, then will fill in nan cols after concat the wide df + missing_vars = ( + [] + ) # append missing vars, then will fill in nan cols after concat the wide df for var in [ - 'year', - 'balive', - 'mort', - 'frac_pop_mort_insect', - 'frac_pop_mort_disease', - 'frac_pop_mort_fire', - 'frac_pop_mort_animal', - 'frac_pop_mort_weather', - 'frac_pop_mort_vegetation', - 'frac_pop_mort_unknown', - 'disturb_animal', - 'disturb_insect', - 'disturb_disease', - 'disturb_fire', - 'disturb_human', - 'disturb_weather', - 'treatment_cutting', - 'treatment_regeneration', - 'treatment_preparation', - 'treatment_other', + "year", + "balive", + "mort", + "frac_pop_mort_insect", + "frac_pop_mort_disease", + "frac_pop_mort_fire", + "frac_pop_mort_animal", + "frac_pop_mort_weather", + "frac_pop_mort_vegetation", + "frac_pop_mort_unknown", + "disturb_animal", + "disturb_insect", + "disturb_disease", + "disturb_fire", + "disturb_human", + "disturb_weather", + "treatment_cutting", + "treatment_regeneration", + "treatment_preparation", + "treatment_other", ]: - state_long['tmp_idx'] = var + '_' + state_long['wide_idx'].astype(str) + state_long["tmp_idx"] = var + "_" + state_long["wide_idx"].astype(str) if var in state_long.columns: - tmp.append(state_long.pivot(index=['plt_uid', 'CONDID'], columns='tmp_idx', values=var)) + tmp.append( + state_long.pivot( + index=["plt_uid", "CONDID"], columns="tmp_idx", values=var + ) + ) else: missing_vars.append(var) wide = pd.concat(tmp, axis=1) @@ -408,48 +414,48 @@ def fia_state_grouped(store, state, clean): for missing_var in missing_vars: wide[missing_var] = np.nan - attrs = state_long.groupby(['plt_uid', 'CONDID'])[ + attrs = state_long.groupby(["plt_uid", "CONDID"])[ [ - 'lat', - 'lon', - 'age', - 'type_code', - 'elevation', - 'slope', - 'aspect', - 'condprop', - 'owner', - 'height', - 'physiographic_code', - 'all_stocking_code', - 'grow_stocking_code', + "lat", + "lon", + "age", + "type_code", + "elevation", + "slope", + "aspect", + "condprop", + "owner", + "height", + "physiographic_code", + "all_stocking_code", + "grow_stocking_code", ] ].max() - if 'year_1' not in wide.columns: + if "year_1" not in wide.columns: return None - df = attrs.join(wide).dropna(subset=['year_1']) + df = attrs.join(wide).dropna(subset=["year_1"]) if clean: inds = ( - (~np.isnan(df['type_code'])) - & (df['type_code'] != 999) - & (df['type_code'] != 950) - & (df['type_code'] <= 983) + (~np.isnan(df["type_code"])) + & (df["type_code"] != 999) + & (df["type_code"] != 950) + & (df["type_code"] <= 983) ) df = df[inds] for year in range(6): - key = f'year_{year}' + key = f"year_{year}" if key in df.columns: if clean: df = df[(df[key] < 9999) | np.isnan((df[key]))] if sum(np.isnan(df[key])) == len(df): del df[key] - df['type_code'] = df['type_code'].map(forest_type_remap) + df["type_code"] = df["type_code"].map(forest_type_remap) - df['state'] = state.upper() + df["state"] = state.upper() return df.reset_index(drop=True) diff --git a/carbonplan_forest_risks/load/mask.py b/carbonplan_forest_risks/load/mask.py index dbbd925..dba7f70 100644 --- a/carbonplan_forest_risks/load/mask.py +++ b/carbonplan_forest_risks/load/mask.py @@ -1,8 +1,8 @@ from .nlcd import nlcd -def mask(store='az', year=2001, coarsen=None): - bands = nlcd(store=store, classes='all', year=year) +def mask(store="az", year=2001, coarsen=None): + bands = nlcd(store=store, classes="all", year=year) if coarsen: - bands = bands.coarsen(x=coarsen, y=coarsen, boundary='trim').mean() - return bands.sum('band') + bands = bands.coarsen(x=coarsen, y=coarsen, boundary="trim").mean() + return bands.sum("band") diff --git a/carbonplan_forest_risks/load/mtbs.py b/carbonplan_forest_risks/load/mtbs.py index fd0a29f..b8213cb 100644 --- a/carbonplan_forest_risks/load/mtbs.py +++ b/carbonplan_forest_risks/load/mtbs.py @@ -7,14 +7,14 @@ from .. import setup -def mtbs(store='az', tlim=(1984, 2018), mask=None, coarsen=None): +def mtbs(store="az", tlim=(1984, 2018), mask=None, coarsen=None): path = setup.loading(store) - prefix = (path / 'carbonplan-data/processed/mtbs/conus/4000m/monthly.zarr').as_uri() + prefix = (path / "carbonplan-data/processed/mtbs/conus/4000m/monthly.zarr").as_uri() mapper = fsspec.get_mapper(prefix) with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=RuntimeWarning) + warnings.simplefilter("ignore", category=RuntimeWarning) mtbs = xr.open_zarr(mapper, consolidated=True) @@ -28,7 +28,7 @@ def mtbs(store='az', tlim=(1984, 2018), mask=None, coarsen=None): mtbs = mtbs * vals if coarsen: - mtbs = mtbs.coarsen(x=coarsen, y=coarsen, boundary='trim').mean() + mtbs = mtbs.coarsen(x=coarsen, y=coarsen, boundary="trim").mean() mtbs.load() return mtbs diff --git a/carbonplan_forest_risks/load/nftd.py b/carbonplan_forest_risks/load/nftd.py index 4fb07a6..387ced3 100644 --- a/carbonplan_forest_risks/load/nftd.py +++ b/carbonplan_forest_risks/load/nftd.py @@ -10,10 +10,17 @@ def load_rio(f): return src.read(1) -def nftd(store='az', groups='all', coarsen=None, append_all=False, mask=None, area_threshold=None): +def nftd( + store="az", + groups="all", + coarsen=None, + append_all=False, + mask=None, + area_threshold=None, +): path = setup.loading(store) - if groups == 'all': + if groups == "all": groups = [ 100, 120, @@ -45,16 +52,20 @@ def nftd(store='az', groups='all', coarsen=None, append_all=False, mask=None, ar bands = xr.concat( [ xr.open_rasterio( - (path / f'carbonplan-data/processed/nftd/conus/4000m/group_g{g}.tif').as_uri() + ( + path / f"carbonplan-data/processed/nftd/conus/4000m/group_g{g}.tif" + ).as_uri() )[0] for g in groups ], - dim=xr.Variable('band', groups), + dim=xr.Variable("band", groups), ) if area_threshold is not None: - band_inds = bands['band'].values - areas = np.asarray([bands.sel(band=band).sum(['x', 'y']).item() for band in band_inds]) + band_inds = bands["band"].values + areas = np.asarray( + [bands.sel(band=band).sum(["x", "y"]).item() for band in band_inds] + ) small_inds = areas < area_threshold large_inds = areas >= area_threshold matches = {} @@ -75,9 +86,9 @@ def nftd(store='az', groups='all', coarsen=None, append_all=False, mask=None, ar bands = bands[large_inds] if append_all: - total = bands.sum('band').values[np.newaxis, :, :] - total = xr.DataArray(total, dims=['band', 'y', 'x'], coords={'band': [0]}) - bands = xr.concat([bands, total], dim='band') + total = bands.sum("band").values[np.newaxis, :, :] + total = xr.DataArray(total, dims=["band", "y", "x"], coords={"band": [0]}) + bands = xr.concat([bands, total], dim="band") if mask is not None: vals = mask.values @@ -85,7 +96,7 @@ def nftd(store='az', groups='all', coarsen=None, append_all=False, mask=None, ar bands = bands * vals if coarsen is not None: - bands = bands.coarsen(x=coarsen, y=coarsen, boundary='trim').mean() + bands = bands.coarsen(x=coarsen, y=coarsen, boundary="trim").mean() bands.load() return bands diff --git a/carbonplan_forest_risks/load/nlcd.py b/carbonplan_forest_risks/load/nlcd.py index af822a0..0e107b0 100644 --- a/carbonplan_forest_risks/load/nlcd.py +++ b/carbonplan_forest_risks/load/nlcd.py @@ -4,20 +4,43 @@ from .. import setup -def nlcd(store='az', classes='all', year=2001, coarsen=None, mask=None): +def nlcd(store="az", classes="all", year=2001, coarsen=None, mask=None): path = setup.loading(store) - if classes == 'all': - classes = [11, 12, 21, 22, 23, 24, 31, 41, 42, 43, 51, 52, 71, 72, 73, 74, 81, 82, 90, 95] + if classes == "all": + classes = [ + 11, + 12, + 21, + 22, + 23, + 24, + 31, + 41, + 42, + 43, + 51, + 52, + 71, + 72, + 73, + 74, + 81, + 82, + 90, + 95, + ] bands = xr.concat( [ xr.open_rasterio( - (path / f'carbonplan-data/processed/nlcd/conus/4000m/{year}_c{c}.tif').as_uri() + ( + path / f"carbonplan-data/processed/nlcd/conus/4000m/{year}_c{c}.tif" + ).as_uri() ) for c in classes ], - dim=xr.Variable('band', classes), + dim=xr.Variable("band", classes), ) if mask is not None: @@ -26,7 +49,7 @@ def nlcd(store='az', classes='all', year=2001, coarsen=None, mask=None): bands = bands * vals if coarsen is not None: - bands = bands.coarsen(x=coarsen, y=coarsen, boundary='trim').mean() + bands = bands.coarsen(x=coarsen, y=coarsen, boundary="trim").mean() bands.load() return bands diff --git a/carbonplan_forest_risks/load/terraclim.py b/carbonplan_forest_risks/load/terraclim.py index e15d557..f975835 100644 --- a/carbonplan_forest_risks/load/terraclim.py +++ b/carbonplan_forest_risks/load/terraclim.py @@ -14,36 +14,38 @@ @retry(stop=stop_after_attempt(5)) def terraclim( - store='az', + store="az", df=None, tlim=None, coarsen=None, - sampling='annual', - variables=['ppt', 'tmean'], + sampling="annual", + variables=["ppt", "tmean"], mask=None, group_repeats=False, remove_nans=False, ): with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=ResourceWarning) - warnings.simplefilter('ignore', category=FutureWarning) - warnings.simplefilter('ignore', category=RuntimeWarning) + warnings.simplefilter("ignore", category=ResourceWarning) + warnings.simplefilter("ignore", category=FutureWarning) + warnings.simplefilter("ignore", category=RuntimeWarning) path = setup.loading(store) - prefix = f'obs/conus/4000m/{sampling}/terraclimate_plus_v3.zarr' + prefix = f"obs/conus/4000m/{sampling}/terraclimate_plus_v3.zarr" - if store == 'az': + if store == "az": mapper = zarr.storage.ABSStore( - 'carbonplan-downscaling', prefix=prefix, account_name='carbonplan' + "carbonplan-downscaling", prefix=prefix, account_name="carbonplan" ) else: - mapper = fsspec.get_mapper((path / 'carbonplan-downscaling' / prefix).as_uri()) + mapper = fsspec.get_mapper( + (path / "carbonplan-downscaling" / prefix).as_uri() + ) ds = xr.open_zarr(mapper, consolidated=True) - ds['cwd'] = ds['pet'] - ds['aet'] - ds['pdsi'] = ds['pdsi'].clip(-16, 16) + ds["cwd"] = ds["pet"] - ds["aet"] + ds["pdsi"] = ds["pdsi"].clip(-16, 16) X = xr.Dataset() @@ -62,69 +64,75 @@ def terraclim( if coarsen: X_coarse = xr.Dataset() for key in variables: - X_coarse[key] = X[key].coarsen(x=coarsen, y=coarsen, boundary='trim').mean() + X_coarse[key] = ( + X[key].coarsen(x=coarsen, y=coarsen, boundary="trim").mean() + ) X = X_coarse if df is not None: t = Affine(*utils.albers_conus_transform(4000)) p1 = Proj(utils.albers_conus_crs()) - p2 = Proj(proj='latlong', datum='WGS84') - x, y = transform(p2, p1, df['lon'].values, df['lat'].values) + p2 = Proj(proj="latlong", datum="WGS84") + x, y = transform(p2, p1, df["lon"].values, df["lat"].values) rc = rowcol(t, x, y) - ind_r = xr.DataArray(rc[0], dims=['c']) - ind_c = xr.DataArray(rc[1], dims=['c']) + ind_r = xr.DataArray(rc[0], dims=["c"]) + ind_c = xr.DataArray(rc[1], dims=["c"]) if not group_repeats: base = X[variables].isel(y=ind_r, x=ind_c).load() for key in variables: - df[key + '_mean'] = base[key].mean('time').values - df[key + '_min'] = base[key].min('time').values - df[key + '_max'] = base[key].max('time').values + df[key + "_mean"] = base[key].mean("time").values + df[key + "_min"] = base[key].min("time").values + df[key + "_max"] = base[key].max("time").values if remove_nans: for key in variables: - df = df[~np.isnan(df[key + '_mean'])] + df = df[~np.isnan(df[key + "_mean"])] df = df.reset_index(drop=True) return df else: base = X[variables].isel(y=ind_r, x=ind_c).load() for key in variables: array = base[key].values.T - time = np.arange(X['time.year'].min(), X['time.year'].max() + 1) + time = np.arange(X["time.year"].min(), X["time.year"].max() + 1) maxyear = max( map( - lambda x: int(x.split('_')[1]), - df.columns[['year' in c for c in df.columns]], + lambda x: int(x.split("_")[1]), + df.columns[["year" in c for c in df.columns]], ) ) pairs = [(str(y), str(y + 1)) for y in range(maxyear)] for pair in pairs: tlims = [ - (time > tmin) & (time <= tmax) - if (~np.isnan(tmin) & ~np.isnan(tmax)) - else [] - for (tmin, tmax) in zip(df[f'year_{pair[0]}'], df[f'year_{pair[1]}']) + ( + (time > tmin) & (time <= tmax) + if (~np.isnan(tmin) & ~np.isnan(tmax)) + else [] + ) + for (tmin, tmax) in zip( + df[f"year_{pair[0]}"], df[f"year_{pair[1]}"] + ) ] def get_stats(a, t): if (len(t) > 0) and (t.sum() > 0): selection = a[t] return { - 'min': selection.min(), - 'max': selection.max(), - 'mean': selection.mean(), + "min": selection.min(), + "max": selection.max(), + "mean": selection.mean(), } else: - return {'min': np.NaN, 'max': np.NaN, 'mean': np.NaN} + return {"min": np.NaN, "max": np.NaN, "mean": np.NaN} stats = [get_stats(a, t) for (a, t) in zip(array, tlims)] - df[key + '_min_' + pair[1]] = [d['min'] for d in stats] - df[key + '_max_' + pair[1]] = [d['max'] for d in stats] - df[key + '_mean_' + pair[1]] = [d['mean'] for d in stats] + df[key + "_min_" + pair[1]] = [d["min"] for d in stats] + df[key + "_max_" + pair[1]] = [d["max"] for d in stats] + df[key + "_mean_" + pair[1]] = [d["mean"] for d in stats] if remove_nans: for key in variables: - df = df[~np.isnan(df[key + '_mean_1'])] + df = df[~np.isnan(df[key + "_mean_1"])] df = df.reset_index(drop=True) return df else: diff --git a/carbonplan_forest_risks/load/tiff.py b/carbonplan_forest_risks/load/tiff.py index 745f8ba..d15e5b1 100644 --- a/carbonplan_forest_risks/load/tiff.py +++ b/carbonplan_forest_risks/load/tiff.py @@ -6,11 +6,17 @@ def tiff(url, model_ds, coarsen=1): - target = nlcd(store='az', year=2001).isel(band=0).drop('band') + target = nlcd(store="az", year=2001).isel(band=0).drop("band") source = xr.open_rasterio(url) source = source.where(source > -1) ds = source.rio.reproject_match(target, resampling=Resampling.bilinear) - ds = ds.where(ds > -1).sel(band=1).drop('band').drop('spatial_ref').astype(np.float64) + ds = ( + ds.where(ds > -1) + .sel(band=1) + .drop("band") + .drop("spatial_ref") + .astype(np.float64) + ) if coarsen != 1: ds = ds.coarsen(x=coarsen, y=coarsen, boundary="trim").mean() # make sure that the coordinates are *exactly* aligned- otherwise you'll have @@ -27,7 +33,7 @@ def impacts( period_start=1970, period_end=2100, coarsen=1, - met_data='cmip', + met_data="cmip", ): start_years = np.arange(period_start, period_end, 10) end_years = np.arange(period_start + 9, period_end + 9, 10) @@ -43,35 +49,39 @@ def impacts( ("MPI-ESM1-2-LR", "r10i1p1f1"), ("CanESM5-CanOE", "r3i1p2f1"), ] - if met_data == 'cmip': + if met_data == "cmip": for scenario in scenarios: impact_ds = xr.Dataset() - for (gcm, ensemble_member) in gcms: + for gcm, ensemble_member in gcms: year_coords, impact_ds_list = [], [] for start_year, end_year in zip(start_years, end_years): if start_year < 2005: url = url_template.format( - gcm, 'historical', ensemble_member, start_year, end_year + gcm, "historical", ensemble_member, start_year, end_year ) else: url = url_template.format( gcm, scenario, ensemble_member, start_year, end_year ) - impact_ds_list.append(tiff(url, spatial_template, coarsen=coarsen).load()) + impact_ds_list.append( + tiff(url, spatial_template, coarsen=coarsen).load() + ) year_coords.append(start_year) - impact_ds[gcm] = xr.concat(impact_ds_list, 'year').assign_coords( - {'year': year_coords} + impact_ds[gcm] = xr.concat(impact_ds_list, "year").assign_coords( + {"year": year_coords} ) full_ds_list.append(impact_ds) - full_ds = xr.concat(full_ds_list, 'scenario').assign_coords({'scenario': scenarios}) + full_ds = xr.concat(full_ds_list, "scenario").assign_coords( + {"scenario": scenarios} + ) - elif met_data == 'terraclimate': + elif met_data == "terraclimate": year_coords, impact_ds_list = [], [] for start_year, end_year in zip(start_years, end_years): url = url_template.format(start_year, end_year) impact_ds_list.append(tiff(url, spatial_template, coarsen=coarsen).load()) year_coords.append(start_year) - full_ds = xr.concat(impact_ds_list, 'year').assign_coords({'year': year_coords}) + full_ds = xr.concat(impact_ds_list, "year").assign_coords({"year": year_coords}) if mask is not None: full_ds = full_ds.where(mask > 0) return full_ds diff --git a/carbonplan_forest_risks/plot/carto.py b/carbonplan_forest_risks/plot/carto.py index 25800a9..b4b366a 100644 --- a/carbonplan_forest_risks/plot/carto.py +++ b/carbonplan_forest_risks/plot/carto.py @@ -9,9 +9,9 @@ def carto( data=None, - lon='lon', - lat='lat', - projection='albersUsa', + lon="lon", + lat="lat", + projection="albersUsa", color=None, cmap=None, clabel=None, @@ -23,17 +23,17 @@ def carto( opacity=1, ): if data is None: - df = pd.DataFrame({'lon': lon, 'lat': lat}) - if color is not None and hasattr(color, 'name') and clabel is None: + df = pd.DataFrame({"lon": lon, "lat": lat}) + if color is not None and hasattr(color, "name") and clabel is None: clabel = color.name if color is not None: - df['color'] = color - _color = 'color' + df["color"] = color + _color = "color" else: df = data _color = color if clabel is None: - clabel = 'color' + clabel = "color" clegend = alt.Legend(title=clabel) @@ -43,24 +43,30 @@ def color_scaled(color): elif clim is None and cmap is not None: return alt.Color(color, legend=clegend, scale=alt.Scale(scheme=cmap)) elif clim is not None and cmap is None: - return alt.Color(color, legend=clegend, scale=alt.Scale(domain=clim, clamp=True)) + return alt.Color( + color, legend=clegend, scale=alt.Scale(domain=clim, clamp=True) + ) elif clim is not None and cmap is not None: if type(cmap) == str: return alt.Color( - color, legend=clegend, scale=alt.Scale(domain=clim, scheme=cmap, clamp=True) + color, + legend=clegend, + scale=alt.Scale(domain=clim, scheme=cmap, clamp=True), ) else: return alt.Color( - color, legend=clegend, scale=alt.Scale(domain=clim, range=cmap, clamp=True) + color, + legend=clegend, + scale=alt.Scale(domain=clim, range=cmap, clamp=True), ) - states = alt.topo_feature(vega_data.us_10m.url, 'states') + states = alt.topo_feature(vega_data.us_10m.url, "states") background = ( alt.Chart(states) .mark_geoshape( - fill='white', - stroke='black', + fill="white", + stroke="black", strokeWidth=0.3, ) .project(projection) @@ -74,8 +80,8 @@ def color_scaled(color): if color is None: geomap = ( alt.Chart(df) - .mark_square(size=size, color='rgb(150,150,150)', opacity=opacity) - .encode(longitude='lon:Q', latitude='lat:Q') + .mark_square(size=size, color="rgb(150,150,150)", opacity=opacity) + .encode(longitude="lon:Q", latitude="lat:Q") .project(type=projection) .properties(width=width, height=height) ) @@ -83,7 +89,7 @@ def color_scaled(color): geomap = ( alt.Chart(df) .mark_square(size=size, opacity=opacity) - .encode(longitude='lon:Q', latitude='lat:Q', color=color_scaled(_color)) + .encode(longitude="lon:Q", latitude="lat:Q", color=color_scaled(_color)) .project(type=projection) .properties(width=width, height=height) ) @@ -91,7 +97,7 @@ def color_scaled(color): return background + geomap -def cartopy_proj_albers(region='conus'): +def cartopy_proj_albers(region="conus"): return ccrs.AlbersEqualArea( central_longitude=-96, central_latitude=23, standard_parallels=(29.5, 45.5) ) @@ -99,21 +105,23 @@ def cartopy_proj_albers(region='conus'): def cartopy_borders(): states_df = gpd.read_file( - shapereader.natural_earth('50m', 'cultural', 'admin_1_states_provinces') + shapereader.natural_earth("50m", "cultural", "admin_1_states_provinces") ) states = ( - states_df.loc[states_df['iso_a2'] == 'US'] + states_df.loc[states_df["iso_a2"] == "US"] .set_crs(epsg=4326) - .to_crs(utils.projections('albers', 'conus')) - .drop([49, 60])['geometry'] + .to_crs(utils.projections("albers", "conus")) + .drop([49, 60])["geometry"] .values ) - countries_df = gpd.read_file(shapereader.natural_earth('50m', 'cultural', 'admin_0_countries')) + countries_df = gpd.read_file( + shapereader.natural_earth("50m", "cultural", "admin_0_countries") + ) countries = ( - countries_df[countries_df['ADMIN'] == 'United States of America'] + countries_df[countries_df["ADMIN"] == "United States of America"] .set_crs(epsg=4326) - .to_crs(utils.projections('albers', 'conus'))['geometry'] + .to_crs(utils.projections("albers", "conus"))["geometry"] .values ) return states, countries diff --git a/carbonplan_forest_risks/plot/fire.py b/carbonplan_forest_risks/plot/fire.py index 009911c..872192b 100644 --- a/carbonplan_forest_risks/plot/fire.py +++ b/carbonplan_forest_risks/plot/fire.py @@ -8,16 +8,23 @@ from . import carto, line -def monthly(data, data_var='monthly', projection='albersUsa', clim=None, cmap='reds', clabel=None): - lat = data['lat'].values.flatten() - lon = data['lon'].values.flatten() +def monthly( + data, + data_var="monthly", + projection="albersUsa", + clim=None, + cmap="reds", + clabel=None, +): + lat = data["lat"].values.flatten() + lon = data["lon"].values.flatten() - shape = data['lat'].shape + shape = data["lat"].shape size = (170 / shape[0]) * (270 / shape[1]) * 0.9 months = [2, 3, 4, 5, 6, 7, 8, 9, 10] - months_labels = ['mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov'] - fires = data[data_var].groupby('time.month').mean().isel(month=months) + months_labels = ["mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov"] + fires = data[data_var].groupby("time.month").mean().isel(month=months) chart = alt.vconcat() counter = 0 @@ -51,18 +58,18 @@ def simple_map( data, data2=None, clabel=None, - projection='albersUsa', + projection="albersUsa", clim=None, - cmap='reds', + cmap="reds", title1=None, title2=None, ): - lat = data['lat'].values.flatten() - lon = data['lon'].values.flatten() + lat = data["lat"].values.flatten() + lon = data["lon"].values.flatten() color = data.values.flatten() inds = color >= clim[0] - shape = data['lat'].shape + shape = data["lat"].shape size = (250 / shape[0]) * (400 / shape[1]) * 0.9 row = alt.hconcat() @@ -102,26 +109,32 @@ def simple_map( return row -def summary(data, data_var='monthly', projection='albersUsa', clim=None, clabel=None, title=None): - lat = data['lat'].values.flatten() - lon = data['lon'].values.flatten() - color = data.groupby('time.year').sum().mean('year').values.flatten() +def summary( + data, data_var="monthly", projection="albersUsa", clim=None, clabel=None, title=None +): + lat = data["lat"].values.flatten() + lon = data["lon"].values.flatten() + color = data.groupby("time.year").sum().mean("year").values.flatten() inds = color > clim[0] - shape = data['lat'].shape + shape = data["lat"].shape size = (300 / shape[0]) * (500 / shape[1]) * 0.9 column = alt.vconcat() - x = data.groupby('time.year').sum()['year'].values - y = data.groupby('time.year').sum().mean(['x', 'y']).values + x = data.groupby("time.year").sum()["year"].values + y = data.groupby("time.year").sum().mean(["x", "y"]).values - column &= line(x=x, y=y, width=300, height=122, strokeWidth=2, color='rgb(175,91,92)') + column &= line( + x=x, y=y, width=300, height=122, strokeWidth=2, color="rgb(175,91,92)" + ) - x = data.groupby('time.month').mean()['month'].values - y = data.groupby('time.month').mean().mean(['x', 'y']).values + x = data.groupby("time.month").mean()["month"].values + y = data.groupby("time.month").mean().mean(["x", "y"]).values - column &= line(x=x, y=y, width=300, height=122, strokeWidth=2, color='rgb(175,91,92)') + column &= line( + x=x, y=y, width=300, height=122, strokeWidth=2, color="rgb(175,91,92)" + ) row = alt.hconcat() @@ -132,7 +145,7 @@ def summary(data, data_var='monthly', projection='albersUsa', clim=None, clabel= lon=lon[inds], color=color[inds], clim=clim, - cmap='reds', + cmap="reds", clabel=clabel, size=size, width=500, @@ -155,31 +168,35 @@ def evaluation( data, model, mask, - projection='albersUsa', + projection="albersUsa", clim=None, - cmap='reds', + cmap="reds", percentage=True, comparison=True, add_map=True, clabel=None, ): - lat = data['lat'].values.flatten() - lon = data['lon'].values.flatten() + lat = data["lat"].values.flatten() + lon = data["lon"].values.flatten() - color_model = model.groupby('time.year').sum().where(mask).mean('year').values.flatten() - color_data = data.groupby('time.year').sum().where(mask).mean('year').values.flatten() + color_model = ( + model.groupby("time.year").sum().where(mask).mean("year").values.flatten() + ) + color_data = ( + data.groupby("time.year").sum().where(mask).mean("year").values.flatten() + ) inds_model = color_model >= clim[0] inds_data = color_data >= clim[0] - shape = data['lat'].shape + shape = data["lat"].shape size = (300 / shape[0]) * (500 / shape[1]) * 0.85 column = alt.vconcat() - x = data.groupby('time.year').sum().where(mask)['year'].values - y = data.groupby('time.year').sum().where(mask).mean(['x', 'y']).values - yhat = model.groupby('time.year').sum().where(mask).mean(['x', 'y']).values + x = data.groupby("time.year").sum().where(mask)["year"].values + y = data.groupby("time.year").sum().where(mask).mean(["x", "y"]).values + yhat = model.groupby("time.year").sum().where(mask).mean(["x", "y"]).values column &= line( x=x, @@ -188,9 +205,9 @@ def evaluation( height=233, strokeWidth=2, opacity=1, - color='darkgrey', - ylabel='Burn area (fraction/year', - xlabel='Time', + color="darkgrey", + ylabel="Burn area (fraction/year", + xlabel="Time", ) + line( x=x, y=yhat, @@ -198,14 +215,14 @@ def evaluation( height=233, strokeWidth=2, opacity=1, - color='#D77B40', - ylabel='Burn area (fraction/year)', - xlabel='Time', + color="#D77B40", + ylabel="Burn area (fraction/year)", + xlabel="Time", ) - x = data.groupby('time.month').mean()['month'].values - y = data.groupby('time.month').mean().mean(['x', 'y']).values - yhat = model.groupby('time.month').mean().mean(['x', 'y']).values + x = data.groupby("time.month").mean()["month"].values + y = data.groupby("time.month").mean().mean(["x", "y"]).values + yhat = model.groupby("time.month").mean().mean(["x", "y"]).values column &= line( x=x, @@ -214,23 +231,23 @@ def evaluation( height=233, strokeWidth=2, opacity=1, - color='darkgrey', - ylabel='Burn area (fraction/month)', - xlabel='Month', + color="darkgrey", + ylabel="Burn area (fraction/month)", + xlabel="Month", ) + line( x=x, y=yhat, width=450, height=233, strokeWidth=2, - color='#D77B40', - ylabel='Burn area (fraction/month)', - xlabel='Month', + color="#D77B40", + ylabel="Burn area (fraction/month)", + xlabel="Month", ) - x = data['time'].values - y = data.mean(['x', 'y']).values - yhat = model.mean(['x', 'y']).values + x = data["time"].values + y = data.mean(["x", "y"]).values + yhat = model.mean(["x", "y"]).values column &= line( x=x, @@ -239,18 +256,18 @@ def evaluation( height=233, strokeWidth=2, opacity=1, - color='darkgrey', - ylabel='Burn area (fraction/month)', - xlabel='Time', + color="darkgrey", + ylabel="Burn area (fraction/month)", + xlabel="Time", ) + line( x=x, y=yhat, width=450, height=233, strokeWidth=2, - color='#D77B40', - ylabel='Burn area (fraction/month)', - xlabel='Time', + color="#D77B40", + ylabel="Burn area (fraction/month)", + xlabel="Time", ) chart = column @@ -291,11 +308,11 @@ def evaluation( def full_eval( data, model, - data_var='vlf', - model_var='prediction', - projection='albersUsa', + data_var="vlf", + model_var="prediction", + projection="albersUsa", clim=None, - cmap='reds', + cmap="reds", percentage=True, comparison=True, clabel=None, @@ -307,21 +324,21 @@ def full_eval( spatial_corr = np.corrcoef(a[inds], b[inds])[0, 1] ** 2 eval_metrics = { - 'seasonal': np.corrcoef( + "seasonal": np.corrcoef( data[data_var].groupby("time.month").mean().mean(["x", "y"]), model[model_var].groupby("time.month").mean().mean(["x", "y"]), )[0, 1] ** 2, - 'annual': np.corrcoef( + "annual": np.corrcoef( data[data_var].groupby("time.year").mean().mean(["x", "y"]), model[model_var].groupby("time.year").mean().mean(["x", "y"]), )[0, 1] ** 2, - 'spatial': spatial_corr, + "spatial": spatial_corr, } for metric, performance in eval_metrics.items(): - print('performance at {} scale is: {}'.format(metric, performance)) + print("performance at {} scale is: {}".format(metric, performance)) return eval_metrics, evaluation( data, @@ -341,7 +358,7 @@ def integrated_risk(p): return (1 - binom.cdf(0, 100, p)) * 100 -def supersection(data, varname, store='az'): +def supersection(data, varname, store="az"): from palettable.colorbrewer.sequential import YlOrRd_9 cmap = YlOrRd_9.mpl_colormap @@ -350,14 +367,14 @@ def supersection(data, varname, store='az'): ) masks = rm.mask_3D_geopandas(regions, data) - groupby = data.groupby('time.year').sum().mean('year') + groupby = data.groupby("time.year").sum().mean("year") risks = np.asarray( [ - groupby[varname].where(masks.sel(region=i)).mean(['x', 'y']).values.item() - for i in masks['region'] + groupby[varname].where(masks.sel(region=i)).mean(["x", "y"]).values.item() + for i in masks["region"] ] ) - regions.to_crs('EPSG:5070').plot( + regions.to_crs("EPSG:5070").plot( integrated_risk(risks), figsize=[15, 8], cmap=cmap, @@ -367,42 +384,50 @@ def supersection(data, varname, store='az'): vmax=25, legend=True, ) - plt.axis('off') + plt.axis("off") def calc_decadal_averages(simulation): decadal_averages = ( - simulation.sel(time=slice('2020', '2099')).coarsen(time=120).sum().load() / 10 + simulation.sel(time=slice("2020", "2099")).coarsen(time=120).sum().load() / 10 ) return decadal_averages def future_ts(decadal_averages, historical=None, domain=(0.0, 0.05)): - df = decadal_averages.mean(dim=['x', 'y']).to_dataframe() + df = decadal_averages.mean(dim=["x", "y"]).to_dataframe() - df['time'] = df.index - df_toplot = df.melt('time', var_name='gcm_scenario', value_name='probability') + df["time"] = df.index + df_toplot = df.melt("time", var_name="gcm_scenario", value_name="probability") if historical is not None: - historical['time'] = historical.index + historical["time"] = historical.index # the historical_historical is a hack to make the gcm/scenario splitting below not fail - historical_df = historical.rename(columns={'historical': 'historical_historical'}) + historical_df = historical.rename( + columns={"historical": "historical_historical"} + ) df_toplot = df_toplot.append( - historical_df.melt('time', var_name='gcm_scenario', value_name='probability'), + historical_df.melt( + "time", var_name="gcm_scenario", value_name="probability" + ), ignore_index=True, ) - df_toplot['gcm'] = df_toplot.apply(lambda row: row.gcm_scenario.split('_')[0], axis=1) - df_toplot['scenario'] = df_toplot.apply(lambda row: row.gcm_scenario.split('_')[1], axis=1) - scenarios = ['historical', 'ssp245', 'ssp370', 'ssp585'] - colors_ = ['black', '#7eb36a', '#ea9755', '#f07071'] + df_toplot["gcm"] = df_toplot.apply( + lambda row: row.gcm_scenario.split("_")[0], axis=1 + ) + df_toplot["scenario"] = df_toplot.apply( + lambda row: row.gcm_scenario.split("_")[1], axis=1 + ) + scenarios = ["historical", "ssp245", "ssp370", "ssp585"] + colors_ = ["black", "#7eb36a", "#ea9755", "#f07071"] base = alt.Chart(df_toplot).properties(width=550) line = base.mark_line().encode( - alt.Y('probability:Q', scale=alt.Scale(domain=domain)), - x='time', - color=alt.Color('scenario', scale=alt.Scale(domain=scenarios, range=colors_)), - strokeDash='gcm', + alt.Y("probability:Q", scale=alt.Scale(domain=domain)), + x="time", + color=alt.Color("scenario", scale=alt.Scale(domain=scenarios, range=colors_)), + strokeDash="gcm", ) return line diff --git a/carbonplan_forest_risks/plot/line.py b/carbonplan_forest_risks/plot/line.py index 816f782..43edaa5 100644 --- a/carbonplan_forest_risks/plot/line.py +++ b/carbonplan_forest_risks/plot/line.py @@ -22,20 +22,20 @@ def line( plot two variables optionally colored by some feature """ if data is None: - if hasattr(x, 'name') and xlabel is None: + if hasattr(x, "name") and xlabel is None: xlabel = x.name - if hasattr(y, 'name') and ylabel is None: + if hasattr(y, "name") and ylabel is None: ylabel = y.name - df = pd.DataFrame({'x': x, 'y': y}) - _x = 'x' - _y = 'y' + df = pd.DataFrame({"x": x, "y": y}) + _x = "x" + _y = "y" if color is not None: - df['color'] = color - _color = 'color' + df["color"] = color + _color = "color" else: - if hasattr(data[x], 'name') and xlabel is None: + if hasattr(data[x], "name") and xlabel is None: xlabel = data[x].name - if hasattr(data[y], 'name') and ylabel is None: + if hasattr(data[y], "name") and ylabel is None: ylabel = data[y].name df = data _x = x @@ -43,10 +43,10 @@ def line( _color = color if xlabel is None: - xlabel = 'x' + xlabel = "x" if ylabel is None: - ylabel = 'y' + ylabel = "y" xaxis = alt.Axis(title=xlabel) yaxis = alt.Axis(title=ylabel) @@ -65,16 +65,18 @@ def y_scaled(y): def color_scaled(color): if clim is None and cmap is None: - return alt.Color(color, scale=alt.Scale(scheme='viridis')) + return alt.Color(color, scale=alt.Scale(scheme="viridis")) elif clim is None and cmap is not None: return alt.Color(color, scale=alt.Scale(scheme=cmap)) elif clim is not None and cmap is None: return alt.Color(color, scale=alt.Scale(domain=clim)) elif clim is not None and cmap is not None: - return alt.Color(color, scale=alt.Scale(domain=clim, scheme=cmap, clamp=True)) + return alt.Color( + color, scale=alt.Scale(domain=clim, scheme=cmap, clamp=True) + ) if color is None: - color = '#EA9755' + color = "#EA9755" if type(color) is str: line = ( diff --git a/carbonplan_forest_risks/plot/paper.py b/carbonplan_forest_risks/plot/paper.py index e372430..ccb1eeb 100644 --- a/carbonplan_forest_risks/plot/paper.py +++ b/carbonplan_forest_risks/plot/paper.py @@ -6,25 +6,25 @@ from . import cartopy_borders, cartopy_proj_albers -def map_pretty(ax, title=''): +def map_pretty(ax, title=""): state_borders, us_border = cartopy_borders() ax.add_geometries( state_borders, - facecolor='none', - edgecolor='k', + facecolor="none", + edgecolor="k", crs=cartopy_proj_albers(), linewidth=0.3, zorder=0, ) ax.add_geometries( us_border, - facecolor='none', - edgecolor='k', + facecolor="none", + edgecolor="k", crs=cartopy_proj_albers(), linewidth=0.3, zorder=0, ) - ax.axis('off') + ax.axis("off") ax.set_extent([-125, -70, 20, 50]) ax.text(0.77, 0.96, title, transform=ax.transAxes) @@ -39,8 +39,8 @@ def add_colorbar( width=0.018, vmin=None, vmax=None, - cbar_label='', - cmap='viridis', + cbar_label="", + cmap="viridis", ): cax = fig.add_axes([x_location, y_location, width, height]) cax.text( @@ -48,32 +48,32 @@ def add_colorbar( -0.08, vmin, transform=cax.transAxes, - horizontalalignment='center', - verticalalignment='center', + horizontalalignment="center", + verticalalignment="center", ) cax.text( 0.5, 1.08, vmax, transform=cax.transAxes, - horizontalalignment='center', - verticalalignment='center', + horizontalalignment="center", + verticalalignment="center", ) cax.text( 1.8, 0.5, cbar_label, transform=cax.transAxes, - verticalalignment='center', - multialignment='center', + verticalalignment="center", + multialignment="center", rotation=-90, ) if to_plot is not None: - cbar = fig.colorbar(to_plot, cax=cax, orientation='vertical') + cbar = fig.colorbar(to_plot, cax=cax, orientation="vertical") else: norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax) cbar = fig.colorbar( - mpl.cm.ScalarMappable(norm=norm, cmap=cmap), cax=cax, orientation='vertical' + mpl.cm.ScalarMappable(norm=norm, cmap=cmap), cax=cax, orientation="vertical" ) cbar.outline.set_visible(False) cbar.set_ticks([]) @@ -119,10 +119,12 @@ def multipanel_ts(results_dict, region_bboxes, fig_path): ax.add_patch( mpatches.Rectangle( - xy=[region_bboxes[region]['x'].start, region_bboxes[region]['y'].start], - width=region_bboxes[region]['x'].stop - region_bboxes[region]['x'].start, - height=region_bboxes[region]['y'].stop - region_bboxes[region]['y'].start, - facecolor='grey', + xy=[region_bboxes[region]["x"].start, region_bboxes[region]["y"].start], + width=region_bboxes[region]["x"].stop + - region_bboxes[region]["x"].start, + height=region_bboxes[region]["y"].stop + - region_bboxes[region]["y"].start, + facecolor="grey", alpha=0.5, ) ) @@ -135,9 +137,11 @@ def multipanel_ts(results_dict, region_bboxes, fig_path): results_dict[impact][region]["historical"].plot(ax=ax, color="k", zorder=60) for scenario in ["ssp245", "ssp370", "ssp585"]: impact_axes.append(ax) - plot_future_ts_traces(ax, results_dict[impact][region]["future"], scenario, gcms) + plot_future_ts_traces( + ax, results_dict[impact][region]["future"], scenario, gcms + ) ts_pretty(ax, impact, ylims[impact]) - if impact != 'insects': + if impact != "insects": ax.set_xticks([]) if len(impact_axes) > 3: ax.set_yticks([]) @@ -163,8 +167,10 @@ def plot_future_ts_traces(ax, ds, scenario, gcms): ssp_rename = {"ssp245": "SSP2-4.5", "ssp370": "SSP3-7.0", "ssp585": "SSP5-8.5"} - for (gcm, location) in gcms: - ds.sel(gcm=gcm, scenario=scenario).plot(ax=ax, color=scenario_colors_light[scenario]) + for gcm, location in gcms: + ds.sel(gcm=gcm, scenario=scenario).plot( + ax=ax, color=scenario_colors_light[scenario] + ) ds.sel(scenario=scenario).mean(dim="gcm").plot( ax=ax, color=scenario_colors[scenario], label=ssp_rename[scenario], zorder=30 diff --git a/carbonplan_forest_risks/plot/xy.py b/carbonplan_forest_risks/plot/xy.py index 7dcf2ed..5cf61d4 100644 --- a/carbonplan_forest_risks/plot/xy.py +++ b/carbonplan_forest_risks/plot/xy.py @@ -22,24 +22,24 @@ def xy( plot two variables optionally colored by some feature """ if data is None: - if hasattr(x, 'name') and xlabel is None: + if hasattr(x, "name") and xlabel is None: xlabel = x.name - if hasattr(y, 'name') and ylabel is None: + if hasattr(y, "name") and ylabel is None: ylabel = y.name - if color is not None and hasattr(color, 'name') and clabel is None: + if color is not None and hasattr(color, "name") and clabel is None: clabel = color.name - df = pd.DataFrame({'x': x, 'y': y}) - _x = 'x' - _y = 'y' + df = pd.DataFrame({"x": x, "y": y}) + _x = "x" + _y = "y" if color is not None: - df['color'] = color - _color = 'color' + df["color"] = color + _color = "color" else: - if hasattr(data[x], 'name') and xlabel is None: + if hasattr(data[x], "name") and xlabel is None: xlabel = data[x].name - if hasattr(data[y], 'name') and ylabel is None: + if hasattr(data[y], "name") and ylabel is None: ylabel = data[y].name - if color is not None and hasattr(data[color], 'name') and clabel is None: + if color is not None and hasattr(data[color], "name") and clabel is None: clabel = color.name df = data _x = x @@ -47,13 +47,13 @@ def xy( _color = color if xlabel is None: - xlabel = 'x' + xlabel = "x" if ylabel is None: - ylabel = 'y' + ylabel = "y" if clabel is None: - clabel = 'color' + clabel = "color" xaxis = alt.Axis(title=xlabel) yaxis = alt.Axis(title=ylabel) @@ -73,20 +73,22 @@ def y_scaled(y): def color_scaled(color): if clim is None and cmap is None: - return alt.Color(color, legend=clegend, scale=alt.Scale(scheme='viridis')) + return alt.Color(color, legend=clegend, scale=alt.Scale(scheme="viridis")) elif clim is None and cmap is not None: return alt.Color(color, legend=clegend, scale=alt.Scale(scheme=cmap)) elif clim is not None and cmap is None: return alt.Color(color, legend=clegend, scale=alt.Scale(domain=clim)) elif clim is not None and cmap is not None: return alt.Color( - color, legend=clegend, scale=alt.Scale(domain=clim, scheme=cmap, clamp=True) + color, + legend=clegend, + scale=alt.Scale(domain=clim, scheme=cmap, clamp=True), ) if color is None: points = ( alt.Chart(df) - .mark_circle(size=42, color='black', fill='black', opacity=opacity) + .mark_circle(size=42, color="black", fill="black", opacity=opacity) .encode( x=x_scaled(_x), y=y_scaled(_y), diff --git a/carbonplan_forest_risks/prepare.py b/carbonplan_forest_risks/prepare.py index dbee1c0..c4173f4 100644 --- a/carbonplan_forest_risks/prepare.py +++ b/carbonplan_forest_risks/prepare.py @@ -10,7 +10,7 @@ def annualize( signal, climate_prepend=None, rolling_period=None, - analysis_tlim=slice('1984', '2018'), + analysis_tlim=slice("1984", "2018"), ): """ function to aggregate your full spatial @@ -22,24 +22,24 @@ def annualize( else: da = ds[variable] - if signal == 'global': - da = da.mean(dim=['x', 'y']) + if signal == "global": + da = da.mean(dim=["x", "y"]) if climate_prepend is not None: aggregated = da.rolling( - dim={'time': rolling_period}, min_periods=rolling_period, center=False + dim={"time": rolling_period}, min_periods=rolling_period, center=False ) else: - aggregated = da.groupby('time.year') + aggregated = da.groupby("time.year") - if variable == 'tmean': + if variable == "tmean": aggregated = aggregated.max() - elif variable == 'ppt': + elif variable == "ppt": aggregated = aggregated.max() - elif variable == 'cwd': + elif variable == "cwd": aggregated = aggregated.max() else: - print('{} not implemented'.format(variable)) + print("{} not implemented".format(variable)) # drop your first year if you were rolling if climate_prepend is not None: @@ -47,19 +47,19 @@ def annualize( return aggregated -def package_annualized(da, shape, signal='global', climate_prepend=None): +def package_annualized(da, shape, signal="global", climate_prepend=None): """ to get them into the right shapes for the model we need to do some ugly array reshaping """ - if signal == 'global': + if signal == "global": if climate_prepend is not None: tile_shape = [shape[1], shape[2]] else: tile_shape = [12, shape[1], shape[2]] arr = np.asarray([np.tile(a, tile_shape) for a in da]).flatten() - if signal == 'local': + if signal == "local": if climate_prepend is not None: arr = da.values.flatten() else: @@ -78,7 +78,7 @@ def fire( add_global_climate_trends=False, add_local_climate_trends=False, climate_prepend=None, - analysis_tlim=('1970', '2099'), + analysis_tlim=("1970", "2099"), ): """ Prepare x and y and group variables for fire model fitting @@ -91,7 +91,7 @@ def fire( # after you've done your climate packaging you can tack on the earlier year for aggregations with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=RuntimeWarning) + warnings.simplefilter("ignore", category=RuntimeWarning) if add_global_climate_trends is not None: arr_list = [] for var, attrs in add_global_climate_trends.items(): @@ -100,14 +100,14 @@ def fire( annualize( climate, var, - 'global', - climate_prepend=attrs['climate_prepend'], - rolling_period=attrs['rolling_period'], + "global", + climate_prepend=attrs["climate_prepend"], + rolling_period=attrs["rolling_period"], analysis_tlim=analysis_tlim, ), shape, - 'global', - climate_prepend=attrs['climate_prepend'], + "global", + climate_prepend=attrs["climate_prepend"], ) ) f2 = np.asarray(arr_list).T @@ -119,14 +119,14 @@ def fire( annualize( climate, var, - 'local', - climate_prepend=attrs['climate_prepend'], - rolling_period=attrs['rolling_period'], + "local", + climate_prepend=attrs["climate_prepend"], + rolling_period=attrs["rolling_period"], analysis_tlim=analysis_tlim, ), shape, - 'local', - climate_prepend=attrs['climate_prepend'], + "local", + climate_prepend=attrs["climate_prepend"], ) ) @@ -141,7 +141,7 @@ def fire( return x else: - y = mtbs['monthly'].values.flatten() + y = mtbs["monthly"].values.flatten() return x, y @@ -153,34 +153,40 @@ def drought(df, eval_only=False, duration=10): df = df.copy() if eval_only: - fit_vars = ['ppt_sum_min', 'tavg_mean_max', 'age', 'age_squared', 'duration'] - df['age_squared'] = df['age'] ** 2 - df['duration'] = duration + fit_vars = ["ppt_sum_min", "tavg_mean_max", "age", "age_squared", "duration"] + df["age_squared"] = df["age"] ** 2 + df["duration"] = duration x = df[fit_vars] x = x.values - meta = df[['lat', 'lon', 'type_code']].reset_index(drop=True) + meta = df[["lat", "lon", "type_code"]].reset_index(drop=True) return x, meta else: - fit_vars = ['ppt_sum_min_1', 'tavg_mean_max_1', 'age', 'age_squared', 'duration'] + fit_vars = [ + "ppt_sum_min_1", + "tavg_mean_max_1", + "age", + "age_squared", + "duration", + ] # 'pdsi_mean_min_1','cwd_sum_max_1', # 'pet_mean_max_1', 'vpd_mean_max_1', inds = ( - (df['condprop'] > 0.3) - & (not (df['disturb_human_1'] is True)) - & (not (df['disturb_fire_1'] is True)) - & (not (df['treatment_cutting_1'] is True)) + (df["condprop"] > 0.3) + & (df["disturb_human_1"] is not True) + & (df["disturb_fire_1"] is not True) + & (df["treatment_cutting_1"] is not True) ) df = df[inds].copy() - df['age_squared'] = df['age'] ** 2 - df['duration'] = df['year_1'] - df['year_0'] - y = df['mort_1'] / df['balive_0'] + df["age_squared"] = df["age"] ** 2 + df["duration"] = df["year_1"] - df["year_0"] + y = df["mort_1"] / df["balive_0"] x = df[fit_vars] inds = (np.isnan(x).sum(axis=1) == 0) & (~np.isnan(y)) & (y < 1) - meta = df[inds][['lat', 'lon', 'type_code']].reset_index(drop=True) + meta = df[inds][["lat", "lon", "type_code"]].reset_index(drop=True) x = x[inds].values y = y[inds].values @@ -196,40 +202,40 @@ def insects(df, eval_only=False, duration=10): df = df.copy() if eval_only: - fit_vars = ['ppt_sum_min', 'tavg_mean_max', 'age', 'age_squared', 'duration'] - df['age_squared'] = df['age'] ** 2 - df['duration'] = duration + fit_vars = ["ppt_sum_min", "tavg_mean_max", "age", "age_squared", "duration"] + df["age_squared"] = df["age"] ** 2 + df["duration"] = duration x = df[fit_vars] x = x.values - meta = df[['lat', 'lon', 'type_code']].reset_index(drop=True) + meta = df[["lat", "lon", "type_code"]].reset_index(drop=True) return x, meta else: fit_vars = [ - 'ppt_sum_min_1', - 'tavg_mean_max_1', - 'age', - 'age_squared', - 'duration', + "ppt_sum_min_1", + "tavg_mean_max_1", + "age", + "age_squared", + "duration", ] inds = ( - (df['condprop'] > 0.3) - & (not (df['disturb_human_1'] is True)) - & (not (df['disturb_fire_1'] is True)) - & (not (df['treatment_cutting_1'] is True)) + (df["condprop"] > 0.3) + & (df["disturb_human_1"] is not True) + & (df["disturb_fire_1"] is not True) + & (df["treatment_cutting_1"] is not True) ) df = df[inds].copy() - df['age_squared'] = df['age'] ** 2 - df['duration'] = df['year_1'] - df['year_0'] - y = df['fraction_insect_1'] * (df['mort_1'] / df['balive_0']) + df["age_squared"] = df["age"] ** 2 + df["duration"] = df["year_1"] - df["year_0"] + y = df["fraction_insect_1"] * (df["mort_1"] / df["balive_0"]) x = df[fit_vars] inds = (np.isnan(x).sum(axis=1) == 0) & (~np.isnan(y)) & (y < 1) - meta = df[inds][['lat', 'lon', 'type_code']].reset_index(drop=True) + meta = df[inds][["lat", "lon", "type_code"]].reset_index(drop=True) x = x[inds].values y = y[inds].values diff --git a/carbonplan_forest_risks/preprocess/fia.py b/carbonplan_forest_risks/preprocess/fia.py index 22eaad3..347682e 100644 --- a/carbonplan_forest_risks/preprocess/fia.py +++ b/carbonplan_forest_risks/preprocess/fia.py @@ -8,22 +8,22 @@ def fia(states, save=True): if type(states) is str: - if states == 'all': - df = pd.read_csv('gs://carbonplan-data/raw/fia/REF_RESEARCH_STATION.csv') - return [preprocess_state(state, save=save) for state in df['STATE_ABBR']] + if states == "all": + df = pd.read_csv("gs://carbonplan-data/raw/fia/REF_RESEARCH_STATION.csv") + return [preprocess_state(state, save=save) for state in df["STATE_ABBR"]] else: return preprocess_state(states, save=save) else: return [preprocess_state(state, save=save) for state in states] -def generate_uids(data, prev_cn_var='PREV_PLT_CN'): +def generate_uids(data, prev_cn_var="PREV_PLT_CN"): """ Generate dict mapping ever CN to a unique group, allows tracking single plot/tree through time Can change `prev_cn_var` to apply to tree (etc) """ g = nx.Graph() - for _, row in data[['CN', prev_cn_var]].iterrows(): + for _, row in data[["CN", prev_cn_var]].iterrows(): if ~np.isnan(row[prev_cn_var]): # has ancestor, add nodes + edge g.add_edge(row.CN, row[prev_cn_var]) else: @@ -44,21 +44,23 @@ def get_mort_removal_df(tree_df): mort_df = mort_df.reset_index(drop=True) # 10 is minimum code - 0s are legacy and we assume anything < 10 is legacy as well. - mort_df = mort_df[(mort_df['AGENTCD'] >= 10) & (mort_df['AGENTCD'] < 90)] + mort_df = mort_df[(mort_df["AGENTCD"] >= 10) & (mort_df["AGENTCD"] < 90)] # fill in missing TPA_UNADJ with TPAGROW_UNADJ - this is totally off list but JS approved - mort_df.loc[np.isnan(mort_df['TPA_UNADJ']), 'TPA_UNADJ'] = mort_df['TPAGROW_UNADJ'] - mort_df['unadj_basal_area'] = math.pi * (mort_df['DIA'] / (2 * 12)) ** 2 * mort_df['TPA_UNADJ'] + mort_df.loc[np.isnan(mort_df["TPA_UNADJ"]), "TPA_UNADJ"] = mort_df["TPAGROW_UNADJ"] + mort_df["unadj_basal_area"] = ( + math.pi * (mort_df["DIA"] / (2 * 12)) ** 2 * mort_df["TPA_UNADJ"] + ) # drop trees where TPA_UNADJ == 0 -- we have no way of using these data # This is rare, but I interpret this to mean that tree cannot be reliably scaled to acre-1 measurement -- for whatever reason - mort_df = mort_df[mort_df['TPA_UNADJ'] > 0] + mort_df = mort_df[mort_df["TPA_UNADJ"] > 0] # smaller-ish trees with agent code somtimes lack a DIA, but have a DIACALC -- back-fill. JS approved! - mort_df.loc[np.isnan(mort_df['DIA']), 'unadj_basal_area'] = ( + mort_df.loc[np.isnan(mort_df["DIA"]), "unadj_basal_area"] = ( math.pi - * (mort_df[np.isnan(mort_df['DIA'])]['DIACALC'] / (2 * 12)) ** 2 - * mort_df[np.isnan(mort_df['DIA'])]['TPA_UNADJ'] + * (mort_df[np.isnan(mort_df["DIA"])]["DIACALC"] / (2 * 12)) ** 2 + * mort_df[np.isnan(mort_df["DIA"])]["TPA_UNADJ"] ) # TODO: There is another pot of trees we can access if we impute old diameters @@ -69,81 +71,96 @@ def get_mort_removal_df(tree_df): # These would allow recovery a handful of conditions, primarily in region 8 (i think) # do not return records without unadj_basal_area -- see above TODO. - return mort_df[mort_df['unadj_basal_area'] > 0] + return mort_df[mort_df["unadj_basal_area"] > 0] def preprocess_state(state_abbr, save=True): state_abbr = state_abbr.lower() tree_df = pd.read_parquet( - f'gs://carbonplan-data/raw/fia-states/tree_{state_abbr}.parquet', + f"gs://carbonplan-data/raw/fia-states/tree_{state_abbr}.parquet", columns=[ - 'CN', - 'PLT_CN', - 'DIA', - 'DIACALC', - 'HT', - 'ACTUALHT', - 'STATUSCD', - 'CONDID', - 'TPA_UNADJ', - 'AGENTCD', - 'TPAGROW_UNADJ', - 'TPAMORT_UNADJ', - 'TPAREMV_UNADJ', - 'DIACHECK', - 'CARBON_AG', - 'CARBON_BG', + "CN", + "PLT_CN", + "DIA", + "DIACALC", + "HT", + "ACTUALHT", + "STATUSCD", + "CONDID", + "TPA_UNADJ", + "AGENTCD", + "TPAGROW_UNADJ", + "TPAMORT_UNADJ", + "TPAREMV_UNADJ", + "DIACHECK", + "CARBON_AG", + "CARBON_BG", ], ) # calculate tree-level statistics that will sum later. - tree_df['unadj_basal_area'] = math.pi * (tree_df['DIA'] / (2 * 12)) ** 2 * tree_df['TPA_UNADJ'] + tree_df["unadj_basal_area"] = ( + math.pi * (tree_df["DIA"] / (2 * 12)) ** 2 * tree_df["TPA_UNADJ"] + ) # 892.179 converts lbs/acre to t/ha - tree_df['unadj_ag_biomass'] = ( - tree_df['CARBON_AG'] * tree_df['TPA_UNADJ'] * 2 / 892.1791216197013 + tree_df["unadj_ag_biomass"] = ( + tree_df["CARBON_AG"] * tree_df["TPA_UNADJ"] * 2 / 892.1791216197013 ) - tree_df['unadj_bg_biomass'] = ( - tree_df['CARBON_BG'] * tree_df['TPA_UNADJ'] * 2 / 892.1791216197013 + tree_df["unadj_bg_biomass"] = ( + tree_df["CARBON_BG"] * tree_df["TPA_UNADJ"] * 2 / 892.1791216197013 ) - plot_df = pd.read_parquet(f'gs://carbonplan-data/raw/fia-states/plot_{state_abbr}.parquet') - cond_df = pd.read_parquet(f'gs://carbonplan-data/raw/fia-states/cond_{state_abbr}.parquet') + plot_df = pd.read_parquet( + f"gs://carbonplan-data/raw/fia-states/plot_{state_abbr}.parquet" + ) + cond_df = pd.read_parquet( + f"gs://carbonplan-data/raw/fia-states/cond_{state_abbr}.parquet" + ) cond_vars = [ - 'STDAGE', - 'BALIVE', - 'SICOND', - 'SISP', - 'OWNCD', - 'SITECLCD', - 'PHYSCLCD', - 'ALSTK', - 'ALSTKCD', - 'GSSTK', - 'GSSTKCD', - 'FORTYPCD', - 'FLDTYPCD', - 'DSTRBCD1', - 'DSTRBCD2', - 'DSTRBCD3', - 'TRTCD1', - 'TRTCD2', - 'TRTCD3', - 'CONDPROP_UNADJ', - 'COND_STATUS_CD', - 'COND_NONSAMPLE_REASN_CD', - 'SLOPE', - 'ASPECT', - 'INVYR', - 'CN', + "STDAGE", + "BALIVE", + "SICOND", + "SISP", + "OWNCD", + "SITECLCD", + "PHYSCLCD", + "ALSTK", + "ALSTKCD", + "GSSTK", + "GSSTKCD", + "FORTYPCD", + "FLDTYPCD", + "DSTRBCD1", + "DSTRBCD2", + "DSTRBCD3", + "TRTCD1", + "TRTCD2", + "TRTCD3", + "CONDPROP_UNADJ", + "COND_STATUS_CD", + "COND_NONSAMPLE_REASN_CD", + "SLOPE", + "ASPECT", + "INVYR", + "CN", ] - cond_agg = cond_df.groupby(['PLT_CN', 'CONDID'])[cond_vars].max() + cond_agg = cond_df.groupby(["PLT_CN", "CONDID"])[cond_vars].max() cond_agg = cond_agg.join( - plot_df[plot_df['PLOT_STATUS_CD'] != 2].set_index('CN')[ - ['LAT', 'LON', 'ELEV', 'KINDCD', 'MEASYEAR', 'REMPER', 'RDDISTCD', 'ECOSUBCD'] + plot_df[plot_df["PLOT_STATUS_CD"] != 2].set_index("CN")[ + [ + "LAT", + "LON", + "ELEV", + "KINDCD", + "MEASYEAR", + "REMPER", + "RDDISTCD", + "ECOSUBCD", + ] ], - on='PLT_CN', + on="PLT_CN", ) def dstrbcd_to_disturb_class(dstrbcd): @@ -151,29 +168,32 @@ def dstrbcd_to_disturb_class(dstrbcd): Transforms dstrbcd (int 0-90) to bulk disturbance class (bugs, fires, weather, etc) """ disturb_class_map = { - 10: 'insect', - 12: 'insect', - 20: 'insect', - 22: 'insect', - 30: 'fire', - 32: 'fire', - 50: 'weather', - 51: 'weather', - 52: 'weather', - 53: 'weather', - 54: 'drought', - 80: 'human', + 10: "insect", + 12: "insect", + 20: "insect", + 22: "insect", + 30: "fire", + 32: "fire", + 50: "weather", + 51: "weather", + 52: "weather", + 53: "weather", + 54: "drought", + 80: "human", } return (dstrbcd).map(disturb_class_map) dstrb_hot_encodings = [ - pd.get_dummies(dstrbcd_to_disturb_class(cond_agg[k]), prefix='disturb') - for k in ['DSTRBCD1', 'DSTRBCD2', 'DSTRBCD3'] + pd.get_dummies(dstrbcd_to_disturb_class(cond_agg[k]), prefix="disturb") + for k in ["DSTRBCD1", "DSTRBCD2", "DSTRBCD3"] ] # sum all disturb codes, then cast to bool so we know if 0/1 disturbance type occurred # https://stackoverflow.com/questions/13078751/combine-duplicated-columns-within-a-dataframe disturb_flags = ( - (pd.concat(dstrb_hot_encodings, axis=1)).groupby(level=0, axis=1).sum().astype(bool) + (pd.concat(dstrb_hot_encodings, axis=1)) + .groupby(level=0, axis=1) + .sum() + .astype(bool) ) def trtcd_to_treatment_class(trtcd): @@ -181,51 +201,58 @@ def trtcd_to_treatment_class(trtcd): Transforms trtcd (int 00-30) to bulk treatment class (bugs, fires, weather, etc) """ treatment_class_map = { - 10: 'cutting', - 20: 'preparation', - 30: 'regeneration', - 40: 'regeneration', - 50: 'other', + 10: "cutting", + 20: "preparation", + 30: "regeneration", + 40: "regeneration", + 50: "other", } return (trtcd).map(treatment_class_map) trt_hot_encodings = [ - pd.get_dummies(trtcd_to_treatment_class(cond_agg[k]), prefix='treatment') - for k in ['TRTCD1', 'TRTCD2', 'TRTCD3'] + pd.get_dummies(trtcd_to_treatment_class(cond_agg[k]), prefix="treatment") + for k in ["TRTCD1", "TRTCD2", "TRTCD3"] ] # sum all treatment codes, then cast to bool so we know if 0/1 treatment type occurred # https://stackoverflow.com/questions/13078751/combine-duplicated-columns-within-a-dataframe treatment_flags = ( - (pd.concat(trt_hot_encodings, axis=1)).groupby(level=0, axis=1).sum().astype(bool) + (pd.concat(trt_hot_encodings, axis=1)) + .groupby(level=0, axis=1) + .sum() + .astype(bool) ) # per-tree variables that need to sum per condition - alive_sum_vars = ['unadj_ag_biomass', 'unadj_bg_biomass', 'unadj_basal_area'] + alive_sum_vars = ["unadj_ag_biomass", "unadj_bg_biomass", "unadj_basal_area"] condition_alive_sums = ( - tree_df.loc[tree_df['STATUSCD'] == 1].groupby(['PLT_CN', 'CONDID'])[alive_sum_vars].sum() + tree_df.loc[tree_df["STATUSCD"] == 1] + .groupby(["PLT_CN", "CONDID"])[alive_sum_vars] + .sum() ) - alive_mean_vars = ['HT', 'ACTUALHT'] + alive_mean_vars = ["HT", "ACTUALHT"] condition_alive_means = ( - tree_df.loc[tree_df['STATUSCD'] == 1].groupby(['PLT_CN', 'CONDID'])[alive_mean_vars].mean() + tree_df.loc[tree_df["STATUSCD"] == 1] + .groupby(["PLT_CN", "CONDID"])[alive_mean_vars] + .mean() ) mort_removal_trees = get_mort_removal_df(tree_df) # define queries -- we then subset mort_removal_trees and aggregate separately to prevent zeros from sneaking in mort_removal_queries = { - 'unadj_full_mort': (mort_removal_trees['AGENTCD'] < 80), - 'unadj_pop_mort': (mort_removal_trees['TPAMORT_UNADJ'] > 0) - & (mort_removal_trees['AGENTCD'] >= 10) - & (mort_removal_trees['AGENTCD'] < 80), - 'unadj_removal': (mort_removal_trees['AGENTCD'] == 80) + "unadj_full_mort": (mort_removal_trees["AGENTCD"] < 80), + "unadj_pop_mort": (mort_removal_trees["TPAMORT_UNADJ"] > 0) + & (mort_removal_trees["AGENTCD"] >= 10) + & (mort_removal_trees["AGENTCD"] < 80), + "unadj_removal": (mort_removal_trees["AGENTCD"] == 80), # & (mort_removal_trees['TPAREMV_UNADJ'] > 0), } condition_mort_removal = pd.concat( [ mort_removal_trees[idx] - .groupby(['PLT_CN', 'CONDID'])['unadj_basal_area'] + .groupby(["PLT_CN", "CONDID"])["unadj_basal_area"] .sum() .rename(k) for k, idx in mort_removal_queries.items() @@ -234,23 +261,23 @@ def trtcd_to_treatment_class(trtcd): ) # rerun aggregation with AGENTCD to get fraction mortality on pop estimates - pop_mort_trees = mort_removal_trees[mort_removal_queries['unadj_pop_mort']] + pop_mort_trees = mort_removal_trees[mort_removal_queries["unadj_pop_mort"]] pop_mort_by_agent = ( - pop_mort_trees.groupby(['PLT_CN', 'CONDID', pop_mort_trees['AGENTCD'] // 10])[ - 'unadj_basal_area' + pop_mort_trees.groupby(["PLT_CN", "CONDID", pop_mort_trees["AGENTCD"] // 10])[ + "unadj_basal_area" ] .sum() .unstack(2) # agents to col ) BULK_AGENT_MAP = { - 1: 'frac_pop_mort_insect', - 2: 'frac_pop_mort_disease', - 3: 'frac_pop_mort_fire', - 4: 'frac_pop_mort_animal', - 5: 'frac_pop_mort_weather', - 6: 'frac_pop_mort_vegetation', - 7: 'frac_pop_mort_unknown', + 1: "frac_pop_mort_insect", + 2: "frac_pop_mort_disease", + 3: "frac_pop_mort_fire", + 4: "frac_pop_mort_animal", + 5: "frac_pop_mort_weather", + 6: "frac_pop_mort_vegetation", + 7: "frac_pop_mort_unknown", } # convert to fraction @@ -271,25 +298,25 @@ def trtcd_to_treatment_class(trtcd): full = full.join(fraction_pop_mort) full = full.reset_index() - full.loc[:, 'adj_full_mort'] = full.unadj_full_mort / full.CONDPROP_UNADJ - full.loc[:, 'adj_pop_mort'] = full.unadj_pop_mort / full.CONDPROP_UNADJ + full.loc[:, "adj_full_mort"] = full.unadj_full_mort / full.CONDPROP_UNADJ + full.loc[:, "adj_pop_mort"] = full.unadj_pop_mort / full.CONDPROP_UNADJ - full.loc[:, 'adj_removal'] = full.unadj_removal / full.CONDPROP_UNADJ - full.loc[:, 'adj_balive'] = full.unadj_basal_area / full.CONDPROP_UNADJ - full.loc[:, 'adj_bg_biomass'] = full.unadj_bg_biomass / full.CONDPROP_UNADJ - full.loc[:, 'adj_ag_biomass'] = full.unadj_ag_biomass / full.CONDPROP_UNADJ + full.loc[:, "adj_removal"] = full.unadj_removal / full.CONDPROP_UNADJ + full.loc[:, "adj_balive"] = full.unadj_basal_area / full.CONDPROP_UNADJ + full.loc[:, "adj_bg_biomass"] = full.unadj_bg_biomass / full.CONDPROP_UNADJ + full.loc[:, "adj_ag_biomass"] = full.unadj_ag_biomass / full.CONDPROP_UNADJ - full['adj_sapling_mort'] = ( - full['adj_full_mort'] - full['adj_pop_mort'] + full["adj_sapling_mort"] = ( + full["adj_full_mort"] - full["adj_pop_mort"] ) # diff out mort due to saplings plt_uids = generate_uids(plot_df) - full['plt_uid'] = full['PLT_CN'].map(plt_uids) + full["plt_uid"] = full["PLT_CN"].map(plt_uids) if save: full.to_parquet( - f'gs://carbonplan-data/processed/fia-states/long/{state_abbr}.parquet', - compression='gzip', - engine='fastparquet', + f"gs://carbonplan-data/processed/fia-states/long/{state_abbr}.parquet", + compression="gzip", + engine="fastparquet", ) return full diff --git a/carbonplan_forest_risks/setup/loading.py b/carbonplan_forest_risks/setup/loading.py index aadbf2b..1128242 100644 --- a/carbonplan_forest_risks/setup/loading.py +++ b/carbonplan_forest_risks/setup/loading.py @@ -5,12 +5,12 @@ def loading(store=None): if store is None: - raise ValueError('data store not specified') - if store == 'gs': - base = urlpath.URL('gs://') - elif store == 'az': - base = urlpath.URL('https://carbonplan.blob.core.windows.net') - elif store == 'local': - base = pathlib.Path(pathlib.Path.home() / 'workdir') + raise ValueError("data store not specified") + if store == "gs": + base = urlpath.URL("gs://") + elif store == "az": + base = urlpath.URL("https://carbonplan.blob.core.windows.net") + elif store == "local": + base = pathlib.Path(pathlib.Path.home() / "workdir") return base diff --git a/carbonplan_forest_risks/setup/plotting.py b/carbonplan_forest_risks/setup/plotting.py index 1fe85da..1d3868c 100644 --- a/carbonplan_forest_risks/setup/plotting.py +++ b/carbonplan_forest_risks/setup/plotting.py @@ -2,9 +2,9 @@ def plotting(remote=False): - alt.renderers.enable('default', embed_options={'actions': False}) + alt.renderers.enable("default", embed_options={"actions": False}) if remote: - alt.data_transformers.enable('data_server_proxied', urlpath='/user-redirect') + alt.data_transformers.enable("data_server_proxied", urlpath="/user-redirect") else: - alt.data_transformers.enable('data_server') + alt.data_transformers.enable("data_server") alt.data_transformers.disable_max_rows() diff --git a/carbonplan_forest_risks/utils.py b/carbonplan_forest_risks/utils.py index 42d687d..7c0eb7c 100644 --- a/carbonplan_forest_risks/utils.py +++ b/carbonplan_forest_risks/utils.py @@ -69,7 +69,7 @@ def rowcol_to_latlon(row, col, res=250): col = np.asarray(col) if type(col) is list else col x, y = xy(Affine(*albers_conus_transform(res)), row, col) p1 = Proj(CRS.from_wkt(albers_conus_crs())) - p2 = Proj(proj='latlong', datum='WGS84') + p2 = Proj(proj="latlong", datum="WGS84") lon, lat = transform(p1, p2, x, y) return lat, lon @@ -84,7 +84,7 @@ def latlon_to_rowcol(lat, lon, res=250): def latlon_to_xy(lat, lon, base_crs=albers_conus_crs()): p1 = Proj(base_crs) - p2 = Proj(proj='latlong', datum='WGS84') + p2 = Proj(proj="latlong", datum="WGS84") x, y = transform(p2, p1, np.asarray(lon), np.asarray(lat)) return x, y @@ -124,14 +124,14 @@ def remove_nans(x, y=None, return_inds=False): def weighted_mean(ds, *args, **kwargs): weights = ds.time.dt.days_in_month - return ds.weighted(weights).mean(dim='time') + return ds.weighted(weights).mean(dim="time") def get_store(bucket, prefix, account_key=None): - ''' helper function to create a zarr store''' + """helper function to create a zarr store""" if account_key is None: - account_key = os.environ.get('BLOB_ACCOUNT_KEY', None) + account_key = os.environ.get("BLOB_ACCOUNT_KEY", None) store = zarr.storage.ABSStore( bucket, diff --git a/notebooks/biomass/biomass_model.ipynb b/notebooks/biomass/biomass_model.ipynb index 04d5e61..42d62c9 100644 --- a/notebooks/biomass/biomass_model.ipynb +++ b/notebooks/biomass/biomass_model.ipynb @@ -1,258 +1,258 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Biomass Model\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", - "\n", - "This notebook show examples of fitting predictive biomass growth curves from FIA\n", - "data\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Biomass Model\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", + "\n", + "This notebook show examples of fitting predictive biomass growth curves from FIA\n", + "data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forest_risks import load, setup, plot, fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we load the data. To speed things up, we'll just load data from\n", + "California. We load the raw FIA data, as well as two climatic variables `tavg`\n", + "and `ppt` from the terraclim dataset.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df = load.fia(store=\"az\", states=\"CA\")\n", + "df = load.terraclim(\n", + " store=\"az\",\n", + " tlim=(2000, 2020),\n", + " variables=[\"tmean\", \"ppt\"],\n", + " df=df,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll now pick a single forest type and plot biomass vs age colored by our\n", + "climatic variables\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "inds = df[\"type_code\"] == 221\n", + "x = df[inds][\"age\"]\n", + "y = df[inds][\"biomass\"]\n", + "f = [df[inds][\"tmean_mean\"], df[inds][\"ppt_mean\"]]\n", + "(\n", + " plot.xy(x=x, y=y, color=f[0], cmap=\"magma\", xlim=[0, 250], ylim=[0, 600])\n", + " | plot.xy(\n", + " x=x, y=y, color=f[1], cmap=\"viridis\", xlim=[0, 250], ylim=[0, 600]\n", + " )\n", + ").resolve_scale(color=\"independent\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To fit the model to these data we use the `fit.biomass` method\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = fit.biomass(x=x, y=y, f=f, noise=\"gamma\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can evaluate `r2` on the training data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.r2(x, f, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we can plot the fitted curves. When plotting, we show curves for different\n", + "levels of the climatic variables, to show the form of dependency.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "xlim = [0, 250]\n", + "ylim = [0, 700]\n", + "(\n", + " (\n", + " plot.xy(x=x, y=y, color=f[0], cmap=\"magma\", xlim=xlim, ylim=ylim)\n", + " + plot.line(\n", + " x=x,\n", + " y=model.predict(x, f, [90, 50]),\n", + " color=np.nanpercentile(f[0], 90),\n", + " )\n", + " + plot.line(\n", + " x=x,\n", + " y=model.predict(x, f, [10, 50]),\n", + " color=np.nanpercentile(f[0], 10),\n", + " )\n", + " )\n", + " | (\n", + " plot.xy(x=x, y=y, color=f[1], cmap=\"viridis\", xlim=xlim, ylim=ylim)\n", + " + plot.line(\n", + " x=x,\n", + " y=model.predict(x, f, [50, 10]),\n", + " color=np.nanpercentile(f[1], 10),\n", + " )\n", + " + plot.line(\n", + " x=x,\n", + " y=model.predict(x, f, [50, 90]),\n", + " color=np.nanpercentile(f[1], 90),\n", + " )\n", + " )\n", + ").resolve_scale(color=\"independent\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an inspection of model validity, we can plot the raw data and a sample from\n", + "the generative process underlying the model using the fitted parameters\n", + "(specifically, the fitted growth curve, and the Gamma noise model). It should\n", + "look qualatatively similar to the actual data. In particular, note how the noise\n", + "grows with age, and there are no negative values.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "xlim = [0, 250]\n", + "ylim = [-200, 700]\n", + "(\n", + " (\n", + " plot.xy(x=x, y=y, xlim=xlim, ylim=ylim)\n", + " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", + " )\n", + " | (\n", + " plot.xy(x=x, y=model.sample(x, f), xlim=xlim, ylim=ylim)\n", + " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can set the noise to `'normal'` instead of `'gamma'` and see that the sampled\n", + "data no longer matches the real data. While the fitted curve is similar, the\n", + "variability is too high for low ages, and there are negative predictions where\n", + "there shouldn't be! These behaviors help justify the choice of Gamma\n", + "distribution.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = fit.biomass(x=x, y=y, f=f, noise=\"normal\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "xlim = [0, 250]\n", + "ylim = [-200, 700]\n", + "(\n", + " (\n", + " plot.xy(x=x, y=y, xlim=xlim, ylim=ylim)\n", + " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", + " )\n", + " | (\n", + " plot.xy(x=x, y=model.sample(x, f), xlim=xlim, ylim=ylim)\n", + " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", + " )\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forest_risks import load, setup, plot, fit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we load the data. To speed things up, we'll just load data from\n", - "California. We load the raw FIA data, as well as two climatic variables `tavg`\n", - "and `ppt` from the terraclim dataset.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df = load.fia(store=\"az\", states=\"CA\")\n", - "df = load.terraclim(\n", - " store=\"az\",\n", - " tlim=(2000, 2020),\n", - " variables=[\"tmean\", \"ppt\"],\n", - " df=df,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll now pick a single forest type and plot biomass vs age colored by our\n", - "climatic variables\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "inds = df[\"type_code\"] == 221\n", - "x = df[inds][\"age\"]\n", - "y = df[inds][\"biomass\"]\n", - "f = [df[inds][\"tmean_mean\"], df[inds][\"ppt_mean\"]]\n", - "(\n", - " plot.xy(x=x, y=y, color=f[0], cmap=\"magma\", xlim=[0, 250], ylim=[0, 600])\n", - " | plot.xy(\n", - " x=x, y=y, color=f[1], cmap=\"viridis\", xlim=[0, 250], ylim=[0, 600]\n", - " )\n", - ").resolve_scale(color=\"independent\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To fit the model to these data we use the `fit.biomass` method\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = fit.biomass(x=x, y=y, f=f, noise=\"gamma\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can evaluate `r2` on the training data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model.r2(x, f, y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can plot the fitted curves. When plotting, we show curves for different\n", - "levels of the climatic variables, to show the form of dependency.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "xlim = [0, 250]\n", - "ylim = [0, 700]\n", - "(\n", - " (\n", - " plot.xy(x=x, y=y, color=f[0], cmap=\"magma\", xlim=xlim, ylim=ylim)\n", - " + plot.line(\n", - " x=x,\n", - " y=model.predict(x, f, [90, 50]),\n", - " color=np.nanpercentile(f[0], 90),\n", - " )\n", - " + plot.line(\n", - " x=x,\n", - " y=model.predict(x, f, [10, 50]),\n", - " color=np.nanpercentile(f[0], 10),\n", - " )\n", - " )\n", - " | (\n", - " plot.xy(x=x, y=y, color=f[1], cmap=\"viridis\", xlim=xlim, ylim=ylim)\n", - " + plot.line(\n", - " x=x,\n", - " y=model.predict(x, f, [50, 10]),\n", - " color=np.nanpercentile(f[1], 10),\n", - " )\n", - " + plot.line(\n", - " x=x,\n", - " y=model.predict(x, f, [50, 90]),\n", - " color=np.nanpercentile(f[1], 90),\n", - " )\n", - " )\n", - ").resolve_scale(color=\"independent\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As an inspection of model validity, we can plot the raw data and a sample from\n", - "the generative process underlying the model using the fitted parameters\n", - "(specifically, the fitted growth curve, and the Gamma noise model). It should\n", - "look qualatatively similar to the actual data. In particular, note how the noise\n", - "grows with age, and there are no negative values.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "xlim = [0, 250]\n", - "ylim = [-200, 700]\n", - "(\n", - " (\n", - " plot.xy(x=x, y=y, xlim=xlim, ylim=ylim)\n", - " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", - " )\n", - " | (\n", - " plot.xy(x=x, y=model.sample(x, f), xlim=xlim, ylim=ylim)\n", - " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can set the noise to `'normal'` instead of `'gamma'` and see that the sampled\n", - "data no longer matches the real data. While the fitted curve is similar, the\n", - "variability is too high for low ages, and there are negative predictions where\n", - "there shouldn't be! These behaviors help justify the choice of Gamma\n", - "distribution.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = fit.biomass(x=x, y=y, f=f, noise=\"normal\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "xlim = [0, 250]\n", - "ylim = [-200, 700]\n", - "(\n", - " (\n", - " plot.xy(x=x, y=y, xlim=xlim, ylim=ylim)\n", - " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", - " )\n", - " | (\n", - " plot.xy(x=x, y=model.sample(x, f), xlim=xlim, ylim=ylim)\n", - " + plot.line(x=x, y=model.predict(x, f, [50, 50]))\n", - " )\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/biomass/biomass_results.ipynb b/notebooks/biomass/biomass_results.ipynb index 3eba209..ee97605 100644 --- a/notebooks/biomass/biomass_results.ipynb +++ b/notebooks/biomass/biomass_results.ipynb @@ -1,207 +1,207 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Biomass Results\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), October 25, 2020_\n", - "\n", - "This notebook extracts and summarizes results from fitting biomass growth\n", - "curves. See the notebook `biomass_modeling` for how the model is fit. This\n", - "notebook assumes the model has been run and results have been saved using the\n", - "script `biomass.py`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pickle\n", - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forest_risks import setup, plot, load" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we load our model results. This assumes the model has been run and results\n", - "have been saved using the script `biomass.py`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf = pd.read_parquet(\"~/forest-risks/data/biomass.parquet\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Filter out bad values\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "badinds = np.isnan(pf[\"historical\"])\n", - "for key in pf.columns:\n", - " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", - " badinds = badinds | np.isnan(pf[key])\n", - "pf = pf[~badinds]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the results from the model, evaluting on the same climate as used\n", - "for fitting\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"historical\"],\n", - " cmap=\"yellowgreen\",\n", - " clim=(0, 250),\n", - " size=1,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also use other columns to look at future scenarios. For example, here's\n", - "the difference in biomass from 2020 to 2100.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"ACCESS-ESM1-5_ssp245_2095\"] - pf[\"ACCESS-ESM1-5_ssp245_2025\"],\n", - " cmap=\"yellowgreen\",\n", - " clim=(0, 100),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a time series of spatially averaged projections\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "years = np.arange(2015, 2105, 10)\n", - "y1 = [pf[\"CanESM5-CanOE_ssp245_\" + str(year)].mean() for year in years]\n", - "y2 = [pf[\"CanESM5-CanOE_ssp370_\" + str(year)].mean() for year in years]\n", - "y3 = [pf[\"CanESM5-CanOE_ssp585_\" + str(year)].mean() for year in years]\n", - "opts = {\"color\": \"rgb(75,128,97)\", \"ylabel\": \"biomass\", \"xlabel\": \"year\"}\n", - "(\n", - " plot.line(x=years, y=y1, opacity=0.5, ylim=[100, 175], **opts)\n", - " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", - " + plot.line(x=years, y=y3, opacity=1, **opts)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from showit import image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import xarray as xr" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ds = xr.open_zarr(\"~/forest-risks/data/biomass.zarr\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "image(ds[\"ssp245\"][0], clim=[0, 200], size=15)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Biomass Results\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), October 25, 2020_\n", + "\n", + "This notebook extracts and summarizes results from fitting biomass growth\n", + "curves. See the notebook `biomass_modeling` for how the model is fit. This\n", + "notebook assumes the model has been run and results have been saved using the\n", + "script `biomass.py`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forest_risks import setup, plot, load" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we load our model results. This assumes the model has been run and results\n", + "have been saved using the script `biomass.py`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf = pd.read_parquet(\"~/forest-risks/data/biomass.parquet\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Filter out bad values\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "badinds = np.isnan(pf[\"historical\"])\n", + "for key in pf.columns:\n", + " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", + " badinds = badinds | np.isnan(pf[key])\n", + "pf = pf[~badinds]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the results from the model, evaluting on the same climate as used\n", + "for fitting\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"historical\"],\n", + " cmap=\"yellowgreen\",\n", + " clim=(0, 250),\n", + " size=1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also use other columns to look at future scenarios. For example, here's\n", + "the difference in biomass from 2020 to 2100.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"ACCESS-ESM1-5_ssp245_2095\"] - pf[\"ACCESS-ESM1-5_ssp245_2025\"],\n", + " cmap=\"yellowgreen\",\n", + " clim=(0, 100),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a time series of spatially averaged projections\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "years = np.arange(2015, 2105, 10)\n", + "y1 = [pf[\"CanESM5-CanOE_ssp245_\" + str(year)].mean() for year in years]\n", + "y2 = [pf[\"CanESM5-CanOE_ssp370_\" + str(year)].mean() for year in years]\n", + "y3 = [pf[\"CanESM5-CanOE_ssp585_\" + str(year)].mean() for year in years]\n", + "opts = {\"color\": \"rgb(75,128,97)\", \"ylabel\": \"biomass\", \"xlabel\": \"year\"}\n", + "(\n", + " plot.line(x=years, y=y1, opacity=0.5, ylim=[100, 175], **opts)\n", + " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", + " + plot.line(x=years, y=y3, opacity=1, **opts)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from showit import image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import xarray as xr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds = xr.open_zarr(\"~/forest-risks/data/biomass.zarr\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "image(ds[\"ssp245\"][0], clim=[0, 200], size=15)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/drought/drought_model.ipynb b/notebooks/drought/drought_model.ipynb index bea2739..3b08c6b 100644 --- a/notebooks/drought/drought_model.ipynb +++ b/notebooks/drought/drought_model.ipynb @@ -1,235 +1,235 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Drought Model\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", - "\n", - "This notebook demos drought modelling.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forests import setup, plot, load, fit, utils, prepare" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the raw FIA data grouped by repeated inventories\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df = load.fia(store=\"local\", states=\"conus\", group_repeats=True)\n", - "df = load.terraclim(\n", - " store=\"local\",\n", - " tlim=(int(df[\"year_0\"].min()), 2020),\n", - " data_vars=[\"ppt\", \"tavg\", \"pdsi\", \"pet\"],\n", - " data_aggs=[\"sum\", \"mean\", \"mean\", \"mean\"],\n", - " df=df,\n", - " group_repeats=True,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Prepare data for model fitting\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x, y, meta = prepare.drought(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_z, x_mean, x_std = utils.zscore_2d(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Fit models\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf = meta.copy()\n", - "pf[\"observed\"] = y\n", - "pf[\"predicted\"] = np.NaN\n", - "\n", - "models = {}\n", - "for code in pf[\"type_code\"].unique():\n", - " inds = pf[\"type_code\"] == code\n", - " model = fit.hurdle(x=x_z[inds], y=y[inds])\n", - " models[code] = model\n", - " pf.loc[inds, \"predicted\"] = model.predict(x=x_z[inds])\n", - "\n", - "pf = pf[pf[\"predicted\"] < 1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute simple score metrics (on all data and just on the values where mortality\n", - "was positive)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf[[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf[pf[\"observed\"] > 0][[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot observed vs predicted\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.xy(\n", - " x=pf[\"observed\"],\n", - " y=pf[\"predicted\"],\n", - " xlabel=\"measured mort ratio\",\n", - " ylabel=\"predicted mort ratio\",\n", - " xlim=(0, 1),\n", - " ylim=(0, 1),\n", - " width=300,\n", - " height=300,\n", - " opacity=0.1,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of actual mortality ratios\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"observed\"],\n", - " cmap=\"blues\",\n", - " clim=(0, 0.1),\n", - " size=2,\n", - " opacity=0.75,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of predicted mortality ratios\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"predicted\"],\n", - " clabel=\"mortality\",\n", - " cmap=\"blues\",\n", - " clim=(0, 0.1),\n", - " size=2,\n", - " opacity=0.75,\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Drought Model\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", + "\n", + "This notebook demos drought modelling.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forests import setup, plot, load, fit, utils, prepare" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the raw FIA data grouped by repeated inventories\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df = load.fia(store=\"local\", states=\"conus\", group_repeats=True)\n", + "df = load.terraclim(\n", + " store=\"local\",\n", + " tlim=(int(df[\"year_0\"].min()), 2020),\n", + " data_vars=[\"ppt\", \"tavg\", \"pdsi\", \"pet\"],\n", + " data_aggs=[\"sum\", \"mean\", \"mean\", \"mean\"],\n", + " df=df,\n", + " group_repeats=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Prepare data for model fitting\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x, y, meta = prepare.drought(df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_z, x_mean, x_std = utils.zscore_2d(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fit models\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf = meta.copy()\n", + "pf[\"observed\"] = y\n", + "pf[\"predicted\"] = np.NaN\n", + "\n", + "models = {}\n", + "for code in pf[\"type_code\"].unique():\n", + " inds = pf[\"type_code\"] == code\n", + " model = fit.hurdle(x=x_z[inds], y=y[inds])\n", + " models[code] = model\n", + " pf.loc[inds, \"predicted\"] = model.predict(x=x_z[inds])\n", + "\n", + "pf = pf[pf[\"predicted\"] < 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute simple score metrics (on all data and just on the values where mortality\n", + "was positive)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf[[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf[pf[\"observed\"] > 0][[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot observed vs predicted\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.xy(\n", + " x=pf[\"observed\"],\n", + " y=pf[\"predicted\"],\n", + " xlabel=\"measured mort ratio\",\n", + " ylabel=\"predicted mort ratio\",\n", + " xlim=(0, 1),\n", + " ylim=(0, 1),\n", + " width=300,\n", + " height=300,\n", + " opacity=0.1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of actual mortality ratios\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"observed\"],\n", + " cmap=\"blues\",\n", + " clim=(0, 0.1),\n", + " size=2,\n", + " opacity=0.75,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of predicted mortality ratios\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"predicted\"],\n", + " clabel=\"mortality\",\n", + " cmap=\"blues\",\n", + " clim=(0, 0.1),\n", + " size=2,\n", + " opacity=0.75,\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/drought/drought_results.ipynb b/notebooks/drought/drought_results.ipynb index 27eba43..8ee6cfb 100644 --- a/notebooks/drought/drought_results.ipynb +++ b/notebooks/drought/drought_results.ipynb @@ -1,148 +1,148 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Drought Results\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", - "\n", - "This notebook loads drought results.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Drought Results\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", + "\n", + "This notebook loads drought results.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forests import setup, plot, load, fit, utils, prepare" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the results\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf = pd.read_parquet(\n", + " \"/Users/freeman/github/carbonplan/forests/scripts/data/drought.parquet\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Filter out bad values\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "badinds = (pf[\"historical\"] > 1) | (np.isnan(pf[\"historical\"]))\n", + "for key in pf.columns:\n", + " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", + " badinds = badinds | ((pf[key] > 1) | (np.isnan(pf[key])))\n", + "pf = pf[~badinds]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of the historical prediction (from the period used for fitting)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"historical\"],\n", + " cmap=\"blues\",\n", + " clim=(0, 0.3),\n", + " size=2,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a time series of spatially averaged projections\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "years = np.arange(2020, 2120, 20)\n", + "y1 = [pf[\"BCC-CSM2-MR_ssp245_\" + str(year)].mean() for year in years]\n", + "y2 = [pf[\"BCC-CSM2-MR_ssp370_\" + str(year)].mean() for year in years]\n", + "y3 = [pf[\"BCC-CSM2-MR_ssp585_\" + str(year)].mean() for year in years]\n", + "opts = {\n", + " \"color\": \"rgb(70,112,164)\",\n", + " \"ylabel\": \"fractional mortality\",\n", + " \"xlabel\": \"year\",\n", + "}\n", + "(\n", + " plot.line(x=years, y=y1, opacity=0.5, ylim=[0.06, 0.07], **opts)\n", + " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", + " + plot.line(x=years, y=y3, opacity=1, **opts)\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forests import setup, plot, load, fit, utils, prepare" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the results\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf = pd.read_parquet(\n", - " \"/Users/freeman/github/carbonplan/forests/scripts/data/drought.parquet\"\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Filter out bad values\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "badinds = (pf[\"historical\"] > 1) | (np.isnan(pf[\"historical\"]))\n", - "for key in pf.columns:\n", - " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", - " badinds = badinds | ((pf[key] > 1) | (np.isnan(pf[key])))\n", - "pf = pf[~badinds]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of the historical prediction (from the period used for fitting)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"historical\"],\n", - " cmap=\"blues\",\n", - " clim=(0, 0.3),\n", - " size=2,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a time series of spatially averaged projections\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "years = np.arange(2020, 2120, 20)\n", - "y1 = [pf[\"BCC-CSM2-MR_ssp245_\" + str(year)].mean() for year in years]\n", - "y2 = [pf[\"BCC-CSM2-MR_ssp370_\" + str(year)].mean() for year in years]\n", - "y3 = [pf[\"BCC-CSM2-MR_ssp585_\" + str(year)].mean() for year in years]\n", - "opts = {\n", - " \"color\": \"rgb(70,112,164)\",\n", - " \"ylabel\": \"fractional mortality\",\n", - " \"xlabel\": \"year\",\n", - "}\n", - "(\n", - " plot.line(x=years, y=y1, opacity=0.5, ylim=[0.06, 0.07], **opts)\n", - " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", - " + plot.line(x=years, y=y3, opacity=1, **opts)\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/fire/figure_foresttype.ipynb b/notebooks/fire/figure_foresttype.ipynb index b92668a..788ed43 100644 --- a/notebooks/fire/figure_foresttype.ipynb +++ b/notebooks/fire/figure_foresttype.ipynb @@ -1,315 +1,315 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "connected-collaboration", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Figure 3: Dependence of model performance on forest type\n", - "\n", - "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", - "\n", - "The methods below conduct the analyses to recreate Figure 3 included the\n", - "manuscript <<< insert doi >>>. In this analysis we mean forest group type.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "connected-collaboration", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Figure 3: Dependence of model performance on forest type\n", + "\n", + "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", + "\n", + "The methods below conduct the analyses to recreate Figure 3 included the\n", + "manuscript <<< insert doi >>>. In this analysis we mean forest group type.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "rough-geneva", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forests.utils import get_store\n", + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "regional-accreditation", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect\n", + "from showit import image\n", + "import scipy as sp\n", + "import numpy as np\n", + "import pandas as pd\n", + "import xarray as xr\n", + "import geopandas as gpd\n", + "import regionmask as rm\n", + "from scipy.stats import binom\n", + "import altair as alt\n", + "\n", + "from palettable.colorbrewer.sequential import YlOrRd_9" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "charming-worship", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "store = \"az\"\n", + "tlim = (\"1984\", \"2018\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "amber-deviation", + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + ").astype(\"float\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aerial-apartment", + "metadata": {}, + "outputs": [], + "source": [ + "nlcd = load.nlcd(store=store, year=2001, coarsen=coarsen, mask=mask)\n", + "nftd = load.nftd(store=store, area_threshold=1500, coarsen=coarsen, mask=mask)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "opposite-quebec", + "metadata": {}, + "outputs": [], + "source": [ + "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=tlim, mask=mask)\n", + "# give it the same x/y coords as nftd (and nlcd and climate)\n", + "# this re-assignment will help us with some masking lower down\n", + "mtbs = mtbs.assign_coords({\"x\": nftd.x, \"y\": nftd.y})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "operating-directory", + "metadata": {}, + "outputs": [], + "source": [ + "historical_ds = xr.open_zarr(\n", + " get_store(\"carbonplan-scratch\", \"data/fire_historical_final.zarr\")\n", + ").assign_coords({\"y\": mtbs.y, \"x\": mtbs.x})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "breathing-internet", + "metadata": {}, + "outputs": [], + "source": [ + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "outer-calcium", + "metadata": {}, + "outputs": [], + "source": [ + "all_evals = []\n", + "all_stats = []\n", + "for band in nftd.band.values:\n", + " stats = plot.fire.full_eval(\n", + " mtbs * nftd.sel(band=band),\n", + " historical_ds * nftd.sel(band=band),\n", + " data_var=\"monthly\",\n", + " model_var=\"historical\",\n", + " clim=(-0.001, 0.001),\n", + " cmap=\"purplegreen\",\n", + " percentage=False,\n", + " clabel=\"Diff (Mod-Obs) [-]\",\n", + " )\n", + " all_stats.append(stats)\n", + "# all_evals.append(chart)\n", + "# alt.vconcat(*all_evals)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "false-momentum", + "metadata": {}, + "outputs": [], + "source": [ + "performances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "pharmaceutical-polymer", + "metadata": {}, + "outputs": [], + "source": [ + "test = (mtbs.groupby(\"time.year\").sum().mean(dim=\"year\") * nftd).mean(\n", + " [\"x\", \"y\"]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "existing-split", + "metadata": {}, + "outputs": [], + "source": [ + "mtbs_band_probs = test.mean([\"x\", \"y\"]).to_dataframe()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fancy-relations", + "metadata": {}, + "outputs": [], + "source": [ + "df_stats = pd.DataFrame(\n", + " columns=[\n", + " \"Forest group type\",\n", + " \"Annual correlation\",\n", + " \"Seasonal correlation\",\n", + " \"Spatial correlation\",\n", + " \"Burn area (fraction/year)\",\n", + " ]\n", + ")\n", + "for row, (band, (performances)) in enumerate(zip(nftd.band.values, all_stats)):\n", + " df_stats.loc[row] = [\n", + " str(band),\n", + " performances[0][\"annual\"],\n", + " performances[0][\"seasonal\"],\n", + " performances[0][\"spatial\"],\n", + " mtbs_band_probs.loc[band][\"monthly\"],\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bridal-spyware", + "metadata": {}, + "outputs": [], + "source": [ + "df_stats" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "center-chicken", + "metadata": {}, + "outputs": [], + "source": [ + "alt.Chart(df_stats).mark_bar().encode(\n", + " alt.Y(\"Annual correlation\", scale=alt.Scale(domain=(0, 1), scheme='greys')),\n", + " x=\"Forest group type\",\n", + " color=\"Burn area (fraction/year)\"\n", + ") | alt.Chart(df_stats).mark_bar().encode(\n", + " scale=alt.Scale(domain=(0, 1), scheme='greys')),\n", + " x='Forest group type',\n", + " \n", + " y=\"Seasonal correlation\", color=\"Burn area (fraction/year):Q\"\n", + ") | alt.Chart(\n", + " df_stats\n", + ").mark_bar().encode(\n", + " alt.X(\"Forest group type\",\n", + " scale=alt.Scale(domain=(0, 1), scheme='greys')), y=\"Spatial correlation\", color=\"Burn area (fraction/year)\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "certain-memphis", + "metadata": {}, + "outputs": [], + "source": [ + "df_stats" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "novel-chase", + "metadata": {}, + "outputs": [], + "source": [ + "alt.Chart(df_stats, height=150, width=200).mark_bar().encode(\n", + " alt.Y(\"Annual correlation\", scale=alt.Scale(domain=(0, 1))),\n", + " x=\"Forest group type\",\n", + " color=\"Burn area (fraction/year):Q\",\n", + ") | alt.Chart(df_stats, height=150, width=200).mark_bar().encode(\n", + " alt.Y(\"Seasonal correlation\", scale=alt.Scale(domain=(0, 1))),\n", + " x=\"Forest group type\",\n", + " color=\"Burn area (fraction/year):Q\",\n", + ") | alt.Chart(\n", + " df_stats, height=150, width=200\n", + ").mark_bar().encode(\n", + " alt.Y(\"Spatial correlation\", scale=alt.Scale(domain=(0, 1))),\n", + " alt.Color(\"Burn area (fraction/year):Q\", scale=alt.Scale(scheme=\"greys\")),\n", + " x=\"Forest group type\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "progressive-enemy", + "metadata": {}, + "outputs": [], + "source": [ + "final.configure(fontSize=20)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "rough-geneva", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forests.utils import get_store\n", - "import warnings\n", - "warnings.filterwarnings('ignore')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "regional-accreditation", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect\n", - "from showit import image\n", - "import scipy as sp\n", - "import numpy as np\n", - "import pandas as pd\n", - "import xarray as xr\n", - "import geopandas as gpd\n", - "import regionmask as rm\n", - "from scipy.stats import binom\n", - "import altair as alt\n", - "\n", - "from palettable.colorbrewer.sequential import YlOrRd_9" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "charming-worship", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "store = \"az\"\n", - "tlim = (\"1984\", \"2018\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "amber-deviation", - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - ").astype(\"float\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aerial-apartment", - "metadata": {}, - "outputs": [], - "source": [ - "nlcd = load.nlcd(store=store, year=2001, coarsen=coarsen, mask=mask)\n", - "nftd = load.nftd(store=store, area_threshold=1500, coarsen=coarsen, mask=mask)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "opposite-quebec", - "metadata": {}, - "outputs": [], - "source": [ - "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=tlim, mask=mask)\n", - "# give it the same x/y coords as nftd (and nlcd and climate)\n", - "# this re-assignment will help us with some masking lower down\n", - "mtbs = mtbs.assign_coords({\"x\": nftd.x, \"y\": nftd.y})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "operating-directory", - "metadata": {}, - "outputs": [], - "source": [ - "historical_ds = xr.open_zarr(\n", - " get_store(\"carbonplan-scratch\", \"data/fire_historical_final.zarr\")\n", - ").assign_coords({\"y\": mtbs.y, \"x\": mtbs.x})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "breathing-internet", - "metadata": {}, - "outputs": [], - "source": [ - "alt.data_transformers.disable_max_rows()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "outer-calcium", - "metadata": {}, - "outputs": [], - "source": [ - "all_evals = []\n", - "all_stats = []\n", - "for band in nftd.band.values:\n", - " stats = plot.fire.full_eval(\n", - " mtbs * nftd.sel(band=band),\n", - " historical_ds * nftd.sel(band=band),\n", - " data_var=\"monthly\",\n", - " model_var=\"historical\",\n", - " clim=(-0.001, 0.001),\n", - " cmap=\"purplegreen\",\n", - " percentage=False,\n", - " clabel=\"Diff (Mod-Obs) [-]\",\n", - " )\n", - " all_stats.append(stats)\n", - "# all_evals.append(chart)\n", - "# alt.vconcat(*all_evals)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "false-momentum", - "metadata": {}, - "outputs": [], - "source": [ - "performances" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "pharmaceutical-polymer", - "metadata": {}, - "outputs": [], - "source": [ - "test = (mtbs.groupby(\"time.year\").sum().mean(dim=\"year\") * nftd).mean(\n", - " [\"x\", \"y\"]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "existing-split", - "metadata": {}, - "outputs": [], - "source": [ - "mtbs_band_probs = test.mean([\"x\", \"y\"]).to_dataframe()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fancy-relations", - "metadata": {}, - "outputs": [], - "source": [ - "df_stats = pd.DataFrame(\n", - " columns=[\n", - " \"Forest group type\",\n", - " \"Annual correlation\",\n", - " \"Seasonal correlation\",\n", - " \"Spatial correlation\",\n", - " \"Burn area (fraction/year)\",\n", - " ]\n", - ")\n", - "for row, (band, (performances)) in enumerate(zip(nftd.band.values, all_stats)):\n", - " df_stats.loc[row] = [\n", - " str(band),\n", - " performances[0][\"annual\"],\n", - " performances[0][\"seasonal\"],\n", - " performances[0][\"spatial\"],\n", - " mtbs_band_probs.loc[band][\"monthly\"],\n", - " ]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bridal-spyware", - "metadata": {}, - "outputs": [], - "source": [ - "df_stats" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "center-chicken", - "metadata": {}, - "outputs": [], - "source": [ - "alt.Chart(df_stats).mark_bar().encode(\n", - " alt.Y(\"Annual correlation\", scale=alt.Scale(domain=(0, 1), scheme='greys')),\n", - " x=\"Forest group type\",\n", - " color=\"Burn area (fraction/year)\"\n", - ") | alt.Chart(df_stats).mark_bar().encode(\n", - " scale=alt.Scale(domain=(0, 1), scheme='greys')),\n", - " x='Forest group type',\n", - " \n", - " y=\"Seasonal correlation\", color=\"Burn area (fraction/year):Q\"\n", - ") | alt.Chart(\n", - " df_stats\n", - ").mark_bar().encode(\n", - " alt.X(\"Forest group type\",\n", - " scale=alt.Scale(domain=(0, 1), scheme='greys')), y=\"Spatial correlation\", color=\"Burn area (fraction/year)\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "certain-memphis", - "metadata": {}, - "outputs": [], - "source": [ - "df_stats" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "novel-chase", - "metadata": {}, - "outputs": [], - "source": [ - "alt.Chart(df_stats, height=150, width=200).mark_bar().encode(\n", - " alt.Y(\"Annual correlation\", scale=alt.Scale(domain=(0, 1))),\n", - " x=\"Forest group type\",\n", - " color=\"Burn area (fraction/year):Q\",\n", - ") | alt.Chart(df_stats, height=150, width=200).mark_bar().encode(\n", - " alt.Y(\"Seasonal correlation\", scale=alt.Scale(domain=(0, 1))),\n", - " x=\"Forest group type\",\n", - " color=\"Burn area (fraction/year):Q\",\n", - ") | alt.Chart(\n", - " df_stats, height=150, width=200\n", - ").mark_bar().encode(\n", - " alt.Y(\"Spatial correlation\", scale=alt.Scale(domain=(0, 1))),\n", - " alt.Color(\"Burn area (fraction/year):Q\", scale=alt.Scale(scheme=\"greys\")),\n", - " x=\"Forest group type\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "progressive-enemy", - "metadata": {}, - "outputs": [], - "source": [ - "final.configure(fontSize=20)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/fire/fire_model.ipynb b/notebooks/fire/fire_model.ipynb index b132a9f..9e45563 100644 --- a/notebooks/fire/fire_model.ipynb +++ b/notebooks/fire/fire_model.ipynb @@ -1,567 +1,567 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# MTBS Fire Model\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", - "\n", - "This notebook loads downsampled MTBS fire data and TerraClimate climate data and\n", - "fits a logistical regression model.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forests import load, setup, plot, fit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we set some top-level parameters: the level of spatial coarsening (which\n", - "can speed up fitting substantially, especially useful when testing), the time\n", - "range (MTBS spans 1984 to 2018), and the climatic variables we'll use in the\n", - "model.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 16\n", - "tlim = (1984, 2018)\n", - "data_vars = [\"ppt\", \"tavg\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we load data from `nlcd` which we will use to mask out non-land areas and\n", - "data from `nftd` to use a regressors for forest type groups.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mask = load.nlcd(store=\"local\", classes=\"all\", year=2001)\n", - "groups = load.nftd(\n", - " store=\"local\", groups=\"all\", coarsen=coarsen, mask=mask, area_threshold=1500\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we load the `terraclim` data from the same time range.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "climate = load.terraclim(\n", - " store=\"local\", tlim=tlim, coarsen=coarsen, data_vars=data_vars, mask=mask\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And finally load the `mtbs` data, setting all non-zero values to 1 as setup for\n", - "our logistic model.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtbs = load.mtbs(store=\"local\", coarsen=0, tlim=tlim)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from showit import image" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from scipy.stats import binom" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def integrated_risk(p):\n", - " return 1 - binom.cdf(0, 100, p)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "image(\n", - " (mtbs[\"vlf\"].sel(time=slice(\"2006\", \"2006\")).sum(\"time\")),\n", - " size=12,\n", - " clim=(0, 1),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import rasterio" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "r = rasterio.open(\n", - " \"/Users/freeman/workdir/carbonplan-data/processed/mtbs/conus/30m/2006.tif\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "im = r.read(1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%gui qt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import napari" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "napari.view_image(im == 1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Inspecting the data\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the `mtbs` data for a specific year to get a sense of the raw data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.summary(mtbs, clim=(0, 0.1))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(mtbs.sel(time=\"2016\"), \"vlf\", clim=(0, 1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is the same thing but averaged over all years\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtbs.groupby(\"time.month\").mean()[\"vlf\"].max().values" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(mtbs, \"vlf\", clim=(0, 0.2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot spatially averaged data over time to see both seasonable trends, and\n", - "the fact that fire frequency and magnitude has increased over time.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs[\"time\"]\n", - "y = mtbs[\"vlf\"].mean([\"x\", \"y\"])\n", - "\n", - "plot.line(\n", - " x=x, y=y, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can summarize the data by plotting annual, seasonal, and spatial trends by\n", - "averaging across different groupings and dimensions.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.summary(mtbs, \"vlf\", clim=(0, 0.05))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a motivation for fitting fires as functions of climatic variables, we can\n", - "look at spatially averaged climatic variables alongside fires, e.g. average\n", - "tempreature, and see that there is a clear relationship (in this case a positive\n", - "correlation).\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs.groupby(\"time.year\").mean()[\"year\"]\n", - "y1 = mtbs[\"vlf\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"])\n", - "y2 = climate[\"tavg\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"])\n", - "\n", - "(\n", - " plot.line(x=x, y=sp.stats.zscore(y1), color=\"rgb(175,91,92)\")\n", - " + plot.line(x=x, y=sp.stats.zscore(y2), color=\"rgb(175,91,92)\", opacity=0.5)\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Fitting the model\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll define the variables to use in the model\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fit_vars = [\"tavg\", \"ppt\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then we fit it by providing both the climate variables and the forest type\n", - "groups. We distinguish the two sets of input variables because the climate\n", - "variables change over time by the groups do not.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = fit.fire(x=climate[fit_vars], y=mtbs[\"vlf\"], f=groups)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can evaluate the fitting model on arbitrary new inputs. In this case, we\n", - "evaluate on the data we used to fit.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "prediction = model.predict(x=climate[fit_vars], f=groups)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the predictions monthly just as we plotted the real data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(prediction, \"prob\", clim=(0, 0.01))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can plot the same summary of trends but now with both data and model\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.evaluation(mtbs, prediction, \"vlf\", \"prob\", clim=(0.002, 0.02))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also compare the predicted and real probabilities over time.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs[\"time\"]\n", - "y1 = mtbs[\"vlf\"].mean([\"x\", \"y\"])\n", - "y2 = prediction[\"prob\"].mean([\"x\", \"y\"])\n", - "\n", - "(\n", - " plot.line(\n", - " x=x,\n", - " y=y1,\n", - " width=900,\n", - " height=200,\n", - " opacity=0.5,\n", - " color=\"rgb(175,91,92)\",\n", - " strokeWidth=2,\n", - " )\n", - " + plot.line(\n", - " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given that we fit a logistic regression, the natural model performance metric is\n", - "area under the ROC curve, which we computed on the training data (you can use\n", - "the variable `crossval` to repeat the model fit and compute a score on 25% held\n", - "out data).\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model.train_roc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can compute correlations between model and prediction for annual,\n", - "seasonal, and spatial trends. Note that these were not metrics used to\n", - "explicitly fit the model, but in general a better fitting model ought to\n", - "reproduce at least some of these trends.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of annual trends\")\n", - "np.corrcoef(\n", - " mtbs[\"vlf\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", - " prediction[\"prob\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", - ")[0, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of monthly trends\")\n", - "np.corrcoef(\n", - " mtbs[\"vlf\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", - " prediction[\"prob\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", - ")[0, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of spatial trends\")\n", - "np.corrcoef(\n", - " mtbs[\"vlf\"].mean(\"time\").values.flatten(),\n", - " prediction[\"prob\"].mean(\"time\").values.flatten(),\n", - ")[0, 1]" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.2" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# MTBS Fire Model\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", + "\n", + "This notebook loads downsampled MTBS fire data and TerraClimate climate data and\n", + "fits a logistical regression model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forests import load, setup, plot, fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we set some top-level parameters: the level of spatial coarsening (which\n", + "can speed up fitting substantially, especially useful when testing), the time\n", + "range (MTBS spans 1984 to 2018), and the climatic variables we'll use in the\n", + "model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 16\n", + "tlim = (1984, 2018)\n", + "data_vars = [\"ppt\", \"tavg\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we load data from `nlcd` which we will use to mask out non-land areas and\n", + "data from `nftd` to use a regressors for forest type groups.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mask = load.nlcd(store=\"local\", classes=\"all\", year=2001)\n", + "groups = load.nftd(\n", + " store=\"local\", groups=\"all\", coarsen=coarsen, mask=mask, area_threshold=1500\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we load the `terraclim` data from the same time range.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "climate = load.terraclim(\n", + " store=\"local\", tlim=tlim, coarsen=coarsen, data_vars=data_vars, mask=mask\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally load the `mtbs` data, setting all non-zero values to 1 as setup for\n", + "our logistic model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mtbs = load.mtbs(store=\"local\", coarsen=0, tlim=tlim)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from showit import image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.stats import binom" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def integrated_risk(p):\n", + " return 1 - binom.cdf(0, 100, p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "image(\n", + " (mtbs[\"vlf\"].sel(time=slice(\"2006\", \"2006\")).sum(\"time\")),\n", + " size=12,\n", + " clim=(0, 1),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import rasterio" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r = rasterio.open(\n", + " \"/Users/freeman/workdir/carbonplan-data/processed/mtbs/conus/30m/2006.tif\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "im = r.read(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%gui qt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import napari" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "napari.view_image(im == 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inspecting the data\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the `mtbs` data for a specific year to get a sense of the raw data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.summary(mtbs, clim=(0, 0.1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(mtbs.sel(time=\"2016\"), \"vlf\", clim=(0, 1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is the same thing but averaged over all years\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mtbs.groupby(\"time.month\").mean()[\"vlf\"].max().values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(mtbs, \"vlf\", clim=(0, 0.2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot spatially averaged data over time to see both seasonable trends, and\n", + "the fact that fire frequency and magnitude has increased over time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs[\"time\"]\n", + "y = mtbs[\"vlf\"].mean([\"x\", \"y\"])\n", + "\n", + "plot.line(\n", + " x=x, y=y, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can summarize the data by plotting annual, seasonal, and spatial trends by\n", + "averaging across different groupings and dimensions.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.summary(mtbs, \"vlf\", clim=(0, 0.05))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a motivation for fitting fires as functions of climatic variables, we can\n", + "look at spatially averaged climatic variables alongside fires, e.g. average\n", + "tempreature, and see that there is a clear relationship (in this case a positive\n", + "correlation).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs.groupby(\"time.year\").mean()[\"year\"]\n", + "y1 = mtbs[\"vlf\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"])\n", + "y2 = climate[\"tavg\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"])\n", + "\n", + "(\n", + " plot.line(x=x, y=sp.stats.zscore(y1), color=\"rgb(175,91,92)\")\n", + " + plot.line(x=x, y=sp.stats.zscore(y2), color=\"rgb(175,91,92)\", opacity=0.5)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fitting the model\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll define the variables to use in the model\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fit_vars = [\"tavg\", \"ppt\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we fit it by providing both the climate variables and the forest type\n", + "groups. We distinguish the two sets of input variables because the climate\n", + "variables change over time by the groups do not.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = fit.fire(x=climate[fit_vars], y=mtbs[\"vlf\"], f=groups)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can evaluate the fitting model on arbitrary new inputs. In this case, we\n", + "evaluate on the data we used to fit.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "prediction = model.predict(x=climate[fit_vars], f=groups)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the predictions monthly just as we plotted the real data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(prediction, \"prob\", clim=(0, 0.01))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we can plot the same summary of trends but now with both data and model\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.evaluation(mtbs, prediction, \"vlf\", \"prob\", clim=(0.002, 0.02))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also compare the predicted and real probabilities over time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs[\"time\"]\n", + "y1 = mtbs[\"vlf\"].mean([\"x\", \"y\"])\n", + "y2 = prediction[\"prob\"].mean([\"x\", \"y\"])\n", + "\n", + "(\n", + " plot.line(\n", + " x=x,\n", + " y=y1,\n", + " width=900,\n", + " height=200,\n", + " opacity=0.5,\n", + " color=\"rgb(175,91,92)\",\n", + " strokeWidth=2,\n", + " )\n", + " + plot.line(\n", + " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Given that we fit a logistic regression, the natural model performance metric is\n", + "area under the ROC curve, which we computed on the training data (you can use\n", + "the variable `crossval` to repeat the model fit and compute a score on 25% held\n", + "out data).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.train_roc" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can compute correlations between model and prediction for annual,\n", + "seasonal, and spatial trends. Note that these were not metrics used to\n", + "explicitly fit the model, but in general a better fitting model ought to\n", + "reproduce at least some of these trends.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of annual trends\")\n", + "np.corrcoef(\n", + " mtbs[\"vlf\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", + " prediction[\"prob\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", + ")[0, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of monthly trends\")\n", + "np.corrcoef(\n", + " mtbs[\"vlf\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", + " prediction[\"prob\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", + ")[0, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of spatial trends\")\n", + "np.corrcoef(\n", + " mtbs[\"vlf\"].mean(\"time\").values.flatten(),\n", + " prediction[\"prob\"].mean(\"time\").values.flatten(),\n", + ")[0, 1]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/fire/fire_model_redux.ipynb b/notebooks/fire/fire_model_redux.ipynb index b5d7852..29a0938 100644 --- a/notebooks/fire/fire_model_redux.ipynb +++ b/notebooks/fire/fire_model_redux.ipynb @@ -1,867 +1,867 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# MTBS Fire Model\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", - "\n", - "This notebook loads downsampled MTBS fire data and TerraClimate climate data and\n", - "fits a logistical regression model.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "from showit import image, tile\n", - "import xarray as xr\n", - "from astropy.convolution import Gaussian2DKernel\n", - "from astropy.convolution import convolve, convolve_fft\n", - "import altair as alt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we set some top-level parameters: the level of spatial coarsening (which\n", - "can speed up fitting substantially, especially useful when testing), the time\n", - "range (MTBS spans 1984 to 2018), and the climatic variables we'll use in the\n", - "model.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "full_climate_period = (1983, 2018)\n", - "analysis_tlim = (1984, 2018)\n", - "variables = [\"ppt\", \"tmean\", \"cwd\"] # , \"tmax\", \"tmin\"]\n", - "store = \"az\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First we load data from `nlcd` which we will use to mask out non-land areas and\n", - "data from `nftd` to use a regressors for forest type groups.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - ").astype(\"float\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "nlcd = load.nlcd(store=store, year=2001, coarsen=coarsen, mask=mask)\n", - "nftd = load.nftd(store=store, area_threshold=1500, coarsen=coarsen, mask=mask)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "coarsened_conus_domain = load.mask(coarsen=coarsen)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we load the `terraclim` data from the same time range.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "climate = load.terraclim(\n", - " store=store,\n", - " tlim=full_climate_period,\n", - " coarsen=coarsen,\n", - " variables=variables,\n", - " mask=mask,\n", - " sampling=\"monthly\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And finally load the `mtbs` data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=analysis_tlim, mask=mask)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Inspecting the data\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot the `mtbs` data for a specific year to get a sense of the raw data.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(mtbs.sel(time=\"2018\"), clim=(0, 0.2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is the same thing but averaged over all years\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(mtbs, clim=(0.00005, 0.0015))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## We can plot spatially averaged data over time to see both seasonable trends, and\n", - "\n", - "the fact that fire frequency and magnitude has increased over time.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs[\"time\"]\n", - "y = mtbs[\"monthly\"].mean([\"x\", \"y\"])\n", - "\n", - "plot.line(\n", - " x=x, y=y, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can summarize the data by plotting annual, seasonal, and spatial trends by\n", - "averaging across different groupings and dimensions.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.summary(mtbs, clim=(0, 0.005))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a motivation for fitting fires as functions of climatic variables, we can\n", - "look at spatially averaged climatic variables alongside fires, e.g. average\n", - "tempreature, and see that there is a clear relationship (in this case a positive\n", - "correlation).\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs.groupby(\"time.year\").mean()[\"year\"]\n", - "y1 = mtbs[\"monthly\"].groupby(\"time.year\").sum().mean([\"x\", \"y\"])\n", - "y2 = climate[\"tmean\"].groupby(\"time.year\").max().mean([\"x\", \"y\"])\n", - "\n", - "(\n", - " plot.line(x=x, y=sp.stats.zscore(y1), color=\"rgb(175,91,92)\")\n", - " + plot.line(x=x, y=sp.stats.zscore(y2), color=\"rgb(175,91,92)\", opacity=0.5)\n", - ")\n", - "\n", - "# np.corrcoef(y1,y2)[0,1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tmean precip comparisons\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sl = slice(\"1984\", \"2008\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "single_pixel = climate.isel(x=25, y=25)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(15, 8))\n", - "xr.DataArray(\n", - " np.repeat(\n", - " single_pixel[\"tmean\"].sel(time=sl).groupby(\"time.year\").max().values, 12\n", - " ),\n", - " coords=single_pixel.sel(time=sl).coords,\n", - ").plot(label=\"groupby\")\n", - "single_pixel[\"tmean\"].rolling(time=12).max().sel(time=sl).plot(label=\"rolling\")\n", - "single_pixel[\"tmean\"].sel(time=sl).plot(label=\"raw\", alpha=0.5)\n", - "plt.legend()\n", - "plt.ylim(0, 18)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ts = climate[\"tmean\"].groupby(\"time.year\").max().mean(dim=[\"x\", \"y\"]).values\n", - "groupby_global_max_then_mean = xr.DataArray(\n", - " np.repeat(ts, 12), coords=single_pixel.coords\n", - ")\n", - "ts = climate[\"tmean\"].mean(dim=[\"x\", \"y\"]).groupby(\"time.year\").max().values\n", - "groupby_global_mean_then_max = xr.DataArray(\n", - " np.repeat(ts, 12), coords=single_pixel.coords\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "groupby_global_mean_then_max.plot(label=\"mean then max\")\n", - "groupby_global_max_then_mean.plot(label=\"max then mean\")\n", - "plt.legend()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rolling_global_max = (\n", - " climate[\"tmean\"]\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .rolling(dim={\"time\": 12}, min_periods=8, center=False)\n", - " .max()\n", - " .sel(time=inspection_slice)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(10, 8))\n", - "inspection_slice = slice(\"1984\", \"1990\")\n", - "rolling_global_max.sel(time=inspection_slice).plot(label=\"rolling\")\n", - "# groupby_global_max_annual.sel(time=inspection_slice).plot(label='groupby')\n", - "\n", - "groupby_global_mean_then_max.sel(time=inspection_slice).plot(label=\"groupby\")\n", - "climate[\"tmean\"].sel(time=inspection_slice).mean(dim=[\"x\", \"y\"]).plot(\n", - " label=\"raw\"\n", - ")\n", - "plt.ylim(16, 24)\n", - "plt.legend()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate[\"ppt\"].rolling(time=12, center=False).sum().mean(dim=[\"x\", \"y\"]).plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate[\"tmean\"].rolling(time=12, center=False).max().isel(time=30).plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate[\"ppt\"].resample(time=\"AS\").mean().isel(time=3).plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate[\"ppt\"].groupby(\"time.year\").sum().mean(dim=[\"x\", \"y\"]).plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rolling = (\n", - " climate[\"ppt\"]\n", - " .rolling(time=12, center=False)\n", - " .sum()\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .sel(time=inspection_slice)\n", - ")\n", - "rolling.plot(label=\"rolling\")\n", - "xr.DataArray(\n", - " np.repeat(\n", - " climate[\"ppt\"]\n", - " .sel(time=inspection_slice)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .values,\n", - " 12,\n", - " ),\n", - " coords=rolling.coords,\n", - ").plot(label=\"groupby\")\n", - "plt.legend()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Original\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate.isel(time=3).tmean.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rolling_period = slice(\"1984\", \"2018\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", - "x, y = prepare.fire(\n", - " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", - " nftd,\n", - " mtbs,\n", - " add_global_climate_trends={\n", - " \"tmean\": {\"climate_prepend\": None, \"gaussian_kernel_size\": None},\n", - " \"ppt\": {\"climate_prepend\": None, \"gaussian_kernel_size\": None},\n", - " },\n", - " add_local_climate_trends=None,\n", - ")\n", - "# {'tmean': {'climate_prepend': None,\n", - "# 'gaussian_kernel_size': None},\n", - "# 'ppt': {'climate_prepend': None,\n", - "# 'gaussian_kernel_size': None}},)\n", - "# rolling_period=12)\n", - "x_z, x_mean, x_std = utils.zscore_2d(x)\n", - "model = fit.hurdle(x_z, y, log=False)\n", - "yhat = model.predict(x_z)\n", - "prediction_original = collect.fire(yhat, mtbs)\n", - "metrics, chart = plot.fire.full_eval(\n", - " mtbs,\n", - " prediction_original,\n", - " \"monthly\",\n", - " \"prediction\",\n", - " clim=(-0.08, 0.08),\n", - " cmap=\"purplegreen\",\n", - ")\n", - "chart" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", - "x, y = prepare.fire(\n", - " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", - " nftd,\n", - " mtbs,\n", - " add_global_climate_trends={\n", - " \"tmean\": {\n", - " \"climate_prepend\": climate_prepend,\n", - " \"gaussian_kernel_size\": None,\n", - " },\n", - " \"ppt\": {\n", - " \"climate_prepend\": climate_prepend,\n", - " \"gaussian_kernel_size\": None,\n", - " },\n", - " },\n", - " add_local_climate_trends=None,\n", - " # {'tmean': {'climate_prepend': None,\n", - " # 'gaussian_kernel_size': None},\n", - " # 'ppt': {'climate_prepend': None,\n", - " # 'gaussian_kernel_size': None}},)\n", - " rolling_period=8,\n", - ")\n", - "x_z, x_mean, x_std = utils.zscore_2d(x)\n", - "model = fit.hurdle(x_z, y, log=False)\n", - "yhat = model.predict(x_z)\n", - "prediction_original = collect.fire(yhat, mtbs)\n", - "metrics, chart = plot.fire.full_eval(\n", - " mtbs,\n", - " prediction_original,\n", - " \"monthly\",\n", - " \"prediction\",\n", - " clim=(-0.08, 0.08),\n", - " cmap=\"purplegreen\",\n", - ")\n", - "chart" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", - "x, y = prepare.fire(\n", - " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", - " nftd,\n", - " mtbs,\n", - " add_global_climate_trends={\n", - " \"tmean\": {\n", - " \"climate_prepend\": climate_prepend,\n", - " \"gaussian_kernel_size\": None,\n", - " },\n", - " \"ppt\": {\n", - " \"climate_prepend\": climate_prepend,\n", - " \"gaussian_kernel_size\": None,\n", - " },\n", - " },\n", - " add_local_climate_trends=None,\n", - " # {'tmean': {'climate_prepend': None,\n", - " # 'gaussian_kernel_size': None},\n", - " # 'ppt': {'climate_prepend': None,\n", - " # 'gaussian_kernel_size': None}},)\n", - " rolling_period=11,\n", - ")\n", - "x_z, x_mean, x_std = utils.zscore_2d(x)\n", - "model = fit.hurdle(x_z, y, log=False)\n", - "yhat = model.predict(x_z)\n", - "prediction = collect.fire(yhat, mtbs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.monthly(prediction, \"prediction\", clim=(0.00005, 0.0015))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.evaluation(mtbs, prediction, \"monthly\", \"prediction\", clim=(0, 0.005))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_mean" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also compare the predicted and real probabilities over time.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = mtbs[\"time\"]\n", - "y1 = mtbs[\"monthly\"].mean([\"x\", \"y\"])\n", - "y2 = prediction[\"prediction\"].mean([\"x\", \"y\"])\n", - "\n", - "(\n", - " plot.line(\n", - " x=x,\n", - " y=y1,\n", - " width=900,\n", - " height=200,\n", - " opacity=0.5,\n", - " color=\"rgb(175,91,92)\",\n", - " strokeWidth=2,\n", - " )\n", - " + plot.line(\n", - " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Given that we fit a logistic regression, the natural model performance metric is\n", - "area under the ROC curve, which we computed on the training data (you can use\n", - "the variable `crossval` to repeat the model fit and compute a score on 25% held\n", - "out data).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can compute correlations between model and prediction for annual,\n", - "seasonal, and spatial trends. Note that these were not metrics used to\n", - "explicitly fit the model, but in general a better fitting model ought to\n", - "reproduce at least some of these trends.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of annual trends\")\n", - "np.corrcoef(\n", - " mtbs[\"monthly\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", - " prediction[\"prediction\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", - ")[0, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of monthly trends\")\n", - "np.corrcoef(\n", - " mtbs[\"monthly\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", - " prediction[\"prediction\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", - ")[0, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"correlation of spatial trends\")\n", - "a = mtbs[\"monthly\"].mean(\"time\").values.flatten()\n", - "b = prediction[\"prediction\"].mean(\"time\").values.flatten()\n", - "inds = ~np.isnan(a) & ~np.isnan(b)\n", - "np.corrcoef(a[inds], b[inds])[0, 1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Prediction on future climate data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cmip_model = \"CanESM5\"\n", - "scenario = \"ssp245\"\n", - "target = 2025" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cmip_models = [\n", - " (\"CanESM5-CanOE\", \"r3i1p2f1\"),\n", - " (\"MIROC-ES2L\", \"r1i1p1f2\"), #\n", - " (\"ACCESS-CM2\", \"r1i1p1f1\"), #\n", - " (\"ACCESS-ESM1-5\", \"r10i1p1f1\"),\n", - " (\"MRI-ESM2-0\", \"r1i1p1f1\"),\n", - " (\"MPI-ESM1-2-LR\", \"r10i1p1f1\"),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import zarr" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "downscaling = \"bias-corrected\"\n", - "sampling = \"monthly\"\n", - "model = \"CanESM5-CanOE\"\n", - "scenario = \"ssp245\"\n", - "member = \"r3i1p2f1\"\n", - "store = \"az\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "downscaling = \"quantile-mapping\"\n", - "sampling = \"monthly\"\n", - "for (model, member) in cmip_models:\n", - " for scenario in [\"historical\", \"ssp245\", \"ssp370\", \"ssp585\"]:\n", - "\n", - " prefix = f\"cmip6/{downscaling}/conus/4000m/{sampling}/{model}.{scenario}.{member}.zarr\".format()\n", - "\n", - " if store == \"az\":\n", - " mapper = zarr.storage.ABSStore(\n", - " \"carbonplan-downscaling\",\n", - " prefix=prefix,\n", - " account_name=\"carbonplan\",\n", - " )\n", - " ds = xr.open_zarr(mapper, consolidated=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model = \"ACCESS-CM2\"\n", - "scenario = \"ssp370\" #'r1i1p1f1'\n", - "future_climate = load.cmip(\n", - " store=store,\n", - " model=cmip_model,\n", - " coarsen=16,\n", - " scenario=scenario,\n", - " tlim=(2020, 2100),\n", - " variables=variables,\n", - " mask=mask,\n", - " sampling=\"monthly\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = prepare.fire(future_climate, nftd, eval_only=True)\n", - "x_z = utils.zscore_2d(x, mean=x_mean, std=x_std)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "yhat = model.predict(x_z)\n", - "prediction = collect.fire(yhat, future_climate)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.fire.summary(prediction, \"prediction\", clim=(0.0006, 0.008))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = prediction[\"time\"]\n", - "y2 = prediction[\"prediction\"].mean([\"x\", \"y\"])\n", - "\n", - "(\n", - " plot.line(\n", - " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", - " )\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.2" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# MTBS Fire Model\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), September 19, 2020_\n", + "\n", + "This notebook loads downsampled MTBS fire data and TerraClimate climate data and\n", + "fits a logistical regression model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "from showit import image, tile\n", + "import xarray as xr\n", + "from astropy.convolution import Gaussian2DKernel\n", + "from astropy.convolution import convolve, convolve_fft\n", + "import altair as alt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we set some top-level parameters: the level of spatial coarsening (which\n", + "can speed up fitting substantially, especially useful when testing), the time\n", + "range (MTBS spans 1984 to 2018), and the climatic variables we'll use in the\n", + "model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "full_climate_period = (1983, 2018)\n", + "analysis_tlim = (1984, 2018)\n", + "variables = [\"ppt\", \"tmean\", \"cwd\"] # , \"tmax\", \"tmin\"]\n", + "store = \"az\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we load data from `nlcd` which we will use to mask out non-land areas and\n", + "data from `nftd` to use a regressors for forest type groups.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + ").astype(\"float\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nlcd = load.nlcd(store=store, year=2001, coarsen=coarsen, mask=mask)\n", + "nftd = load.nftd(store=store, area_threshold=1500, coarsen=coarsen, mask=mask)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "coarsened_conus_domain = load.mask(coarsen=coarsen)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we load the `terraclim` data from the same time range.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "climate = load.terraclim(\n", + " store=store,\n", + " tlim=full_climate_period,\n", + " coarsen=coarsen,\n", + " variables=variables,\n", + " mask=mask,\n", + " sampling=\"monthly\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally load the `mtbs` data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=analysis_tlim, mask=mask)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inspecting the data\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the `mtbs` data for a specific year to get a sense of the raw data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(mtbs.sel(time=\"2018\"), clim=(0, 0.2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is the same thing but averaged over all years\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(mtbs, clim=(0.00005, 0.0015))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## We can plot spatially averaged data over time to see both seasonable trends, and\n", + "\n", + "the fact that fire frequency and magnitude has increased over time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs[\"time\"]\n", + "y = mtbs[\"monthly\"].mean([\"x\", \"y\"])\n", + "\n", + "plot.line(\n", + " x=x, y=y, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can summarize the data by plotting annual, seasonal, and spatial trends by\n", + "averaging across different groupings and dimensions.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.summary(mtbs, clim=(0, 0.005))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a motivation for fitting fires as functions of climatic variables, we can\n", + "look at spatially averaged climatic variables alongside fires, e.g. average\n", + "tempreature, and see that there is a clear relationship (in this case a positive\n", + "correlation).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs.groupby(\"time.year\").mean()[\"year\"]\n", + "y1 = mtbs[\"monthly\"].groupby(\"time.year\").sum().mean([\"x\", \"y\"])\n", + "y2 = climate[\"tmean\"].groupby(\"time.year\").max().mean([\"x\", \"y\"])\n", + "\n", + "(\n", + " plot.line(x=x, y=sp.stats.zscore(y1), color=\"rgb(175,91,92)\")\n", + " + plot.line(x=x, y=sp.stats.zscore(y2), color=\"rgb(175,91,92)\", opacity=0.5)\n", + ")\n", + "\n", + "# np.corrcoef(y1,y2)[0,1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tmean precip comparisons\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sl = slice(\"1984\", \"2008\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "single_pixel = climate.isel(x=25, y=25)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(15, 8))\n", + "xr.DataArray(\n", + " np.repeat(\n", + " single_pixel[\"tmean\"].sel(time=sl).groupby(\"time.year\").max().values, 12\n", + " ),\n", + " coords=single_pixel.sel(time=sl).coords,\n", + ").plot(label=\"groupby\")\n", + "single_pixel[\"tmean\"].rolling(time=12).max().sel(time=sl).plot(label=\"rolling\")\n", + "single_pixel[\"tmean\"].sel(time=sl).plot(label=\"raw\", alpha=0.5)\n", + "plt.legend()\n", + "plt.ylim(0, 18)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ts = climate[\"tmean\"].groupby(\"time.year\").max().mean(dim=[\"x\", \"y\"]).values\n", + "groupby_global_max_then_mean = xr.DataArray(\n", + " np.repeat(ts, 12), coords=single_pixel.coords\n", + ")\n", + "ts = climate[\"tmean\"].mean(dim=[\"x\", \"y\"]).groupby(\"time.year\").max().values\n", + "groupby_global_mean_then_max = xr.DataArray(\n", + " np.repeat(ts, 12), coords=single_pixel.coords\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "groupby_global_mean_then_max.plot(label=\"mean then max\")\n", + "groupby_global_max_then_mean.plot(label=\"max then mean\")\n", + "plt.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rolling_global_max = (\n", + " climate[\"tmean\"]\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .rolling(dim={\"time\": 12}, min_periods=8, center=False)\n", + " .max()\n", + " .sel(time=inspection_slice)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(10, 8))\n", + "inspection_slice = slice(\"1984\", \"1990\")\n", + "rolling_global_max.sel(time=inspection_slice).plot(label=\"rolling\")\n", + "# groupby_global_max_annual.sel(time=inspection_slice).plot(label='groupby')\n", + "\n", + "groupby_global_mean_then_max.sel(time=inspection_slice).plot(label=\"groupby\")\n", + "climate[\"tmean\"].sel(time=inspection_slice).mean(dim=[\"x\", \"y\"]).plot(\n", + " label=\"raw\"\n", + ")\n", + "plt.ylim(16, 24)\n", + "plt.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate[\"ppt\"].rolling(time=12, center=False).sum().mean(dim=[\"x\", \"y\"]).plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate[\"tmean\"].rolling(time=12, center=False).max().isel(time=30).plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate[\"ppt\"].resample(time=\"AS\").mean().isel(time=3).plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate[\"ppt\"].groupby(\"time.year\").sum().mean(dim=[\"x\", \"y\"]).plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rolling = (\n", + " climate[\"ppt\"]\n", + " .rolling(time=12, center=False)\n", + " .sum()\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .sel(time=inspection_slice)\n", + ")\n", + "rolling.plot(label=\"rolling\")\n", + "xr.DataArray(\n", + " np.repeat(\n", + " climate[\"ppt\"]\n", + " .sel(time=inspection_slice)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .values,\n", + " 12,\n", + " ),\n", + " coords=rolling.coords,\n", + ").plot(label=\"groupby\")\n", + "plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Original\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate.isel(time=3).tmean.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rolling_period = slice(\"1984\", \"2018\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", + "x, y = prepare.fire(\n", + " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", + " nftd,\n", + " mtbs,\n", + " add_global_climate_trends={\n", + " \"tmean\": {\"climate_prepend\": None, \"gaussian_kernel_size\": None},\n", + " \"ppt\": {\"climate_prepend\": None, \"gaussian_kernel_size\": None},\n", + " },\n", + " add_local_climate_trends=None,\n", + ")\n", + "# {'tmean': {'climate_prepend': None,\n", + "# 'gaussian_kernel_size': None},\n", + "# 'ppt': {'climate_prepend': None,\n", + "# 'gaussian_kernel_size': None}},)\n", + "# rolling_period=12)\n", + "x_z, x_mean, x_std = utils.zscore_2d(x)\n", + "model = fit.hurdle(x_z, y, log=False)\n", + "yhat = model.predict(x_z)\n", + "prediction_original = collect.fire(yhat, mtbs)\n", + "metrics, chart = plot.fire.full_eval(\n", + " mtbs,\n", + " prediction_original,\n", + " \"monthly\",\n", + " \"prediction\",\n", + " clim=(-0.08, 0.08),\n", + " cmap=\"purplegreen\",\n", + ")\n", + "chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", + "x, y = prepare.fire(\n", + " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", + " nftd,\n", + " mtbs,\n", + " add_global_climate_trends={\n", + " \"tmean\": {\n", + " \"climate_prepend\": climate_prepend,\n", + " \"gaussian_kernel_size\": None,\n", + " },\n", + " \"ppt\": {\n", + " \"climate_prepend\": climate_prepend,\n", + " \"gaussian_kernel_size\": None,\n", + " },\n", + " },\n", + " add_local_climate_trends=None,\n", + " # {'tmean': {'climate_prepend': None,\n", + " # 'gaussian_kernel_size': None},\n", + " # 'ppt': {'climate_prepend': None,\n", + " # 'gaussian_kernel_size': None}},)\n", + " rolling_period=8,\n", + ")\n", + "x_z, x_mean, x_std = utils.zscore_2d(x)\n", + "model = fit.hurdle(x_z, y, log=False)\n", + "yhat = model.predict(x_z)\n", + "prediction_original = collect.fire(yhat, mtbs)\n", + "metrics, chart = plot.fire.full_eval(\n", + " mtbs,\n", + " prediction_original,\n", + " \"monthly\",\n", + " \"prediction\",\n", + " clim=(-0.08, 0.08),\n", + " cmap=\"purplegreen\",\n", + ")\n", + "chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "climate_prepend = climate.sel(time=slice(\"1983\", \"1983\"))\n", + "x, y = prepare.fire(\n", + " climate.sel(time=slice(*[str(t) for t in analysis_tlim])), #\n", + " nftd,\n", + " mtbs,\n", + " add_global_climate_trends={\n", + " \"tmean\": {\n", + " \"climate_prepend\": climate_prepend,\n", + " \"gaussian_kernel_size\": None,\n", + " },\n", + " \"ppt\": {\n", + " \"climate_prepend\": climate_prepend,\n", + " \"gaussian_kernel_size\": None,\n", + " },\n", + " },\n", + " add_local_climate_trends=None,\n", + " # {'tmean': {'climate_prepend': None,\n", + " # 'gaussian_kernel_size': None},\n", + " # 'ppt': {'climate_prepend': None,\n", + " # 'gaussian_kernel_size': None}},)\n", + " rolling_period=11,\n", + ")\n", + "x_z, x_mean, x_std = utils.zscore_2d(x)\n", + "model = fit.hurdle(x_z, y, log=False)\n", + "yhat = model.predict(x_z)\n", + "prediction = collect.fire(yhat, mtbs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.monthly(prediction, \"prediction\", clim=(0.00005, 0.0015))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.evaluation(mtbs, prediction, \"monthly\", \"prediction\", clim=(0, 0.005))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_mean" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also compare the predicted and real probabilities over time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = mtbs[\"time\"]\n", + "y1 = mtbs[\"monthly\"].mean([\"x\", \"y\"])\n", + "y2 = prediction[\"prediction\"].mean([\"x\", \"y\"])\n", + "\n", + "(\n", + " plot.line(\n", + " x=x,\n", + " y=y1,\n", + " width=900,\n", + " height=200,\n", + " opacity=0.5,\n", + " color=\"rgb(175,91,92)\",\n", + " strokeWidth=2,\n", + " )\n", + " + plot.line(\n", + " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Given that we fit a logistic regression, the natural model performance metric is\n", + "area under the ROC curve, which we computed on the training data (you can use\n", + "the variable `crossval` to repeat the model fit and compute a score on 25% held\n", + "out data).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can compute correlations between model and prediction for annual,\n", + "seasonal, and spatial trends. Note that these were not metrics used to\n", + "explicitly fit the model, but in general a better fitting model ought to\n", + "reproduce at least some of these trends.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of annual trends\")\n", + "np.corrcoef(\n", + " mtbs[\"monthly\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", + " prediction[\"prediction\"].groupby(\"time.year\").mean().mean([\"x\", \"y\"]),\n", + ")[0, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of monthly trends\")\n", + "np.corrcoef(\n", + " mtbs[\"monthly\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", + " prediction[\"prediction\"].groupby(\"time.month\").mean().mean([\"x\", \"y\"]),\n", + ")[0, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"correlation of spatial trends\")\n", + "a = mtbs[\"monthly\"].mean(\"time\").values.flatten()\n", + "b = prediction[\"prediction\"].mean(\"time\").values.flatten()\n", + "inds = ~np.isnan(a) & ~np.isnan(b)\n", + "np.corrcoef(a[inds], b[inds])[0, 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Prediction on future climate data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmip_model = \"CanESM5\"\n", + "scenario = \"ssp245\"\n", + "target = 2025" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmip_models = [\n", + " (\"CanESM5-CanOE\", \"r3i1p2f1\"),\n", + " (\"MIROC-ES2L\", \"r1i1p1f2\"), #\n", + " (\"ACCESS-CM2\", \"r1i1p1f1\"), #\n", + " (\"ACCESS-ESM1-5\", \"r10i1p1f1\"),\n", + " (\"MRI-ESM2-0\", \"r1i1p1f1\"),\n", + " (\"MPI-ESM1-2-LR\", \"r10i1p1f1\"),\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import zarr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "downscaling = \"bias-corrected\"\n", + "sampling = \"monthly\"\n", + "model = \"CanESM5-CanOE\"\n", + "scenario = \"ssp245\"\n", + "member = \"r3i1p2f1\"\n", + "store = \"az\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "downscaling = \"quantile-mapping\"\n", + "sampling = \"monthly\"\n", + "for (model, member) in cmip_models:\n", + " for scenario in [\"historical\", \"ssp245\", \"ssp370\", \"ssp585\"]:\n", + "\n", + " prefix = f\"cmip6/{downscaling}/conus/4000m/{sampling}/{model}.{scenario}.{member}.zarr\".format()\n", + "\n", + " if store == \"az\":\n", + " mapper = zarr.storage.ABSStore(\n", + " \"carbonplan-downscaling\",\n", + " prefix=prefix,\n", + " account_name=\"carbonplan\",\n", + " )\n", + " ds = xr.open_zarr(mapper, consolidated=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = \"ACCESS-CM2\"\n", + "scenario = \"ssp370\" #'r1i1p1f1'\n", + "future_climate = load.cmip(\n", + " store=store,\n", + " model=cmip_model,\n", + " coarsen=16,\n", + " scenario=scenario,\n", + " tlim=(2020, 2100),\n", + " variables=variables,\n", + " mask=mask,\n", + " sampling=\"monthly\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = prepare.fire(future_climate, nftd, eval_only=True)\n", + "x_z = utils.zscore_2d(x, mean=x_mean, std=x_std)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "yhat = model.predict(x_z)\n", + "prediction = collect.fire(yhat, future_climate)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.fire.summary(prediction, \"prediction\", clim=(0.0006, 0.008))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = prediction[\"time\"]\n", + "y2 = prediction[\"prediction\"].mean([\"x\", \"y\"])\n", + "\n", + "(\n", + " plot.line(\n", + " x=x, y=y2, width=900, height=200, color=\"rgb(175,91,92)\", strokeWidth=2\n", + " )\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/fire/fire_model_supersections.ipynb b/notebooks/fire/fire_model_supersections.ipynb index 0edf389..b4be3e1 100644 --- a/notebooks/fire/fire_model_supersections.ipynb +++ b/notebooks/fire/fire_model_supersections.ipynb @@ -1,266 +1,266 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# MTBS Fire Supersection Averages\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), Januyar 13, 2020_\n", - "\n", - "This notebook loads downsampled MTBS fire data and shows avearges within\n", - "supersections.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# MTBS Fire Supersection Averages\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), Januyar 13, 2020_\n", + "\n", + "This notebook loads downsampled MTBS fire data and shows avearges within\n", + "supersections.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "import geopandas as gpd\n", + "import regionmask as rm\n", + "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "from showit import image, tile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from palettable.colorbrewer.sequential import YlOrRd_9\n", + "import matplotlib.pyplot as plt\n", + "\n", + "cmap = YlOrRd_9.mpl_colormap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set variables\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 16\n", + "tlim = (1984, 2018)\n", + "store = \"local\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the mask from `nlcd`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mask = load.mask(store=store, year=2001)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the `mtbs` data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=tlim, mask=mask)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the supersections\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "regions = gpd.read_file(\n", + " \"https://storage.googleapis.com/carbonplan-data/raw/ecoregions/supersections.geojson\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "masks = rm.mask_3D_geopandas(regions, mtbs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create integrated risk function\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.stats import binom\n", + "\n", + "\n", + "def integrated_risk(p):\n", + " return (1 - binom.cdf(0, 100, p)) * 100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make map before 2000\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "before_2000 = (\n", + " mtbs.sel(time=slice(\"1984\", \"2000\")).groupby(\"time.year\").sum().mean(\"year\")\n", + ")\n", + "risks_before_2000 = np.asarray(\n", + " [\n", + " before_2000.monthly.where(masks.sel(region=i))\n", + " .mean([\"x\", \"y\"])\n", + " .values.item()\n", + " for i in masks[\"region\"]\n", + " ]\n", + ")\n", + "regions.to_crs(\"EPSG:5070\").plot(\n", + " integrated_risk(risks_before_2000),\n", + " figsize=[15, 8],\n", + " cmap=cmap,\n", + " edgecolor=[0, 0, 0],\n", + " linewidth=0.3,\n", + " vmin=0,\n", + " vmax=50,\n", + " legend=True,\n", + ")\n", + "plt.axis(\"off\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make map after 2000\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "after_2000 = (\n", + " mtbs.sel(time=slice(\"2001\", \"2018\")).groupby(\"time.year\").sum().mean(\"year\")\n", + ")\n", + "risks_after_2000 = np.asarray(\n", + " [\n", + " after_2000.monthly.where(masks.sel(region=i))\n", + " .mean([\"x\", \"y\"])\n", + " .values.item()\n", + " for i in masks[\"region\"]\n", + " ]\n", + ")\n", + "regions.to_crs(\"EPSG:5070\").plot(\n", + " integrated_risk(risks_after_2000),\n", + " figsize=[15, 8],\n", + " cmap=cmap,\n", + " edgecolor=[0, 0, 0],\n", + " linewidth=0.3,\n", + " vmin=0,\n", + " vmax=50,\n", + " legend=True,\n", + ")\n", + "plt.axis(\"off\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "import geopandas as gpd\n", - "import regionmask as rm\n", - "from carbonplan_forests import load, setup, plot, fit, utils, prepare, collect" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "from showit import image, tile" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from palettable.colorbrewer.sequential import YlOrRd_9\n", - "import matplotlib.pyplot as plt\n", - "\n", - "cmap = YlOrRd_9.mpl_colormap" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Set variables\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 16\n", - "tlim = (1984, 2018)\n", - "store = \"local\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the mask from `nlcd`\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mask = load.mask(store=store, year=2001)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the `mtbs` data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=tlim, mask=mask)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the supersections\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "regions = gpd.read_file(\n", - " \"https://storage.googleapis.com/carbonplan-data/raw/ecoregions/supersections.geojson\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "masks = rm.mask_3D_geopandas(regions, mtbs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create integrated risk function\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from scipy.stats import binom\n", - "\n", - "\n", - "def integrated_risk(p):\n", - " return (1 - binom.cdf(0, 100, p)) * 100" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make map before 2000\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "before_2000 = (\n", - " mtbs.sel(time=slice(\"1984\", \"2000\")).groupby(\"time.year\").sum().mean(\"year\")\n", - ")\n", - "risks_before_2000 = np.asarray(\n", - " [\n", - " before_2000.monthly.where(masks.sel(region=i))\n", - " .mean([\"x\", \"y\"])\n", - " .values.item()\n", - " for i in masks[\"region\"]\n", - " ]\n", - ")\n", - "regions.to_crs(\"EPSG:5070\").plot(\n", - " integrated_risk(risks_before_2000),\n", - " figsize=[15, 8],\n", - " cmap=cmap,\n", - " edgecolor=[0, 0, 0],\n", - " linewidth=0.3,\n", - " vmin=0,\n", - " vmax=50,\n", - " legend=True,\n", - ")\n", - "plt.axis(\"off\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make map after 2000\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "after_2000 = (\n", - " mtbs.sel(time=slice(\"2001\", \"2018\")).groupby(\"time.year\").sum().mean(\"year\")\n", - ")\n", - "risks_after_2000 = np.asarray(\n", - " [\n", - " after_2000.monthly.where(masks.sel(region=i))\n", - " .mean([\"x\", \"y\"])\n", - " .values.item()\n", - " for i in masks[\"region\"]\n", - " ]\n", - ")\n", - "regions.to_crs(\"EPSG:5070\").plot(\n", - " integrated_risk(risks_after_2000),\n", - " figsize=[15, 8],\n", - " cmap=cmap,\n", - " edgecolor=[0, 0, 0],\n", - " linewidth=0.3,\n", - " vmin=0,\n", - " vmax=50,\n", - " legend=True,\n", - ")\n", - "plt.axis(\"off\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/insects/insects_model.ipynb b/notebooks/insects/insects_model.ipynb index 995831e..368093a 100644 --- a/notebooks/insects/insects_model.ipynb +++ b/notebooks/insects/insects_model.ipynb @@ -1,236 +1,236 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Insect Model\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", - "\n", - "This notebook demos insect modelling.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forests import setup, plot, load, fit, utils, prepare" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the raw FIA data grouped by repeated inventories\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "df = load.fia(store=\"local\", states=\"conus\", group_repeats=True)\n", - "df = load.terraclim(\n", - " store=\"local\",\n", - " tlim=(int(df[\"year_0\"].min()), 2020),\n", - " data_vars=[\"ppt\", \"tavg\", \"pdsi\", \"cwd\", \"pet\", \"vpd\"],\n", - " data_aggs=[\"sum\", \"mean\", \"mean\", \"sum\", \"mean\", \"mean\"],\n", - " df=df,\n", - " group_repeats=True,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Prepare data for model fitting\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x, y, meta = prepare.insects(df)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x_z, x_mean, x_std = utils.zscore_2d(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Fit models\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf = meta.copy()\n", - "pf[\"observed\"] = y\n", - "pf[\"predicted\"] = np.NaN\n", - "\n", - "models = {}\n", - "for code in pf[\"type_code\"].unique():\n", - " inds = pf[\"type_code\"] == code\n", - " if (y[inds].sum() > 1) & (y[inds].sum() > 1):\n", - " model = fit.hurdle(x=x_z[inds], y=y[inds])\n", - " models[code] = model\n", - " pf.loc[inds, \"predicted\"] = model.predict(x=x_z[inds])\n", - "\n", - "pf = pf[pf[\"predicted\"] < 1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute simple score metrics (on all data and just on the values where fraction\n", - "insects was positive)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf[[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf[pf[\"observed\"] > 0][[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot observed vs predicted\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.xy(\n", - " x=pf[\"observed\"],\n", - " y=pf[\"predicted\"],\n", - " xlabel=\"measured fraction insects\",\n", - " ylabel=\"predicted fraction insects\",\n", - " xlim=(0, 1),\n", - " ylim=(0, 1),\n", - " width=300,\n", - " height=300,\n", - " opacity=0.1,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of actual fraction insects\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"observed\"],\n", - " cmap=\"reds\",\n", - " clim=(0, 0.2),\n", - " size=5,\n", - " opacity=0.75,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of predicted fraction insects\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"predicted\"],\n", - " clabel=\"fraction_insects * mortality\",\n", - " cmap=\"reds\",\n", - " clim=(0, 0.2),\n", - " size=2,\n", - " opacity=0.75,\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Insect Model\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", + "\n", + "This notebook demos insect modelling.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forests import setup, plot, load, fit, utils, prepare" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the raw FIA data grouped by repeated inventories\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df = load.fia(store=\"local\", states=\"conus\", group_repeats=True)\n", + "df = load.terraclim(\n", + " store=\"local\",\n", + " tlim=(int(df[\"year_0\"].min()), 2020),\n", + " data_vars=[\"ppt\", \"tavg\", \"pdsi\", \"cwd\", \"pet\", \"vpd\"],\n", + " data_aggs=[\"sum\", \"mean\", \"mean\", \"sum\", \"mean\", \"mean\"],\n", + " df=df,\n", + " group_repeats=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Prepare data for model fitting\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x, y, meta = prepare.insects(df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x_z, x_mean, x_std = utils.zscore_2d(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fit models\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf = meta.copy()\n", + "pf[\"observed\"] = y\n", + "pf[\"predicted\"] = np.NaN\n", + "\n", + "models = {}\n", + "for code in pf[\"type_code\"].unique():\n", + " inds = pf[\"type_code\"] == code\n", + " if (y[inds].sum() > 1) & (y[inds].sum() > 1):\n", + " model = fit.hurdle(x=x_z[inds], y=y[inds])\n", + " models[code] = model\n", + " pf.loc[inds, \"predicted\"] = model.predict(x=x_z[inds])\n", + "\n", + "pf = pf[pf[\"predicted\"] < 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute simple score metrics (on all data and just on the values where fraction\n", + "insects was positive)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf[[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf[pf[\"observed\"] > 0][[\"observed\", \"predicted\"]].corr().iloc[0, 1] ** 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot observed vs predicted\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.xy(\n", + " x=pf[\"observed\"],\n", + " y=pf[\"predicted\"],\n", + " xlabel=\"measured fraction insects\",\n", + " ylabel=\"predicted fraction insects\",\n", + " xlim=(0, 1),\n", + " ylim=(0, 1),\n", + " width=300,\n", + " height=300,\n", + " opacity=0.1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of actual fraction insects\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"observed\"],\n", + " cmap=\"reds\",\n", + " clim=(0, 0.2),\n", + " size=5,\n", + " opacity=0.75,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of predicted fraction insects\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"predicted\"],\n", + " clabel=\"fraction_insects * mortality\",\n", + " cmap=\"reds\",\n", + " clim=(0, 0.2),\n", + " size=2,\n", + " opacity=0.75,\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/insects/insects_results.ipynb b/notebooks/insects/insects_results.ipynb index cf79568..1a2c765 100644 --- a/notebooks/insects/insects_results.ipynb +++ b/notebooks/insects/insects_results.ipynb @@ -1,157 +1,157 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# FIA Insects Results\n", - "\n", - "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", - "\n", - "This notebook loads insects results.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# FIA Insects Results\n", + "\n", + "_by Jeremy Freeman (CarbonPlan), October 26, 2020_\n", + "\n", + "This notebook loads insects results.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "from carbonplan_forests import setup, plot, load, fit, utils, prepare" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "setup.plotting(remote=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the raw FIA grouped data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf = pd.read_parquet(\n", + " \"/Users/freeman/github/carbonplan/forests/scripts/data/insects.parquet\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Filter out bad values\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "badinds = (pf[\"historical\"] > 1) | (np.isnan(pf[\"historical\"]))\n", + "for key in pf.columns:\n", + " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", + " badinds = badinds | ((pf[key] > 1) | (np.isnan(pf[key])))\n", + "pf = pf[~badinds]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pf.columns" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a map of the historical prediction (from the period used for fitting)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot.carto(\n", + " lat=pf[\"lat\"],\n", + " lon=pf[\"lon\"],\n", + " color=pf[\"historical\"],\n", + " cmap=\"reds\",\n", + " clim=(0, 0.3),\n", + " size=2,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make a time series of spatially averaged projections\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "years = np.arange(2020, 2120, 20)\n", + "y1 = [pf[\"BCC-CSM2-MR_ssp245_\" + str(year)].mean() for year in years]\n", + "y2 = [pf[\"BCC-CSM2-MR_ssp370_\" + str(year)].mean() for year in years]\n", + "y3 = [pf[\"BCC-CSM2-MR_ssp585_\" + str(year)].mean() for year in years]\n", + "opts = {\n", + " \"color\": \"rgb(164,69,70)\",\n", + " \"ylabel\": \"fraction insects\",\n", + " \"xlabel\": \"year\",\n", + "}\n", + "(\n", + " plot.line(x=years, y=y1, opacity=0.5, ylim=[0, 0.02], **opts)\n", + " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", + " + plot.line(x=years, y=y3, opacity=1, **opts)\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "from carbonplan_forests import setup, plot, load, fit, utils, prepare" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "setup.plotting(remote=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the raw FIA grouped data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf = pd.read_parquet(\n", - " \"/Users/freeman/github/carbonplan/forests/scripts/data/insects.parquet\"\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Filter out bad values\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "badinds = (pf[\"historical\"] > 1) | (np.isnan(pf[\"historical\"]))\n", - "for key in pf.columns:\n", - " if key not in [\"lat\", \"lon\", \"type_code\", \"r2\"]:\n", - " badinds = badinds | ((pf[key] > 1) | (np.isnan(pf[key])))\n", - "pf = pf[~badinds]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pf.columns" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a map of the historical prediction (from the period used for fitting)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot.carto(\n", - " lat=pf[\"lat\"],\n", - " lon=pf[\"lon\"],\n", - " color=pf[\"historical\"],\n", - " cmap=\"reds\",\n", - " clim=(0, 0.3),\n", - " size=2,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make a time series of spatially averaged projections\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "years = np.arange(2020, 2120, 20)\n", - "y1 = [pf[\"BCC-CSM2-MR_ssp245_\" + str(year)].mean() for year in years]\n", - "y2 = [pf[\"BCC-CSM2-MR_ssp370_\" + str(year)].mean() for year in years]\n", - "y3 = [pf[\"BCC-CSM2-MR_ssp585_\" + str(year)].mean() for year in years]\n", - "opts = {\n", - " \"color\": \"rgb(164,69,70)\",\n", - " \"ylabel\": \"fraction insects\",\n", - " \"xlabel\": \"year\",\n", - "}\n", - "(\n", - " plot.line(x=years, y=y1, opacity=0.5, ylim=[0, 0.02], **opts)\n", - " + plot.line(x=years, y=y2, opacity=0.75, **opts)\n", - " + plot.line(x=years, y=y3, opacity=1, **opts)\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/notebooks/paper/Calculations/change-factor-calculations.ipynb b/notebooks/paper/Calculations/change-factor-calculations.ipynb index 053a96b..807c0c7 100644 --- a/notebooks/paper/Calculations/change-factor-calculations.ipynb +++ b/notebooks/paper/Calculations/change-factor-calculations.ipynb @@ -1,147 +1,147 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "incident-friday", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Figure 3: Future projections for each impact\n", - "\n", - "_Author: Oriana Chegwidden_\n", - "\n", - "The methods below conduct the analyses to calculate the fire risk change factors\n", - "included the manuscript <<< insert doi >>>.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "incident-friday", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Figure 3: Future projections for each impact\n", + "\n", + "_Author: Oriana Chegwidden_\n", + "\n", + "The methods below conduct the analyses to calculate the fire risk change factors\n", + "included the manuscript <<< insert doi >>>.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "static-italy", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "import numpy as np\n", + "from carbonplan_forest_risks.utils import get_store" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "driving-print", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "underlying-genealogy", + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " (\n", + " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + " )\n", + " .astype(\"float\")\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "major-compilation", + "metadata": {}, + "source": [ + "# Load in fire data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "armed-penguin", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " get_store(\n", + " \"carbonplan-forests\", \"risks/results/paper/fire_terraclimate_v6.zarr\"\n", + " )\n", + ").load()\n", + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "virgin-scheduling", + "metadata": {}, + "outputs": [], + "source": [ + "ds = (\n", + " xr.open_zarr(\n", + " get_store(\"carbonplan-forests\", \"risks/results/paper/fire_cmip.zarr\")\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .where(fire_mask)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .probability.mean(dim=\"gcm\")\n", + " .compute()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "hybrid-shooting", + "metadata": {}, + "outputs": [], + "source": [ + "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " ts = ds.sel(scenario=scenario)\n", + " hist_slice = slice(1990, 2019)\n", + " fut_slice = slice(2080, 2099)\n", + " change = (\n", + " ts.sel(year=fut_slice).mean(dim=\"year\")\n", + " / ts.sel(year=hist_slice).mean(dim=\"year\").values\n", + " )\n", + " print(\"change factor for {} is {}\".format(scenario, change.values))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "static-italy", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "import numpy as np\n", - "from carbonplan_forest_risks.utils import get_store" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driving-print", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "underlying-genealogy", - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " (\n", - " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - " )\n", - " .astype(\"float\")\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "major-compilation", - "metadata": {}, - "source": [ - "# Load in fire data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "armed-penguin", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " get_store(\n", - " \"carbonplan-forests\", \"risks/results/paper/fire_terraclimate_v6.zarr\"\n", - " )\n", - ").load()\n", - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "virgin-scheduling", - "metadata": {}, - "outputs": [], - "source": [ - "ds = (\n", - " xr.open_zarr(\n", - " get_store(\"carbonplan-forests\", \"risks/results/paper/fire_cmip.zarr\")\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .where(fire_mask)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .probability.mean(dim=\"gcm\")\n", - " .compute()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "hybrid-shooting", - "metadata": {}, - "outputs": [], - "source": [ - "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " ts = ds.sel(scenario=scenario)\n", - " hist_slice = slice(1990, 2019)\n", - " fut_slice = slice(2080, 2099)\n", - " change = (\n", - " ts.sel(year=fut_slice).mean(dim=\"year\")\n", - " / ts.sel(year=hist_slice).mean(dim=\"year\").values\n", - " )\n", - " print(\"change factor for {} is {}\".format(scenario, change.values))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Figure-1/Figure-1.ipynb b/notebooks/paper/Figure-1/Figure-1.ipynb index e9d652e..60ad77f 100644 --- a/notebooks/paper/Figure-1/Figure-1.ipynb +++ b/notebooks/paper/Figure-1/Figure-1.ipynb @@ -1,371 +1,371 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "alternate-hunter", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Figure 1: Observed and modelled forest risks\n", - "\n", - "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", - "\n", - "The methods below conduct the analyses to recreate Figure 1 included the\n", - "manuscript <<< insert doi >>>. They draw from a model data produced by running\n", - "`../../../scripts/fire.py` to create a the file located at\n", - "`az:carbonplan-scratch/data/fire.zarr`.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "alternate-hunter", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Figure 1: Observed and modelled forest risks\n", + "\n", + "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", + "\n", + "The methods below conduct the analyses to recreate Figure 1 included the\n", + "manuscript <<< insert doi >>>. They draw from a model data produced by running\n", + "`../../../scripts/fire.py` to create a the file located at\n", + "`az:carbonplan-scratch/data/fire.zarr`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "impressed-ribbon", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import altair as alt\n", + "import rioxarray\n", + "from carbonplan.data import cat\n", + "from carbonplan_styles.mpl import get_colormap\n", + "import cartopy.crs as ccrs\n", + "import cartopy\n", + "import cartopy.feature as cfeature\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", + "\n", + "from carbonplan_data import utils\n", + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "markdown", + "id": "secondary-falls", + "metadata": {}, + "source": [ + "### Initialize the dictionary where you'll store all of your datasets.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "australian-privacy", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict = {}" + ] + }, + { + "cell_type": "markdown", + "id": "champion-florist", + "metadata": {}, + "source": [ + "## Load in the fire data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "recreational-modern", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "store = \"az\"\n", + "tlim = (\"1984\", \"2018\")" + ] + }, + { + "cell_type": "markdown", + "id": "indirect-remark", + "metadata": {}, + "source": [ + "#### Historical simulation\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "representative-locking", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "smaller-interference", + "metadata": {}, + "outputs": [], + "source": [ + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "alert-laptop", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict[\"Fire_modeled\"] = (\n", + " historical_fire.groupby(\"time.year\").sum().where(fire_mask).mean(dim=\"year\")\n", + ").compute()[\n", + " \"historical\"\n", + "] * 100 # cast into percent" + ] + }, + { + "cell_type": "markdown", + "id": "informal-running", + "metadata": {}, + "source": [ + "#### Observations\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "soviet-contributor", + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + ").astype(\"float\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "internal-adapter", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict[\"Fire_observed\"] = load.mtbs(\n", + " store=store, coarsen=coarsen, tlim=tlim, mask=mask\n", + ")\n", + "ds_dict[\"Fire_observed\"] = (\n", + " ds_dict[\"Fire_observed\"]\n", + " .assign_coords(\n", + " {\"x\": ds_dict[\"Fire_modeled\"].x, \"y\": ds_dict[\"Fire_modeled\"].y}\n", + " )\n", + " .assign_coords(\n", + " {\n", + " \"lat\": ds_dict[\"Fire_modeled\"].lat,\n", + " \"lon\": ds_dict[\"Fire_modeled\"].lon,\n", + " }\n", + " )\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .mean(dim=\"year\")\n", + ").compute()[\n", + " \"monthly\"\n", + "] * 100 # cast into percent" + ] + }, + { + "cell_type": "markdown", + "id": "national-address", + "metadata": {}, + "source": [ + "### Load in insect and drought data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "static-madonna", + "metadata": {}, + "outputs": [], + "source": [ + "base_url_tempate = \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/insects_and_drought_eval/{}\"\n", + "dataset_urls = {\n", + " \"Drought\": {\n", + " \"modeled\": base_url_tempate.format(\n", + " \"Fig1D_DroughtModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tiff\"\n", + " ),\n", + " \"observed\": base_url_tempate.format(\n", + " \"Fig1C_DroughtModel_FIAwide-ObsMort_05-08-2021.tiff\"\n", + " ),\n", + " },\n", + " \"Insects\": {\n", + " \"modeled\": base_url_tempate.format(\n", + " \"Fig1F_InsectModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tiff\"\n", + " ),\n", + " \"observed\": base_url_tempate.format(\n", + " # \"Fig1E_InsectModel_ObservedHistMort_3-30-2021.tif\"\n", + " # \"Fig1E_InsectModel_ObservedHistMort_04-09-2021.tif\"\n", + " \"Fig1E_InsectModel_FIAwide-ObsMort_05-08-2021.tiff\"\n", + " ),\n", + " },\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "seventh-scope", + "metadata": {}, + "source": [ + "## Load in the drought and insect data\n" + ] + }, + { + "cell_type": "markdown", + "id": "overall-cincinnati", + "metadata": {}, + "source": [ + "#### Observations and Historical simulations\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "pressing-rwanda", + "metadata": {}, + "outputs": [], + "source": [ + "for mechanism, url_dict in dataset_urls.items():\n", + " for setup, url in url_dict.items():\n", + " print(url)\n", + " ds_dict[mechanism + \"_\" + setup] = load.tiff(url, mask, coarsen=1) * 100" + ] + }, + { + "cell_type": "markdown", + "id": "charming-spencer", + "metadata": {}, + "source": [ + "## Plot the figure for all three impacts\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "impressive-election", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"Fire\": {\n", + " \"cmap\": get_colormap(\"reds\"),\n", + " \"var_lims\": (0, 0.4),\n", + " \"label\": \"Burn area\\n(%/year)\",\n", + " \"panel\": [\"A\", \"B\"],\n", + " \"cbar_ylocation\": 0.76,\n", + " },\n", + " \"Drought\": {\n", + " \"cmap\": get_colormap(\"pinks\"),\n", + " \"var_lims\": (0, 2),\n", + " \"label\": \"Drought-related\\nmortality (%/year)\",\n", + " \"panel\": [\"C\", \"D\"],\n", + " \"cbar_ylocation\": 0.4,\n", + " },\n", + " \"Insects\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.4),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.04,\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "optical-athens", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "impressive-finnish", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axarr = plt.subplots(\n", + " nrows=3,\n", + " ncols=2,\n", + " figsize=(10, 12),\n", + " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", + ")\n", + "for row, impact in enumerate([\"Fire\", \"Drought\", \"Insects\"]):\n", + " for column, setup in enumerate([\"observed\", \"modeled\"]):\n", + " vmin, vmax = (\n", + " plot_params[impact][\"var_lims\"][0],\n", + " plot_params[impact][\"var_lims\"][1],\n", + " )\n", + " map_plot = ds_dict[\"{}_{}\".format(impact, setup)].plot.imshow(\n", + " ax=axarr[row, column],\n", + " cmap=plot_params[impact][\"cmap\"],\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " add_colorbar=False,\n", + " )\n", + " plot.map_pretty(axarr[row, column], title=setup.capitalize())\n", + " axarr[row, column].text(\n", + " 0.12,\n", + " 1.05,\n", + " plot_params[impact][\"panel\"][column],\n", + " transform=axarr[row, column].transAxes,\n", + " fontsize=18,\n", + " )\n", + " plot.add_colorbar(\n", + " fig,\n", + " to_plot=map_plot,\n", + " y_location=plot_params[impact][\"cbar_ylocation\"],\n", + " vmin=plot_params[impact][\"var_lims\"][0],\n", + " vmax=plot_params[impact][\"var_lims\"][1],\n", + " cbar_label=plot_params[impact][\"label\"],\n", + " )\n", + "plt.tight_layout(pad=-6)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "impressed-ribbon", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import altair as alt\n", - "import rioxarray\n", - "from carbonplan.data import cat\n", - "from carbonplan_styles.mpl import get_colormap\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import cartopy.feature as cfeature\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib\n", - "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", - "\n", - "from carbonplan_data import utils\n", - "alt.data_transformers.disable_max_rows()" - ] - }, - { - "cell_type": "markdown", - "id": "secondary-falls", - "metadata": {}, - "source": [ - "### Initialize the dictionary where you'll store all of your datasets.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "australian-privacy", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict = {}" - ] - }, - { - "cell_type": "markdown", - "id": "champion-florist", - "metadata": {}, - "source": [ - "## Load in the fire data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "recreational-modern", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "store = \"az\"\n", - "tlim = (\"1984\", \"2018\")" - ] - }, - { - "cell_type": "markdown", - "id": "indirect-remark", - "metadata": {}, - "source": [ - "#### Historical simulation\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "representative-locking", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "smaller-interference", - "metadata": {}, - "outputs": [], - "source": [ - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "alert-laptop", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict[\"Fire_modeled\"] = (\n", - " historical_fire.groupby(\"time.year\").sum().where(fire_mask).mean(dim=\"year\")\n", - ").compute()[\n", - " \"historical\"\n", - "] * 100 # cast into percent" - ] - }, - { - "cell_type": "markdown", - "id": "informal-running", - "metadata": {}, - "source": [ - "#### Observations\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "soviet-contributor", - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - ").astype(\"float\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "internal-adapter", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict[\"Fire_observed\"] = load.mtbs(\n", - " store=store, coarsen=coarsen, tlim=tlim, mask=mask\n", - ")\n", - "ds_dict[\"Fire_observed\"] = (\n", - " ds_dict[\"Fire_observed\"]\n", - " .assign_coords(\n", - " {\"x\": ds_dict[\"Fire_modeled\"].x, \"y\": ds_dict[\"Fire_modeled\"].y}\n", - " )\n", - " .assign_coords(\n", - " {\n", - " \"lat\": ds_dict[\"Fire_modeled\"].lat,\n", - " \"lon\": ds_dict[\"Fire_modeled\"].lon,\n", - " }\n", - " )\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .mean(dim=\"year\")\n", - ").compute()[\n", - " \"monthly\"\n", - "] * 100 # cast into percent" - ] - }, - { - "cell_type": "markdown", - "id": "national-address", - "metadata": {}, - "source": [ - "### Load in insect and drought data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "static-madonna", - "metadata": {}, - "outputs": [], - "source": [ - "base_url_tempate = \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/insects_and_drought_eval/{}\"\n", - "dataset_urls = {\n", - " \"Drought\": {\n", - " \"modeled\": base_url_tempate.format(\n", - " \"Fig1D_DroughtModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tiff\"\n", - " ),\n", - " \"observed\": base_url_tempate.format(\n", - " \"Fig1C_DroughtModel_FIAwide-ObsMort_05-08-2021.tiff\"\n", - " ),\n", - " },\n", - " \"Insects\": {\n", - " \"modeled\": base_url_tempate.format(\n", - " \"Fig1F_InsectModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tiff\"\n", - " ),\n", - " \"observed\": base_url_tempate.format(\n", - " # \"Fig1E_InsectModel_ObservedHistMort_3-30-2021.tif\"\n", - " # \"Fig1E_InsectModel_ObservedHistMort_04-09-2021.tif\"\n", - " \"Fig1E_InsectModel_FIAwide-ObsMort_05-08-2021.tiff\"\n", - " ),\n", - " },\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "seventh-scope", - "metadata": {}, - "source": [ - "## Load in the drought and insect data\n" - ] - }, - { - "cell_type": "markdown", - "id": "overall-cincinnati", - "metadata": {}, - "source": [ - "#### Observations and Historical simulations\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "pressing-rwanda", - "metadata": {}, - "outputs": [], - "source": [ - "for mechanism, url_dict in dataset_urls.items():\n", - " for setup, url in url_dict.items():\n", - " print(url)\n", - " ds_dict[mechanism + \"_\" + setup] = load.tiff(url, mask, coarsen=1) * 100" - ] - }, - { - "cell_type": "markdown", - "id": "charming-spencer", - "metadata": {}, - "source": [ - "## Plot the figure for all three impacts\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "impressive-election", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"Fire\": {\n", - " \"cmap\": get_colormap(\"reds\"),\n", - " \"var_lims\": (0, 0.4),\n", - " \"label\": \"Burn area\\n(%/year)\",\n", - " \"panel\": [\"A\", \"B\"],\n", - " \"cbar_ylocation\": 0.76,\n", - " },\n", - " \"Drought\": {\n", - " \"cmap\": get_colormap(\"pinks\"),\n", - " \"var_lims\": (0, 2),\n", - " \"label\": \"Drought-related\\nmortality (%/year)\",\n", - " \"panel\": [\"C\", \"D\"],\n", - " \"cbar_ylocation\": 0.4,\n", - " },\n", - " \"Insects\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.4),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.04,\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "optical-athens", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "impressive-finnish", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axarr = plt.subplots(\n", - " nrows=3,\n", - " ncols=2,\n", - " figsize=(10, 12),\n", - " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", - ")\n", - "for row, impact in enumerate([\"Fire\", \"Drought\", \"Insects\"]):\n", - " for column, setup in enumerate([\"observed\", \"modeled\"]):\n", - " vmin, vmax = (\n", - " plot_params[impact][\"var_lims\"][0],\n", - " plot_params[impact][\"var_lims\"][1],\n", - " )\n", - " map_plot = ds_dict[\"{}_{}\".format(impact, setup)].plot.imshow(\n", - " ax=axarr[row, column],\n", - " cmap=plot_params[impact][\"cmap\"],\n", - " vmin=vmin,\n", - " vmax=vmax,\n", - " add_colorbar=False,\n", - " )\n", - " plot.map_pretty(axarr[row, column], title=setup.capitalize())\n", - " axarr[row, column].text(\n", - " 0.12,\n", - " 1.05,\n", - " plot_params[impact][\"panel\"][column],\n", - " transform=axarr[row, column].transAxes,\n", - " fontsize=18,\n", - " )\n", - " plot.add_colorbar(\n", - " fig,\n", - " to_plot=map_plot,\n", - " y_location=plot_params[impact][\"cbar_ylocation\"],\n", - " vmin=plot_params[impact][\"var_lims\"][0],\n", - " vmax=plot_params[impact][\"var_lims\"][1],\n", - " cbar_label=plot_params[impact][\"label\"],\n", - " )\n", - "plt.tight_layout(pad=-6)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Figure-2/Figure-2.ipynb b/notebooks/paper/Figure-2/Figure-2.ipynb index 27d2431..b6ce1ef 100644 --- a/notebooks/paper/Figure-2/Figure-2.ipynb +++ b/notebooks/paper/Figure-2/Figure-2.ipynb @@ -1,354 +1,354 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "dietary-return", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Figure 2: Future projections for each impact\n", - "\n", - "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", - "\n", - "The methods below conduct the analyses to recreate Figure 2 included the\n", - "manuscript <<< insert doi >>>. Future risks for fire. We show median changes in\n", - "risk with respect to the historic.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "dietary-return", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Figure 2: Future projections for each impact\n", + "\n", + "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", + "\n", + "The methods below conduct the analyses to recreate Figure 2 included the\n", + "manuscript <<< insert doi >>>. Future risks for fire. We show median changes in\n", + "risk with respect to the historic.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "naughty-madison", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import rioxarray\n", + "from carbonplan.data import cat\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import scipy as sp\n", + "import geopandas as gpd\n", + "import regionmask as rm\n", + "import altair as alt\n", + "alt.themes.enable(\"carbonplan_light\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "careful-boston", + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "defensive-maldives", + "metadata": {}, + "outputs": [], + "source": [ + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "wicked-father", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dutch-acrobat", + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " (\n", + " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + " )\n", + " .astype(\"float\")\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "august-namibia", + "metadata": {}, + "outputs": [], + "source": [ + "# Load in fire data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "clean-glenn", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ")\n", + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "horizontal-cinema", + "metadata": {}, + "outputs": [], + "source": [ + "ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .where(fire_mask)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .compute()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "conscious-career", + "metadata": {}, + "outputs": [], + "source": [ + "historical_annual = (\n", + " historical_fire.assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .compute()\n", + ") * 100 # cast to percent" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "surprising-discovery", + "metadata": {}, + "outputs": [], + "source": [ + "future_ts = {}\n", + "future_ts[\"fire\"] = (\n", + " ds.mean(dim=[\"x\", \"y\"]).rolling(dim={\"year\": 10}).mean().compute()\n", + ") * 100 # cast to percent" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "unexpected-stanford", + "metadata": {}, + "outputs": [], + "source": [ + "gcms = [\n", + " (\"ACCESS-CM2\", \"r1i1p1f1\"),\n", + " (\"ACCESS-ESM1-5\", \"r10i1p1f1\"),\n", + " (\"MRI-ESM2-0\", \"r1i1p1f1\"),\n", + " (\"MIROC-ES2L\", \"r1i1p1f2\"),\n", + " (\"MPI-ESM1-2-LR\", \"r10i1p1f1\"),\n", + " (\"CanESM5-CanOE\", \"r3i1p2f1\"),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "downtown-feature", + "metadata": {}, + "source": [ + "### Load in insect and drought data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "manual-candle", + "metadata": {}, + "outputs": [], + "source": [ + "historical_ts = {}\n", + "for impact in [\"drought\", \"insects\"]:\n", + " historical_ts[impact] = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_terraclimate.zarr\".format(\n", + " impact\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1995, 2020, 10)})\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .compute()\n", + " )\n", + " future_ts[impact] = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", + " impact\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .compute()\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "inclusive-roulette", + "metadata": {}, + "source": [ + "## Plot the figure\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "miniature-valley", + "metadata": {}, + "outputs": [], + "source": [ + "gcms = [\n", + " (\"MRI-ESM2-0\", (0, 0)),\n", + " (\"MIROC-ES2L\", (1, 0)),\n", + " (\"MPI-ESM1-2-LR\", (2, 0)),\n", + " (\"ACCESS-ESM1-5\", (3, 0)),\n", + " (\"ACCESS-CM2\", (4, 0)),\n", + " (\"CanESM5-CanOE\", (5, 0)),\n", + "]\n", + "scenario_colors = {\n", + " \"ssp245\": \"#59A82F\",\n", + " \"ssp370\": \"#D8B525\",\n", + " \"ssp585\": \"#D83232\",\n", + "}\n", + "scenario_colors_light = {\n", + " \"ssp245\": \"#DEEED5\",\n", + " \"ssp370\": \"#F7F0D3\",\n", + " \"ssp585\": \"#F7D6D6\",\n", + "}\n", + "titles = {\n", + " \"fire\": \"Burn area\\n(%/year)\",\n", + " \"drought\": \"Drought-related\\nmortality (%/year)\",\n", + " \"insects\": \"Insect-related\\nmortality (%/year)\",\n", + "}\n", + "ylims = {\n", + " \"fire\": (0, 1.5),\n", + " \"drought\": (0, 3),\n", + " \"insects\": (0, 0.5),\n", + "}\n", + "ssp_rename = {\"ssp245\": \"SSP2-4.5\", \"ssp370\": \"SSP3-7.0\", \"ssp585\": \"SSP5-8.5\"}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "august-audio", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})\n", + "\n", + "fig, axarr = plt.subplots(nrows=3, figsize=(6, 10), sharex=True)\n", + "\n", + "## Fire\n", + "\n", + "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " for (gcm, location) in gcms:\n", + " future_ts[\"fire\"].probability.sel(gcm=gcm, scenario=scenario).sel(\n", + " year=slice(\"1970\", \"2099\")\n", + " ).plot(ax=axarr[0], color=scenario_colors_light[scenario], alpha=1)\n", + "\n", + "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " future_ts[\"fire\"].probability.sel(scenario=scenario).mean(dim=\"gcm\").plot(\n", + " ax=axarr[0], color=scenario_colors[scenario], label=ssp_rename[scenario]\n", + " )\n", + "historical_annual[\"historical\"].rolling(dim={\"year\": 10}).mean().plot(\n", + " ax=axarr[0], color=\"k\", linestyle=\"-\", label=\"Historical\"\n", + ")\n", + "\n", + "axarr[0].legend()\n", + "## Drought and insects\n", + "for i, impact in enumerate([\"drought\", \"insects\"]):\n", + " historical_ts[impact].probability.plot(\n", + " ax=axarr[i + 1], color=\"k\", zorder=50\n", + " )\n", + "\n", + " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " for (gcm, location) in gcms:\n", + " future_ts[impact].probability.sel(scenario=scenario, gcm=gcm).sel(\n", + " year=slice(1970, 2099)\n", + " ).plot(\n", + " ax=axarr[i + 1],\n", + " color=scenario_colors_light[scenario],\n", + " )\n", + "\n", + " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " future_ts[impact].probability.sel(scenario=scenario).mean(\n", + " dim=\"gcm\"\n", + " ).plot(\n", + " ax=axarr[i + 1],\n", + " color=scenario_colors[scenario],\n", + " label=ssp_rename[scenario],\n", + " )\n", + "for i, (impact, title) in enumerate(titles.items()):\n", + " axarr[i].set_ylabel(title)\n", + " axarr[i].set_xlabel(\"\")\n", + " axarr[i].set_ylim(ylims[impact])\n", + " axarr[i].set_title(\"\")\n", + "plt.tight_layout()\n", + "# for format_string in [\"svg\", \"png\"]:\n", + "# plt.savefig(\"Figure-2.\" + format_string, format=format_string)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "naughty-madison", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import rioxarray\n", - "from carbonplan.data import cat\n", - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "import scipy as sp\n", - "import geopandas as gpd\n", - "import regionmask as rm\n", - "import altair as alt\n", - "alt.themes.enable(\"carbonplan_light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "careful-boston", - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "defensive-maldives", - "metadata": {}, - "outputs": [], - "source": [ - "alt.data_transformers.disable_max_rows()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "wicked-father", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dutch-acrobat", - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " (\n", - " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - " )\n", - " .astype(\"float\")\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "august-namibia", - "metadata": {}, - "outputs": [], - "source": [ - "# Load in fire data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "clean-glenn", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ")\n", - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "horizontal-cinema", - "metadata": {}, - "outputs": [], - "source": [ - "ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .where(fire_mask)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .compute()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "conscious-career", - "metadata": {}, - "outputs": [], - "source": [ - "historical_annual = (\n", - " historical_fire.assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .compute()\n", - ") * 100 # cast to percent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "surprising-discovery", - "metadata": {}, - "outputs": [], - "source": [ - "future_ts = {}\n", - "future_ts[\"fire\"] = (\n", - " ds.mean(dim=[\"x\", \"y\"]).rolling(dim={\"year\": 10}).mean().compute()\n", - ") * 100 # cast to percent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "unexpected-stanford", - "metadata": {}, - "outputs": [], - "source": [ - "gcms = [\n", - " (\"ACCESS-CM2\", \"r1i1p1f1\"),\n", - " (\"ACCESS-ESM1-5\", \"r10i1p1f1\"),\n", - " (\"MRI-ESM2-0\", \"r1i1p1f1\"),\n", - " (\"MIROC-ES2L\", \"r1i1p1f2\"),\n", - " (\"MPI-ESM1-2-LR\", \"r10i1p1f1\"),\n", - " (\"CanESM5-CanOE\", \"r3i1p2f1\"),\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "downtown-feature", - "metadata": {}, - "source": [ - "### Load in insect and drought data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "manual-candle", - "metadata": {}, - "outputs": [], - "source": [ - "historical_ts = {}\n", - "for impact in [\"drought\", \"insects\"]:\n", - " historical_ts[impact] = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_terraclimate.zarr\".format(\n", - " impact\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1995, 2020, 10)})\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .compute()\n", - " )\n", - " future_ts[impact] = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", - " impact\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .compute()\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "inclusive-roulette", - "metadata": {}, - "source": [ - "## Plot the figure\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "miniature-valley", - "metadata": {}, - "outputs": [], - "source": [ - "gcms = [\n", - " (\"MRI-ESM2-0\", (0, 0)),\n", - " (\"MIROC-ES2L\", (1, 0)),\n", - " (\"MPI-ESM1-2-LR\", (2, 0)),\n", - " (\"ACCESS-ESM1-5\", (3, 0)),\n", - " (\"ACCESS-CM2\", (4, 0)),\n", - " (\"CanESM5-CanOE\", (5, 0)),\n", - "]\n", - "scenario_colors = {\n", - " \"ssp245\": \"#59A82F\",\n", - " \"ssp370\": \"#D8B525\",\n", - " \"ssp585\": \"#D83232\",\n", - "}\n", - "scenario_colors_light = {\n", - " \"ssp245\": \"#DEEED5\",\n", - " \"ssp370\": \"#F7F0D3\",\n", - " \"ssp585\": \"#F7D6D6\",\n", - "}\n", - "titles = {\n", - " \"fire\": \"Burn area\\n(%/year)\",\n", - " \"drought\": \"Drought-related\\nmortality (%/year)\",\n", - " \"insects\": \"Insect-related\\nmortality (%/year)\",\n", - "}\n", - "ylims = {\n", - " \"fire\": (0, 1.5),\n", - " \"drought\": (0, 3),\n", - " \"insects\": (0, 0.5),\n", - "}\n", - "ssp_rename = {\"ssp245\": \"SSP2-4.5\", \"ssp370\": \"SSP3-7.0\", \"ssp585\": \"SSP5-8.5\"}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "august-audio", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})\n", - "\n", - "fig, axarr = plt.subplots(nrows=3, figsize=(6, 10), sharex=True)\n", - "\n", - "## Fire\n", - "\n", - "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " for (gcm, location) in gcms:\n", - " future_ts[\"fire\"].probability.sel(gcm=gcm, scenario=scenario).sel(\n", - " year=slice(\"1970\", \"2099\")\n", - " ).plot(ax=axarr[0], color=scenario_colors_light[scenario], alpha=1)\n", - "\n", - "for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " future_ts[\"fire\"].probability.sel(scenario=scenario).mean(dim=\"gcm\").plot(\n", - " ax=axarr[0], color=scenario_colors[scenario], label=ssp_rename[scenario]\n", - " )\n", - "historical_annual[\"historical\"].rolling(dim={\"year\": 10}).mean().plot(\n", - " ax=axarr[0], color=\"k\", linestyle=\"-\", label=\"Historical\"\n", - ")\n", - "\n", - "axarr[0].legend()\n", - "## Drought and insects\n", - "for i, impact in enumerate([\"drought\", \"insects\"]):\n", - " historical_ts[impact].probability.plot(\n", - " ax=axarr[i + 1], color=\"k\", zorder=50\n", - " )\n", - "\n", - " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " for (gcm, location) in gcms:\n", - " future_ts[impact].probability.sel(scenario=scenario, gcm=gcm).sel(\n", - " year=slice(1970, 2099)\n", - " ).plot(\n", - " ax=axarr[i + 1],\n", - " color=scenario_colors_light[scenario],\n", - " )\n", - "\n", - " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " future_ts[impact].probability.sel(scenario=scenario).mean(\n", - " dim=\"gcm\"\n", - " ).plot(\n", - " ax=axarr[i + 1],\n", - " color=scenario_colors[scenario],\n", - " label=ssp_rename[scenario],\n", - " )\n", - "for i, (impact, title) in enumerate(titles.items()):\n", - " axarr[i].set_ylabel(title)\n", - " axarr[i].set_xlabel(\"\")\n", - " axarr[i].set_ylim(ylims[impact])\n", - " axarr[i].set_title(\"\")\n", - "plt.tight_layout()\n", - "# for format_string in [\"svg\", \"png\"]:\n", - "# plt.savefig(\"Figure-2.\" + format_string, format=format_string)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Figure-3/Figure-3.ipynb b/notebooks/paper/Figure-3/Figure-3.ipynb index bc6bc05..a71f798 100644 --- a/notebooks/paper/Figure-3/Figure-3.ipynb +++ b/notebooks/paper/Figure-3/Figure-3.ipynb @@ -1,331 +1,331 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "sustained-barrier", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import altair as alt\n", - "from carbonplan.data import cat\n", - "import rioxarray\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import cartopy.feature as cfeature\n", - "import matplotlib.pyplot as plt\n", - "from carbonplan_styles.mpl import get_colormap\n", - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "import matplotlib\n", - "from carbonplan_data import utils\n", - "\n", - "alt.themes.enable(\"carbonplan_light\")" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "sustained-barrier", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import altair as alt\n", + "from carbonplan.data import cat\n", + "import rioxarray\n", + "import cartopy.crs as ccrs\n", + "import cartopy\n", + "import cartopy.feature as cfeature\n", + "import matplotlib.pyplot as plt\n", + "from carbonplan_styles.mpl import get_colormap\n", + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "import matplotlib\n", + "from carbonplan_data import utils\n", + "\n", + "alt.themes.enable(\"carbonplan_light\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bizarre-semester", + "metadata": {}, + "outputs": [], + "source": [ + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "administrative-tours", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "mask = (\n", + " (\n", + " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + " )\n", + " .astype(\"float\")\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "black-michigan", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ").load()\n", + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "legendary-module", + "metadata": {}, + "outputs": [], + "source": [ + "ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .where(fire_mask)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .compute()\n", + ") * 100 # scale to percent" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "invalid-navigator", + "metadata": {}, + "outputs": [], + "source": [ + "future_maps = {}\n", + "future_maps[\"Fire\"] = (\n", + " ds.sel(year=slice(\"2080\", \"2099\")).mean(dim=\"year\").compute()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "focused-quest", + "metadata": {}, + "source": [ + "### load insects and drought\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "final-bailey", + "metadata": {}, + "outputs": [], + "source": [ + "for impact in [\"drought\", \"insects\"]:\n", + " future_maps[impact.capitalize()] = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", + " impact\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", + " .sel(year=slice(2080, 2099))\n", + " .mean(dim=\"year\")\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "photographic-label", + "metadata": {}, + "outputs": [], + "source": [ + "gcms = [\n", + " (\"MRI-ESM2-0\", (0, 0)),\n", + " (\"MIROC-ES2L\", (1, 0)),\n", + " (\"MPI-ESM1-2-LR\", (2, 0)),\n", + " (\"ACCESS-ESM1-5\", (3, 0)),\n", + " (\"ACCESS-CM2\", (4, 0)),\n", + " (\"CanESM5-CanOE\", (5, 0)),\n", + "]\n", + "titles = [\n", + " \"Burn area\\n[%/year]\",\n", + " \"Drought mortality\\n[]\",\n", + " \"Biotic agent mortality\\n[]\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "deadly-newman", + "metadata": {}, + "outputs": [], + "source": [ + "scenario_dict = {}\n", + "for risk in [\"Fire\", \"Insects\", \"Drought\"]:\n", + " scenario_dict[risk] = {}\n", + " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", + " scenario_dict[risk][scenario] = (\n", + " future_maps[risk]\n", + " .probability.sel(scenario=scenario)\n", + " .mean(dim=\"gcm\")\n", + " .drop(\"scenario\")\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "absent-concern", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"Fire\": {\n", + " \"cmap\": get_colormap(\"reds\"),\n", + " \"var_lims\": (0, 3),\n", + " \"label\": \"Burn area\\n(%/year)\",\n", + " \"panel\": [\"A\", \"B\", \"C\"],\n", + " \"cbar_ylocation\": 0.76,\n", + " },\n", + " \"Drought\": {\n", + " \"cmap\": get_colormap(\"pinks\"),\n", + " \"var_lims\": (0, 4),\n", + " \"label\": \"Drought-related\\nmortality (%/year)\",\n", + " \"panel\": [\"D\", \"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.4,\n", + " },\n", + " \"Insects\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.8),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"G\", \"H\", \"I\"],\n", + " \"cbar_ylocation\": 0.04,\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sweet-arena", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "critical-indicator", + "metadata": {}, + "outputs": [], + "source": [ + "ssp_rename = {\"ssp245\": \"SSP2-4.5\", \"ssp370\": \"SSP3-7.0\", \"ssp585\": \"SSP5-8.5\"}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "headed-employee", + "metadata": {}, + "outputs": [], + "source": [ + "state_borders, us_border = plot.cartopy_borders()\n", + "fig, axarr = plt.subplots(\n", + " nrows=3,\n", + " ncols=3,\n", + " figsize=(15, 12),\n", + " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", + ")\n", + "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", + "\n", + "for row, impact in enumerate([\"Fire\", \"Drought\", \"Insects\"]):\n", + " for column, scenario in enumerate([\"ssp245\", \"ssp370\", \"ssp585\"]):\n", + " vmin, vmax = (\n", + " plot_params[impact][\"var_lims\"][0],\n", + " plot_params[impact][\"var_lims\"][1],\n", + " )\n", + "\n", + " map_plot = scenario_dict[impact][scenario].plot.imshow(\n", + " ax=axarr[row, column],\n", + " cmap=plot_params[impact][\"cmap\"],\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " add_colorbar=False,\n", + " )\n", + "\n", + " plot.map_pretty(axarr[row, column], title=ssp_rename[scenario])\n", + " axarr[row, column].text(\n", + " 0.12,\n", + " 1.05,\n", + " plot_params[impact][\"panel\"][column],\n", + " transform=axarr[row, column].transAxes,\n", + " fontsize=18,\n", + " )\n", + " cax = fig.add_axes(\n", + " [1.03, plot_params[impact][\"cbar_ylocation\"], 0.018, 0.12]\n", + " )\n", + " cax.text(\n", + " 0.5,\n", + " -0.12,\n", + " plot_params[impact][\"var_lims\"][0],\n", + " transform=cax.transAxes,\n", + " horizontalalignment=\"center\",\n", + " )\n", + " cax.text(\n", + " 0.5,\n", + " 1.05,\n", + " plot_params[impact][\"var_lims\"][1],\n", + " transform=cax.transAxes,\n", + " horizontalalignment=\"center\",\n", + " )\n", + " cax.text(\n", + " 1.8,\n", + " 0.5,\n", + " plot_params[impact][\"label\"],\n", + " transform=cax.transAxes,\n", + " verticalalignment=\"center\",\n", + " multialignment=\"center\",\n", + " rotation=-90,\n", + " )\n", + " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", + " cbar.outline.set_visible(False)\n", + " cbar.set_ticks([])\n", + "plt.tight_layout(pad=-6)\n", + "# for format_string in [\"svg\", \"png\"]:\n", + "# fig.savefig(\n", + "# \"Figure-3.\" + format_string, format=format_string, bbox_inches=\"tight\"\n", + "# )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "bizarre-semester", - "metadata": {}, - "outputs": [], - "source": [ - "alt.data_transformers.disable_max_rows()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "administrative-tours", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "mask = (\n", - " (\n", - " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - " )\n", - " .astype(\"float\")\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "black-michigan", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ").load()\n", - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "legendary-module", - "metadata": {}, - "outputs": [], - "source": [ - "ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .where(fire_mask)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .compute()\n", - ") * 100 # scale to percent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "invalid-navigator", - "metadata": {}, - "outputs": [], - "source": [ - "future_maps = {}\n", - "future_maps[\"Fire\"] = (\n", - " ds.sel(year=slice(\"2080\", \"2099\")).mean(dim=\"year\").compute()\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "focused-quest", - "metadata": {}, - "source": [ - "### load insects and drought\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "final-bailey", - "metadata": {}, - "outputs": [], - "source": [ - "for impact in [\"drought\", \"insects\"]:\n", - " future_maps[impact.capitalize()] = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", - " impact\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", - " .sel(year=slice(2080, 2099))\n", - " .mean(dim=\"year\")\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "photographic-label", - "metadata": {}, - "outputs": [], - "source": [ - "gcms = [\n", - " (\"MRI-ESM2-0\", (0, 0)),\n", - " (\"MIROC-ES2L\", (1, 0)),\n", - " (\"MPI-ESM1-2-LR\", (2, 0)),\n", - " (\"ACCESS-ESM1-5\", (3, 0)),\n", - " (\"ACCESS-CM2\", (4, 0)),\n", - " (\"CanESM5-CanOE\", (5, 0)),\n", - "]\n", - "titles = [\n", - " \"Burn area\\n[%/year]\",\n", - " \"Drought mortality\\n[]\",\n", - " \"Biotic agent mortality\\n[]\",\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "deadly-newman", - "metadata": {}, - "outputs": [], - "source": [ - "scenario_dict = {}\n", - "for risk in [\"Fire\", \"Insects\", \"Drought\"]:\n", - " scenario_dict[risk] = {}\n", - " for scenario in [\"ssp245\", \"ssp370\", \"ssp585\"]:\n", - " scenario_dict[risk][scenario] = (\n", - " future_maps[risk]\n", - " .probability.sel(scenario=scenario)\n", - " .mean(dim=\"gcm\")\n", - " .drop(\"scenario\")\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "absent-concern", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"Fire\": {\n", - " \"cmap\": get_colormap(\"reds\"),\n", - " \"var_lims\": (0, 3),\n", - " \"label\": \"Burn area\\n(%/year)\",\n", - " \"panel\": [\"A\", \"B\", \"C\"],\n", - " \"cbar_ylocation\": 0.76,\n", - " },\n", - " \"Drought\": {\n", - " \"cmap\": get_colormap(\"pinks\"),\n", - " \"var_lims\": (0, 4),\n", - " \"label\": \"Drought-related\\nmortality (%/year)\",\n", - " \"panel\": [\"D\", \"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.4,\n", - " },\n", - " \"Insects\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.8),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"G\", \"H\", \"I\"],\n", - " \"cbar_ylocation\": 0.04,\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sweet-arena", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "critical-indicator", - "metadata": {}, - "outputs": [], - "source": [ - "ssp_rename = {\"ssp245\": \"SSP2-4.5\", \"ssp370\": \"SSP3-7.0\", \"ssp585\": \"SSP5-8.5\"}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "headed-employee", - "metadata": {}, - "outputs": [], - "source": [ - "state_borders, us_border = plot.cartopy_borders()\n", - "fig, axarr = plt.subplots(\n", - " nrows=3,\n", - " ncols=3,\n", - " figsize=(15, 12),\n", - " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", - ")\n", - "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", - "\n", - "for row, impact in enumerate([\"Fire\", \"Drought\", \"Insects\"]):\n", - " for column, scenario in enumerate([\"ssp245\", \"ssp370\", \"ssp585\"]):\n", - " vmin, vmax = (\n", - " plot_params[impact][\"var_lims\"][0],\n", - " plot_params[impact][\"var_lims\"][1],\n", - " )\n", - "\n", - " map_plot = scenario_dict[impact][scenario].plot.imshow(\n", - " ax=axarr[row, column],\n", - " cmap=plot_params[impact][\"cmap\"],\n", - " vmin=vmin,\n", - " vmax=vmax,\n", - " add_colorbar=False,\n", - " )\n", - "\n", - " plot.map_pretty(axarr[row, column], title=ssp_rename[scenario])\n", - " axarr[row, column].text(\n", - " 0.12,\n", - " 1.05,\n", - " plot_params[impact][\"panel\"][column],\n", - " transform=axarr[row, column].transAxes,\n", - " fontsize=18,\n", - " )\n", - " cax = fig.add_axes(\n", - " [1.03, plot_params[impact][\"cbar_ylocation\"], 0.018, 0.12]\n", - " )\n", - " cax.text(\n", - " 0.5,\n", - " -0.12,\n", - " plot_params[impact][\"var_lims\"][0],\n", - " transform=cax.transAxes,\n", - " horizontalalignment=\"center\",\n", - " )\n", - " cax.text(\n", - " 0.5,\n", - " 1.05,\n", - " plot_params[impact][\"var_lims\"][1],\n", - " transform=cax.transAxes,\n", - " horizontalalignment=\"center\",\n", - " )\n", - " cax.text(\n", - " 1.8,\n", - " 0.5,\n", - " plot_params[impact][\"label\"],\n", - " transform=cax.transAxes,\n", - " verticalalignment=\"center\",\n", - " multialignment=\"center\",\n", - " rotation=-90,\n", - " )\n", - " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", - " cbar.outline.set_visible(False)\n", - " cbar.set_ticks([])\n", - "plt.tight_layout(pad=-6)\n", - "# for format_string in [\"svg\", \"png\"]:\n", - "# fig.savefig(\n", - "# \"Figure-3.\" + format_string, format=format_string, bbox_inches=\"tight\"\n", - "# )" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Figure-4/Figure-4.ipynb b/notebooks/paper/Figure-4/Figure-4.ipynb index 1ffe63c..4e020ef 100644 --- a/notebooks/paper/Figure-4/Figure-4.ipynb +++ b/notebooks/paper/Figure-4/Figure-4.ipynb @@ -1,292 +1,292 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "surface-novel", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Figure 4: Future projections for each impact\n", - "\n", - "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", - "\n", - "The methods below conduct the analyses to recreate Figure 4 included the\n", - "manuscript <<< insert doi >>>. Future risks for fire. We show median changes in\n", - "risk with respect to the historic.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "surface-novel", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Figure 4: Future projections for each impact\n", + "\n", + "_Authors: Oriana Chegwidden and Jeremy Freeman_\n", + "\n", + "The methods below conduct the analyses to recreate Figure 4 included the\n", + "manuscript <<< insert doi >>>. Future risks for fire. We show median changes in\n", + "risk with respect to the historic.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "worth-convergence", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import rioxarray\n", + "from carbonplan.data import cat\n", + "import matplotlib.gridspec as gridspec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "optical-vinyl", + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "forbidden-claim", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "arabic-intelligence", + "metadata": {}, + "outputs": [], + "source": [ + "mask = (\n", + " (\n", + " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + " )\n", + " .astype(\"float\")\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "advised-biodiversity", + "metadata": {}, + "outputs": [], + "source": [ + "region_bboxes = {\n", + " \"PNW\": {\"x\": slice(-2.5e6, -1e6), \"y\": slice(3.5e6, 2.4e6)},\n", + " \"Southwest\": {\"x\": slice(-1.8e6, -0.9e6), \"y\": slice(1.8e6, 0.9e6)},\n", + " \"California\": {\"x\": slice(-2.3e6, -1.8e6), \"y\": slice(2.5e6, 1.2e6)},\n", + " \"Southeast\": {\"x\": slice(0.6e6, 1.8e6), \"y\": slice(1.6e6, 0.3e6)},\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "heated-revelation", + "metadata": {}, + "source": [ + "# Load in fire data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "loaded-supervisor", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ").load()\n", + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "little-injury", + "metadata": {}, + "outputs": [], + "source": [ + "ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .where(fire_mask)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .compute()\n", + ") * 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "further-smile", + "metadata": {}, + "outputs": [], + "source": [ + "historical_annual = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .compute()\n", + ") * 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "modified-entrepreneur", + "metadata": {}, + "outputs": [], + "source": [ + "def load_regional_results(ds, timestep, region):\n", + " selected = ds.sel(**region)\n", + " if timestep == \"annual\":\n", + " return (\n", + " selected.mean(dim=[\"x\", \"y\"])\n", + " .rolling(dim={\"year\": 10})\n", + " .mean()\n", + " .compute()\n", + " )\n", + " elif timestep == \"decadal\":\n", + " return selected.mean(dim=[\"x\", \"y\"]).compute()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "settled-plaza", + "metadata": {}, + "outputs": [], + "source": [ + "results_dict = {\"fire\": {}, \"insects\": {}, \"drought\": {}}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "clear-commodity", + "metadata": {}, + "outputs": [], + "source": [ + "for region, bbox in region_bboxes.items():\n", + " results_dict[\"fire\"][region] = {}\n", + " results_dict[\"fire\"][region][\"future\"] = (\n", + " load_regional_results(ds, \"annual\", region_bboxes[region])\n", + " .sel(year=slice(1970, 2099))\n", + " .probability\n", + " )\n", + " results_dict[\"fire\"][region][\"historical\"] = load_regional_results(\n", + " historical_annual, \"annual\", region_bboxes[region]\n", + " ).historical" + ] + }, + { + "cell_type": "markdown", + "id": "combined-millennium", + "metadata": {}, + "source": [ + "### Load in insect and drought data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "comic-third", + "metadata": {}, + "outputs": [], + "source": [ + "for impact in [\"insects\", \"drought\"]:\n", + " historical_ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_terraclimate.zarr\".format(\n", + " impact\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1995, 2020, 10)})\n", + " .compute()\n", + " )\n", + " future_ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", + " impact\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", + " .compute()\n", + " )\n", + "\n", + " for region, bbox in region_bboxes.items():\n", + " results_dict[impact][region] = {}\n", + " results_dict[impact][region][\"future\"] = (\n", + " load_regional_results(future_ds, \"decadal\", region_bboxes[region])\n", + " .sel(year=slice(1970, 2099))\n", + " .probability\n", + " )\n", + " results_dict[impact][region][\"historical\"] = load_regional_results(\n", + " historical_ds, \"decadal\", region_bboxes[region]\n", + " ).probability" + ] + }, + { + "cell_type": "markdown", + "id": "decent-baltimore", + "metadata": {}, + "source": [ + "## Plot the figure\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "obvious-thursday", + "metadata": {}, + "outputs": [], + "source": [ + "plot.multipanel_ts(results_dict, region_bboxes, \"Figure-4\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "worth-convergence", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "%matplotlib inline\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import rioxarray\n", - "from carbonplan.data import cat\n", - "import matplotlib.gridspec as gridspec" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "optical-vinyl", - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "forbidden-claim", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "arabic-intelligence", - "metadata": {}, - "outputs": [], - "source": [ - "mask = (\n", - " (\n", - " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - " )\n", - " .astype(\"float\")\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "advised-biodiversity", - "metadata": {}, - "outputs": [], - "source": [ - "region_bboxes = {\n", - " \"PNW\": {\"x\": slice(-2.5e6, -1e6), \"y\": slice(3.5e6, 2.4e6)},\n", - " \"Southwest\": {\"x\": slice(-1.8e6, -0.9e6), \"y\": slice(1.8e6, 0.9e6)},\n", - " \"California\": {\"x\": slice(-2.3e6, -1.8e6), \"y\": slice(2.5e6, 1.2e6)},\n", - " \"Southeast\": {\"x\": slice(0.6e6, 1.8e6), \"y\": slice(1.6e6, 0.3e6)},\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "heated-revelation", - "metadata": {}, - "source": [ - "# Load in fire data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "loaded-supervisor", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ").load()\n", - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "little-injury", - "metadata": {}, - "outputs": [], - "source": [ - "ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .where(fire_mask)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .compute()\n", - ") * 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "further-smile", - "metadata": {}, - "outputs": [], - "source": [ - "historical_annual = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .compute()\n", - ") * 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "modified-entrepreneur", - "metadata": {}, - "outputs": [], - "source": [ - "def load_regional_results(ds, timestep, region):\n", - " selected = ds.sel(**region)\n", - " if timestep == \"annual\":\n", - " return (\n", - " selected.mean(dim=[\"x\", \"y\"])\n", - " .rolling(dim={\"year\": 10})\n", - " .mean()\n", - " .compute()\n", - " )\n", - " elif timestep == \"decadal\":\n", - " return selected.mean(dim=[\"x\", \"y\"]).compute()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "settled-plaza", - "metadata": {}, - "outputs": [], - "source": [ - "results_dict = {\"fire\": {}, \"insects\": {}, \"drought\": {}}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "clear-commodity", - "metadata": {}, - "outputs": [], - "source": [ - "for region, bbox in region_bboxes.items():\n", - " results_dict[\"fire\"][region] = {}\n", - " results_dict[\"fire\"][region][\"future\"] = (\n", - " load_regional_results(ds, \"annual\", region_bboxes[region])\n", - " .sel(year=slice(1970, 2099))\n", - " .probability\n", - " )\n", - " results_dict[\"fire\"][region][\"historical\"] = load_regional_results(\n", - " historical_annual, \"annual\", region_bboxes[region]\n", - " ).historical" - ] - }, - { - "cell_type": "markdown", - "id": "combined-millennium", - "metadata": {}, - "source": [ - "### Load in insect and drought data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "comic-third", - "metadata": {}, - "outputs": [], - "source": [ - "for impact in [\"insects\", \"drought\"]:\n", - " historical_ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_terraclimate.zarr\".format(\n", - " impact\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1995, 2020, 10)})\n", - " .compute()\n", - " )\n", - " future_ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", - " impact\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", - " .compute()\n", - " )\n", - "\n", - " for region, bbox in region_bboxes.items():\n", - " results_dict[impact][region] = {}\n", - " results_dict[impact][region][\"future\"] = (\n", - " load_regional_results(future_ds, \"decadal\", region_bboxes[region])\n", - " .sel(year=slice(1970, 2099))\n", - " .probability\n", - " )\n", - " results_dict[impact][region][\"historical\"] = load_regional_results(\n", - " historical_ds, \"decadal\", region_bboxes[region]\n", - " ).probability" - ] - }, - { - "cell_type": "markdown", - "id": "decent-baltimore", - "metadata": {}, - "source": [ - "## Plot the figure\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "obvious-thursday", - "metadata": {}, - "outputs": [], - "source": [ - "plot.multipanel_ts(results_dict, region_bboxes, \"Figure-4\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-01/Supplementary-Figure-01.ipynb b/notebooks/paper/Supplementary-Figure-01/Supplementary-Figure-01.ipynb index cd3f1f9..17428e7 100644 --- a/notebooks/paper/Supplementary-Figure-01/Supplementary-Figure-01.ipynb +++ b/notebooks/paper/Supplementary-Figure-01/Supplementary-Figure-01.ipynb @@ -1,254 +1,254 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "muslim-proof", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "brave-southwest", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"fire_stats.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sunset-bruce", - "metadata": {}, - "outputs": [], - "source": [ - "df" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "municipal-rachel", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"roc\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "subjective-mobile", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"r2\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "loose-empty", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"annual_r2\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "quantitative-launch", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"seasonal_r2\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "personalized-binding", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"spatial_r2\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dab131fe", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"spatial_auc\"].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "mobile-affiliate", - "metadata": {}, - "outputs": [], - "source": [ - "results = {}\n", - "results[\"null\"] = df[df[\"method\"].str.contains(\"shuffle_all\")]\n", - "results[\"split\"] = df[df[\"method\"].str.contains(\"split_halves\")]\n", - "results[\"extrap\"] = df[df[\"method\"].str.contains(\"extrapolate\")]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "neutral-primary", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "burning-pixel", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"roc\": {\n", - " \"ylim\": [0.5, 0.95],\n", - " \"ylabel\": \"Hurdle ROC\",\n", - " \"yticks\": [0.5, 0.6, 0.7, 0.8, 0.9],\n", - " },\n", - " \"r2\": {\"ylim\": [-0.02, 0.08], \"ylabel\": \"Hurdle R2\"},\n", - " \"annual_r2\": {\"ylim\": [-0.1, 0.95], \"ylabel\": \"Annual R2\"},\n", - " \"seasonal_r2\": {\"ylim\": [-0.1, 0.95], \"ylabel\": \"Seasonal R2\"},\n", - " \"spatial_auc\": {\"ylim\": [0.4, 0.95], \"ylabel\": \"Spatial ROC\"},\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "drawn-telephone", - "metadata": {}, - "outputs": [], - "source": [ - "def style(ax, params):\n", - " ax.spines[\"top\"].set_visible(False)\n", - " ax.spines[\"right\"].set_visible(False)\n", - " ax.spines[\"bottom\"].set_visible(False)\n", - " ax.set_xticks([])\n", - " if \"ylim\" in params.keys():\n", - " ax.set_ylim(params[\"ylim\"])\n", - " if \"yticks\" in params.keys():\n", - " ax.set_yticks(params[\"yticks\"])\n", - " if \"ylabel\" in params.keys():\n", - " ax.set_ylabel(params[\"ylabel\"])\n", - "\n", - "\n", - "def plot_lines(ax, field):\n", - " ax.hlines(\n", - " [\n", - " results[\"null\"][field].min(),\n", - " results[\"null\"][field].median(),\n", - " results[\"null\"][field].max(),\n", - " ],\n", - " 0,\n", - " 1,\n", - " color=\"gray\",\n", - " )\n", - "\n", - "\n", - "def plot_dots(ax, field):\n", - " ax.plot(\n", - " [0.25],\n", - " [results[\"split\"][field].median()],\n", - " \".\",\n", - " color=\"black\",\n", - " markersize=15,\n", - " )\n", - " ax.plot(\n", - " [0.75],\n", - " [results[\"extrap\"][field].median()],\n", - " \".\",\n", - " color=\"gray\",\n", - " markersize=15,\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "typical-moscow", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axs = plt.subplots(\n", - " nrows=1,\n", - " ncols=5,\n", - " figsize=(12, 5),\n", - ")\n", - "\n", - "for i, field in enumerate(\n", - " [\"roc\", \"r2\", \"spatial_auc\", \"annual_r2\", \"seasonal_r2\"]\n", - "):\n", - " plot_lines(axs[i], field)\n", - " plot_dots(axs[i], field)\n", - " style(axs[i], plot_params[field])\n", - "\n", - "plt.tight_layout()\n", - "plt.savefig(\"Supplementary-Figure-01.svg\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aggressive-distributor", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"split_halves\")][\"bias\"].median()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "changed-charity", - "metadata": {}, - "outputs": [], - "source": [ - "df[df[\"method\"].str.contains(\"extrapolate\")][\"bias\"].median()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "muslim-proof", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "brave-southwest", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"fire_stats.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sunset-bruce", + "metadata": {}, + "outputs": [], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "municipal-rachel", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"roc\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "subjective-mobile", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"r2\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "loose-empty", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"annual_r2\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "quantitative-launch", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"seasonal_r2\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "personalized-binding", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"spatial_r2\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dab131fe", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"spatial_auc\"].mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "mobile-affiliate", + "metadata": {}, + "outputs": [], + "source": [ + "results = {}\n", + "results[\"null\"] = df[df[\"method\"].str.contains(\"shuffle_all\")]\n", + "results[\"split\"] = df[df[\"method\"].str.contains(\"split_halves\")]\n", + "results[\"extrap\"] = df[df[\"method\"].str.contains(\"extrapolate\")]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "neutral-primary", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "burning-pixel", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"roc\": {\n", + " \"ylim\": [0.5, 0.95],\n", + " \"ylabel\": \"Hurdle ROC\",\n", + " \"yticks\": [0.5, 0.6, 0.7, 0.8, 0.9],\n", + " },\n", + " \"r2\": {\"ylim\": [-0.02, 0.08], \"ylabel\": \"Hurdle R2\"},\n", + " \"annual_r2\": {\"ylim\": [-0.1, 0.95], \"ylabel\": \"Annual R2\"},\n", + " \"seasonal_r2\": {\"ylim\": [-0.1, 0.95], \"ylabel\": \"Seasonal R2\"},\n", + " \"spatial_auc\": {\"ylim\": [0.4, 0.95], \"ylabel\": \"Spatial ROC\"},\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "drawn-telephone", + "metadata": {}, + "outputs": [], + "source": [ + "def style(ax, params):\n", + " ax.spines[\"top\"].set_visible(False)\n", + " ax.spines[\"right\"].set_visible(False)\n", + " ax.spines[\"bottom\"].set_visible(False)\n", + " ax.set_xticks([])\n", + " if \"ylim\" in params.keys():\n", + " ax.set_ylim(params[\"ylim\"])\n", + " if \"yticks\" in params.keys():\n", + " ax.set_yticks(params[\"yticks\"])\n", + " if \"ylabel\" in params.keys():\n", + " ax.set_ylabel(params[\"ylabel\"])\n", + "\n", + "\n", + "def plot_lines(ax, field):\n", + " ax.hlines(\n", + " [\n", + " results[\"null\"][field].min(),\n", + " results[\"null\"][field].median(),\n", + " results[\"null\"][field].max(),\n", + " ],\n", + " 0,\n", + " 1,\n", + " color=\"gray\",\n", + " )\n", + "\n", + "\n", + "def plot_dots(ax, field):\n", + " ax.plot(\n", + " [0.25],\n", + " [results[\"split\"][field].median()],\n", + " \".\",\n", + " color=\"black\",\n", + " markersize=15,\n", + " )\n", + " ax.plot(\n", + " [0.75],\n", + " [results[\"extrap\"][field].median()],\n", + " \".\",\n", + " color=\"gray\",\n", + " markersize=15,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "typical-moscow", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(\n", + " nrows=1,\n", + " ncols=5,\n", + " figsize=(12, 5),\n", + ")\n", + "\n", + "for i, field in enumerate(\n", + " [\"roc\", \"r2\", \"spatial_auc\", \"annual_r2\", \"seasonal_r2\"]\n", + "):\n", + " plot_lines(axs[i], field)\n", + " plot_dots(axs[i], field)\n", + " style(axs[i], plot_params[field])\n", + "\n", + "plt.tight_layout()\n", + "plt.savefig(\"Supplementary-Figure-01.svg\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aggressive-distributor", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"split_halves\")][\"bias\"].median()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "changed-charity", + "metadata": {}, + "outputs": [], + "source": [ + "df[df[\"method\"].str.contains(\"extrapolate\")][\"bias\"].median()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-02/Supplementary-Figure-02.ipynb b/notebooks/paper/Supplementary-Figure-02/Supplementary-Figure-02.ipynb index eacfa86..c547c75 100644 --- a/notebooks/paper/Supplementary-Figure-02/Supplementary-Figure-02.ipynb +++ b/notebooks/paper/Supplementary-Figure-02/Supplementary-Figure-02.ipynb @@ -1,81 +1,81 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "geological-bedroom", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "geological-bedroom", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "initial-bonus", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "helpful-enough", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"StankeSFigData.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "industrial-oasis", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(4, 4))\n", + "ax.plot(df[\"StankeFSI\"], df[\"DrtMort\"], \"k.\", markersize=12)\n", + "ax.spines[\"top\"].set_visible(False)\n", + "ax.spines[\"right\"].set_visible(False)\n", + "ax.set_xlim(-2.25, 1.25)\n", + "ax.set_xticks([-2, -1, 0, 1])\n", + "ax.set_ylim([0, 0.025])\n", + "ax.set_yticks([0, 0.01, 0.02])\n", + "ax.set_ylabel(\"Mortality (%/year)\")\n", + "ax.set_xlabel(\"Forest stability index\")\n", + "m, b = np.polyfit(df[\"StankeFSI\"], df[\"DrtMort\"], 1)\n", + "plt.plot(df[\"StankeFSI\"], m * df[\"StankeFSI\"] + b, \"gray\")\n", + "for i, code in enumerate(df[\"SP.CD\"]):\n", + " ax.annotate(code, (df[\"StankeFSI\"][i] - 0.25, df[\"DrtMort\"][i] + 0.001))\n", + "plt.savefig(\"Supplementary-Figure-02.svg\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "initial-bonus", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "helpful-enough", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"StankeSFigData.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "industrial-oasis", - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots(1, 1, figsize=(4, 4))\n", - "ax.plot(df[\"StankeFSI\"], df[\"DrtMort\"], \"k.\", markersize=12)\n", - "ax.spines[\"top\"].set_visible(False)\n", - "ax.spines[\"right\"].set_visible(False)\n", - "ax.set_xlim(-2.25, 1.25)\n", - "ax.set_xticks([-2, -1, 0, 1])\n", - "ax.set_ylim([0, 0.025])\n", - "ax.set_yticks([0, 0.01, 0.02])\n", - "ax.set_ylabel(\"Mortality (%/year)\")\n", - "ax.set_xlabel(\"Forest stability index\")\n", - "m, b = np.polyfit(df[\"StankeFSI\"], df[\"DrtMort\"], 1)\n", - "plt.plot(df[\"StankeFSI\"], m * df[\"StankeFSI\"] + b, \"gray\")\n", - "for i, code in enumerate(df[\"SP.CD\"]):\n", - " ax.annotate(code, (df[\"StankeFSI\"][i] - 0.25, df[\"DrtMort\"][i] + 0.001))\n", - "plt.savefig(\"Supplementary-Figure-02.svg\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-03/Supplementary-Figure-03.ipynb b/notebooks/paper/Supplementary-Figure-03/Supplementary-Figure-03.ipynb index 22584e2..19e0375 100644 --- a/notebooks/paper/Supplementary-Figure-03/Supplementary-Figure-03.ipynb +++ b/notebooks/paper/Supplementary-Figure-03/Supplementary-Figure-03.ipynb @@ -1,187 +1,187 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "unlike-defendant", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import xarray as xr\n", - "import rioxarray\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "from rasterio.enums import Resampling\n", - "from carbonplan_forest_risks import (\n", - " load,\n", - " setup,\n", - " plot,\n", - " fit,\n", - " utils,\n", - " prepare,\n", - " collect,\n", - ")\n", - "from carbonplan.data import cat\n", - "from carbonplan_styles.mpl import get_colormap" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "unlike-defendant", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import xarray as xr\n", + "import rioxarray\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "from rasterio.enums import Resampling\n", + "from carbonplan_forest_risks import (\n", + " load,\n", + " setup,\n", + " plot,\n", + " fit,\n", + " utils,\n", + " prepare,\n", + " collect,\n", + ")\n", + "from carbonplan.data import cat\n", + "from carbonplan_styles.mpl import get_colormap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "drawn-thirty", + "metadata": {}, + "outputs": [], + "source": [ + "def load_bill(url):\n", + " target = cat.nlcd.raster.to_dask()\n", + " source = xr.open_rasterio(url)\n", + " source = source.where(source > -1)\n", + " ds = source.rio.reproject_match(target, resampling=Resampling.bilinear)\n", + " ds = ds.where(ds > -1).sel(band=1)\n", + "\n", + " return ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sophisticated-uganda", + "metadata": {}, + "outputs": [], + "source": [ + "base_url_tempate = \"https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/Fig1_4-22-21/{}\"\n", + "dataset_urls = {\n", + " \"Insects\": {\n", + " \"modeled\": base_url_tempate.format(\n", + " \"Fig1F_InsectModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tif\"\n", + " ),\n", + " \"observed\": base_url_tempate.format(\n", + " \"Fig1E_InsectModel_FIAwide-ObsMort_05-08-2021.tif\"\n", + " ),\n", + " \"reference\": \"SuppFig_Williamsetal2016_BarkBeetleOccurrenceMap_04-19-2021.tif\",\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dominican-conviction", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict = {}\n", + "ds_dict[\"modeled\"] = load_bill(dataset_urls[\"Insects\"][\"modeled\"]) * 100\n", + "ds_dict[\"observed\"] = load_bill(dataset_urls[\"Insects\"][\"observed\"]) * 100\n", + "ds_dict[\"reference\"] = load_bill(dataset_urls[\"Insects\"][\"reference\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "royal-prairie", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"modeled\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.4),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.76,\n", + " },\n", + " \"observed\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.4),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.4,\n", + " },\n", + " \"reference\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.5),\n", + " \"label\": \"Area affected\\n(% grid cell/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.04,\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "together-enlargement", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "prescribed-overhead", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axarr = plt.subplots(\n", + " nrows=3,\n", + " ncols=1,\n", + " figsize=(5, 12),\n", + " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", + ")\n", + "for row, setup in enumerate([\"observed\", \"modeled\", \"reference\"]):\n", + " vmin, vmax = (\n", + " plot_params[setup][\"var_lims\"][0],\n", + " plot_params[setup][\"var_lims\"][1],\n", + " )\n", + " map_plot = ds_dict[\"{}\".format(setup)].plot.imshow(\n", + " ax=axarr[row],\n", + " cmap=plot_params[setup][\"cmap\"],\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " add_colorbar=False,\n", + " add_labels=False,\n", + " )\n", + " plot.map_pretty(axarr[row], title=setup.capitalize())\n", + " plot.add_colorbar(\n", + " fig,\n", + " to_plot=map_plot,\n", + " y_location=plot_params[setup][\"cbar_ylocation\"],\n", + " vmin=plot_params[setup][\"var_lims\"][0],\n", + " vmax=plot_params[setup][\"var_lims\"][1],\n", + " cbar_label=plot_params[setup][\"label\"],\n", + " )\n", + "plt.tight_layout(pad=-6)\n", + "fig.savefig(\"Supplementary-Figure-03.svg\", format=\"svg\", bbox_inches=\"tight\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "drawn-thirty", - "metadata": {}, - "outputs": [], - "source": [ - "def load_bill(url):\n", - " target = cat.nlcd.raster.to_dask()\n", - " source = xr.open_rasterio(url)\n", - " source = source.where(source > -1)\n", - " ds = source.rio.reproject_match(target, resampling=Resampling.bilinear)\n", - " ds = ds.where(ds > -1).sel(band=1)\n", - "\n", - " return ds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sophisticated-uganda", - "metadata": {}, - "outputs": [], - "source": [ - "base_url_tempate = \"https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/Fig1_4-22-21/{}\"\n", - "dataset_urls = {\n", - " \"Insects\": {\n", - " \"modeled\": base_url_tempate.format(\n", - " \"Fig1F_InsectModel_ModeledFIAlongEnsembleHistMort_04-22-2021.tif\"\n", - " ),\n", - " \"observed\": base_url_tempate.format(\n", - " \"Fig1E_InsectModel_FIAwide-ObsMort_05-08-2021.tif\"\n", - " ),\n", - " \"reference\": \"SuppFig_Williamsetal2016_BarkBeetleOccurrenceMap_04-19-2021.tif\",\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dominican-conviction", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict = {}\n", - "ds_dict[\"modeled\"] = load_bill(dataset_urls[\"Insects\"][\"modeled\"]) * 100\n", - "ds_dict[\"observed\"] = load_bill(dataset_urls[\"Insects\"][\"observed\"]) * 100\n", - "ds_dict[\"reference\"] = load_bill(dataset_urls[\"Insects\"][\"reference\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "royal-prairie", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"modeled\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.4),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.76,\n", - " },\n", - " \"observed\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.4),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.4,\n", - " },\n", - " \"reference\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.5),\n", - " \"label\": \"Area affected\\n(% grid cell/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.04,\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "together-enlargement", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "prescribed-overhead", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axarr = plt.subplots(\n", - " nrows=3,\n", - " ncols=1,\n", - " figsize=(5, 12),\n", - " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", - ")\n", - "for row, setup in enumerate([\"observed\", \"modeled\", \"reference\"]):\n", - " vmin, vmax = (\n", - " plot_params[setup][\"var_lims\"][0],\n", - " plot_params[setup][\"var_lims\"][1],\n", - " )\n", - " map_plot = ds_dict[\"{}\".format(setup)].plot.imshow(\n", - " ax=axarr[row],\n", - " cmap=plot_params[setup][\"cmap\"],\n", - " vmin=vmin,\n", - " vmax=vmax,\n", - " add_colorbar=False,\n", - " add_labels=False,\n", - " )\n", - " plot.map_pretty(axarr[row], title=setup.capitalize())\n", - " plot.add_colorbar(\n", - " fig,\n", - " to_plot=map_plot,\n", - " y_location=plot_params[setup][\"cbar_ylocation\"],\n", - " vmin=plot_params[setup][\"var_lims\"][0],\n", - " vmax=plot_params[setup][\"var_lims\"][1],\n", - " cbar_label=plot_params[setup][\"label\"],\n", - " )\n", - "plt.tight_layout(pad=-6)\n", - "fig.savefig(\"Supplementary-Figure-03.svg\", format=\"svg\", bbox_inches=\"tight\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-04/Supplementary-Figure-04.ipynb b/notebooks/paper/Supplementary-Figure-04/Supplementary-Figure-04.ipynb index 2fb578e..6bbcb56 100644 --- a/notebooks/paper/Supplementary-Figure-04/Supplementary-Figure-04.ipynb +++ b/notebooks/paper/Supplementary-Figure-04/Supplementary-Figure-04.ipynb @@ -1,107 +1,107 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "minimal-pittsburgh", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "minimal-pittsburgh", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "comic-engineer", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "electric-cover", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"Insect2PineFTs_SFigData.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "emerging-ordinance", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(2, 1, figsize=(12, 6))\n", + "width = 0.35\n", + "labels = list(df[\"Coefficient\"])\n", + "index = np.arange(len(df.index))\n", + "rects1a = axs[0].bar(\n", + " index - width / 2, df[\"Limber.Binom\"], width, color=\"black\", label=\"Men\"\n", + ")\n", + "rects1b = axs[0].bar(\n", + " index + width / 2, df[\"Limber.Beta\"], width, color=\"gray\", label=\"Women\"\n", + ")\n", + "rects2a = axs[1].bar(\n", + " index - width / 2, df[\"Lodgepole.Binom\"], width, color=\"black\", label=\"Men\"\n", + ")\n", + "rects2b = axs[1].bar(\n", + " index + width / 2, df[\"Lodgepole.Beta\"], width, color=\"gray\", label=\"Women\"\n", + ")\n", + "axs[0].spines[\"top\"].set_visible(False)\n", + "axs[0].spines[\"right\"].set_visible(False)\n", + "axs[1].spines[\"top\"].set_visible(False)\n", + "axs[1].spines[\"right\"].set_visible(False)\n", + "axs[0].set_xticks([])\n", + "axs[1].set_xticks(index)\n", + "axs[1].set_xticklabels(labels)\n", + "axs[0].set_ylim([-25, 24])\n", + "axs[1].set_ylim([-7, 3])\n", + "axs[0].set_ylabel(\"Coefficient\")\n", + "axs[1].set_ylabel(\"Coefficient\")\n", + "for i, pval in enumerate(df[\"Limber.Binom.pval\"]):\n", + " if pval < 0.05:\n", + " axs[0].annotate(\"*\", (index[i] - width / 2 - 0.045, 20))\n", + "for i, pval in enumerate(df[\"Limber.Beta.pval\"]):\n", + " if pval < 0.05:\n", + " axs[0].annotate(\"*\", (index[i] + width / 2 - 0.045, 20), color=\"gray\")\n", + "for i, pval in enumerate(df[\"Lodgepole.Binom.pval\"]):\n", + " if pval < 0.05:\n", + " axs[1].annotate(\"*\", (index[i] - width / 2 - 0.045, 2.5))\n", + "for i, pval in enumerate(df[\"Lodgepole.Beta.pval\"]):\n", + " if pval < 0.05:\n", + " axs[1].annotate(\"*\", (index[i] + width / 2 - 0.045, 2.5), color=\"gray\")\n", + "\n", + "plt.savefig(\"Supplementary-Figure-04.svg\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "comic-engineer", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "electric-cover", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"Insect2PineFTs_SFigData.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "emerging-ordinance", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axs = plt.subplots(2, 1, figsize=(12, 6))\n", - "width = 0.35\n", - "labels = list(df[\"Coefficient\"])\n", - "index = np.arange(len(df.index))\n", - "rects1a = axs[0].bar(\n", - " index - width / 2, df[\"Limber.Binom\"], width, color=\"black\", label=\"Men\"\n", - ")\n", - "rects1b = axs[0].bar(\n", - " index + width / 2, df[\"Limber.Beta\"], width, color=\"gray\", label=\"Women\"\n", - ")\n", - "rects2a = axs[1].bar(\n", - " index - width / 2, df[\"Lodgepole.Binom\"], width, color=\"black\", label=\"Men\"\n", - ")\n", - "rects2b = axs[1].bar(\n", - " index + width / 2, df[\"Lodgepole.Beta\"], width, color=\"gray\", label=\"Women\"\n", - ")\n", - "axs[0].spines[\"top\"].set_visible(False)\n", - "axs[0].spines[\"right\"].set_visible(False)\n", - "axs[1].spines[\"top\"].set_visible(False)\n", - "axs[1].spines[\"right\"].set_visible(False)\n", - "axs[0].set_xticks([])\n", - "axs[1].set_xticks(index)\n", - "axs[1].set_xticklabels(labels)\n", - "axs[0].set_ylim([-25, 24])\n", - "axs[1].set_ylim([-7, 3])\n", - "axs[0].set_ylabel(\"Coefficient\")\n", - "axs[1].set_ylabel(\"Coefficient\")\n", - "for i, pval in enumerate(df[\"Limber.Binom.pval\"]):\n", - " if pval < 0.05:\n", - " axs[0].annotate(\"*\", (index[i] - width / 2 - 0.045, 20))\n", - "for i, pval in enumerate(df[\"Limber.Beta.pval\"]):\n", - " if pval < 0.05:\n", - " axs[0].annotate(\"*\", (index[i] + width / 2 - 0.045, 20), color=\"gray\")\n", - "for i, pval in enumerate(df[\"Lodgepole.Binom.pval\"]):\n", - " if pval < 0.05:\n", - " axs[1].annotate(\"*\", (index[i] - width / 2 - 0.045, 2.5))\n", - "for i, pval in enumerate(df[\"Lodgepole.Beta.pval\"]):\n", - " if pval < 0.05:\n", - " axs[1].annotate(\"*\", (index[i] + width / 2 - 0.045, 2.5), color=\"gray\")\n", - "\n", - "plt.savefig(\"Supplementary-Figure-04.svg\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-05/Supplementary-Figure-05.ipynb b/notebooks/paper/Supplementary-Figure-05/Supplementary-Figure-05.ipynb index 782a63e..4ac393f 100644 --- a/notebooks/paper/Supplementary-Figure-05/Supplementary-Figure-05.ipynb +++ b/notebooks/paper/Supplementary-Figure-05/Supplementary-Figure-05.ipynb @@ -1,259 +1,259 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "delayed-nirvana", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import xarray as xr\n", - "import rioxarray\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "from rasterio.enums import Resampling\n", - "from carbonplan_forest_risks import (\n", - " load,\n", - " setup,\n", - " plot,\n", - " fit,\n", - " utils,\n", - " prepare,\n", - " collect,\n", - ")\n", - "from carbonplan.data import cat\n", - "from carbonplan_styles.mpl import get_colormap\n", - "\n", - "from showit import image" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "delayed-nirvana", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import xarray as xr\n", + "import rioxarray\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "from rasterio.enums import Resampling\n", + "from carbonplan_forest_risks import (\n", + " load,\n", + " setup,\n", + " plot,\n", + " fit,\n", + " utils,\n", + " prepare,\n", + " collect,\n", + ")\n", + "from carbonplan.data import cat\n", + "from carbonplan_styles.mpl import get_colormap\n", + "\n", + "from showit import image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "human-introduction", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "coral-special", + "metadata": {}, + "outputs": [], + "source": [ + "def load_bill(url):\n", + " target = cat.nlcd.raster.to_dask()\n", + " source = xr.open_rasterio(url)\n", + " source = source.where(source > -1)\n", + " ds = source.rio.reproject_match(target, resampling=Resampling.bilinear)\n", + " ds = (\n", + " ds.where(ds > -1)\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + " .sel(band=1)\n", + " )\n", + "\n", + " return ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sustainable-mainland", + "metadata": {}, + "outputs": [], + "source": [ + "dataset_urls = {\n", + " \"insects\": {\n", + " \"AUC\": \"SuppFig_InsectModel_CV_AUC_04-22-2021.tif\",\n", + " \"R2\": \"SuppFig_InsectModel_CV_nonzeroR2_04-22-2021.tif\",\n", + " },\n", + " \"drought\": {\n", + " \"AUC\": \"SuppFig_DroughtModel_CV_AUC_04-22-2021.tif\",\n", + " \"R2\": \"SuppFig_DroughtModel_CV_nonzeroR2_04-22-2021.tif\",\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "hindu-biography", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict = {}\n", + "ds_dict[\"insects\"] = {}\n", + "ds_dict[\"drought\"] = {}\n", + "ds_dict[\"insects\"][\"AUC\"] = load_bill(dataset_urls[\"insects\"][\"AUC\"])\n", + "ds_dict[\"insects\"][\"R2\"] = load_bill(dataset_urls[\"insects\"][\"R2\"])\n", + "ds_dict[\"drought\"][\"AUC\"] = load_bill(dataset_urls[\"drought\"][\"AUC\"])\n", + "ds_dict[\"drought\"][\"R2\"] = load_bill(dataset_urls[\"drought\"][\"R2\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "defensive-tsunami", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"drought_AUC\": {\n", + " \"cmap\": get_colormap(\"pinks\"),\n", + " \"var_lims\": (0.5, 0.8),\n", + " \"label\": \"Drought-related\\nmortality (%/year)\",\n", + " \"panel\": [\"C\", \"D\"],\n", + " \"cbar_ylocation\": 0.15,\n", + " \"cbar_xlocation\": 0.46,\n", + " },\n", + " \"insects_AUC\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0.5, 0.8),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.64,\n", + " \"cbar_xlocation\": 0.46,\n", + " },\n", + " \"drought_R2\": {\n", + " \"cmap\": get_colormap(\"pinks\"),\n", + " \"var_lims\": (0, 0.05),\n", + " \"label\": \"Drought-related\\nmortality (%/year)\",\n", + " \"panel\": [\"C\", \"D\"],\n", + " \"cbar_ylocation\": 0.15,\n", + " \"cbar_xlocation\": 1.01,\n", + " },\n", + " \"insects_R2\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.15),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": [\"E\", \"F\"],\n", + " \"cbar_ylocation\": 0.64,\n", + " \"cbar_xlocation\": 1.01,\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "altered-norwegian", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "biological-alexandria", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axarr = plt.subplots(\n", + " nrows=2,\n", + " ncols=2,\n", + " figsize=(12, 10),\n", + " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", + ")\n", + "for row, dataset in enumerate([\"insects\", \"drought\"]):\n", + " for column, metric in enumerate([\"AUC\", \"R2\"]):\n", + " vmin, vmax = (\n", + " plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][0],\n", + " plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][1],\n", + " )\n", + " map_plot = ds_dict[dataset][metric].plot.imshow(\n", + " ax=axarr[row, column],\n", + " cmap=plot_params[\"{}_{}\".format(dataset, metric)][\"cmap\"],\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " add_colorbar=False,\n", + " add_labels=False,\n", + " )\n", + " plot.map_pretty(axarr[row, column], title=metric)\n", + " plot.add_colorbar(\n", + " fig,\n", + " to_plot=map_plot,\n", + " y_location=plot_params[\"{}_{}\".format(dataset, metric)][\n", + " \"cbar_ylocation\"\n", + " ],\n", + " x_location=plot_params[\"{}_{}\".format(dataset, metric)][\n", + " \"cbar_xlocation\"\n", + " ],\n", + " vmin=plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][0],\n", + " vmax=plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][1],\n", + " cbar_label=plot_params[\"{}_{}\".format(dataset, metric)][\"label\"],\n", + " )\n", + "plt.tight_layout(pad=-6)\n", + "fig.savefig(\"Supplementary-Figure-05.svg\", format=\"svg\", bbox_inches=\"tight\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "underlying-correlation", + "metadata": {}, + "outputs": [], + "source": [ + "df_drought = pd.read_csv(\n", + " \"Drought_USwide_CrossValidationR2_4-21-21.csv\", header=None\n", + ")\n", + "df_insects = pd.read_csv(\n", + " \"Insect_USwide_CrossValidationR2_4-21-21.csv\", header=None\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "discrete-qatar", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"drought: spatial cross validation R2 percentiles\")\n", + "print(np.percentile(df_drought[0], [5, 50, 95]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bulgarian-bracelet", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"insects: spatial cross validation R2 percentiles\")\n", + "print(np.percentile(df_insects[0], [5, 50, 95]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "human-introduction", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "coral-special", - "metadata": {}, - "outputs": [], - "source": [ - "def load_bill(url):\n", - " target = cat.nlcd.raster.to_dask()\n", - " source = xr.open_rasterio(url)\n", - " source = source.where(source > -1)\n", - " ds = source.rio.reproject_match(target, resampling=Resampling.bilinear)\n", - " ds = (\n", - " ds.where(ds > -1)\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - " .sel(band=1)\n", - " )\n", - "\n", - " return ds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sustainable-mainland", - "metadata": {}, - "outputs": [], - "source": [ - "dataset_urls = {\n", - " \"insects\": {\n", - " \"AUC\": \"SuppFig_InsectModel_CV_AUC_04-22-2021.tif\",\n", - " \"R2\": \"SuppFig_InsectModel_CV_nonzeroR2_04-22-2021.tif\",\n", - " },\n", - " \"drought\": {\n", - " \"AUC\": \"SuppFig_DroughtModel_CV_AUC_04-22-2021.tif\",\n", - " \"R2\": \"SuppFig_DroughtModel_CV_nonzeroR2_04-22-2021.tif\",\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "hindu-biography", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict = {}\n", - "ds_dict[\"insects\"] = {}\n", - "ds_dict[\"drought\"] = {}\n", - "ds_dict[\"insects\"][\"AUC\"] = load_bill(dataset_urls[\"insects\"][\"AUC\"])\n", - "ds_dict[\"insects\"][\"R2\"] = load_bill(dataset_urls[\"insects\"][\"R2\"])\n", - "ds_dict[\"drought\"][\"AUC\"] = load_bill(dataset_urls[\"drought\"][\"AUC\"])\n", - "ds_dict[\"drought\"][\"R2\"] = load_bill(dataset_urls[\"drought\"][\"R2\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "defensive-tsunami", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"drought_AUC\": {\n", - " \"cmap\": get_colormap(\"pinks\"),\n", - " \"var_lims\": (0.5, 0.8),\n", - " \"label\": \"Drought-related\\nmortality (%/year)\",\n", - " \"panel\": [\"C\", \"D\"],\n", - " \"cbar_ylocation\": 0.15,\n", - " \"cbar_xlocation\": 0.46,\n", - " },\n", - " \"insects_AUC\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0.5, 0.8),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.64,\n", - " \"cbar_xlocation\": 0.46,\n", - " },\n", - " \"drought_R2\": {\n", - " \"cmap\": get_colormap(\"pinks\"),\n", - " \"var_lims\": (0, 0.05),\n", - " \"label\": \"Drought-related\\nmortality (%/year)\",\n", - " \"panel\": [\"C\", \"D\"],\n", - " \"cbar_ylocation\": 0.15,\n", - " \"cbar_xlocation\": 1.01,\n", - " },\n", - " \"insects_R2\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.15),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": [\"E\", \"F\"],\n", - " \"cbar_ylocation\": 0.64,\n", - " \"cbar_xlocation\": 1.01,\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "altered-norwegian", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "biological-alexandria", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axarr = plt.subplots(\n", - " nrows=2,\n", - " ncols=2,\n", - " figsize=(12, 10),\n", - " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", - ")\n", - "for row, dataset in enumerate([\"insects\", \"drought\"]):\n", - " for column, metric in enumerate([\"AUC\", \"R2\"]):\n", - " vmin, vmax = (\n", - " plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][0],\n", - " plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][1],\n", - " )\n", - " map_plot = ds_dict[dataset][metric].plot.imshow(\n", - " ax=axarr[row, column],\n", - " cmap=plot_params[\"{}_{}\".format(dataset, metric)][\"cmap\"],\n", - " vmin=vmin,\n", - " vmax=vmax,\n", - " add_colorbar=False,\n", - " add_labels=False,\n", - " )\n", - " plot.map_pretty(axarr[row, column], title=metric)\n", - " plot.add_colorbar(\n", - " fig,\n", - " to_plot=map_plot,\n", - " y_location=plot_params[\"{}_{}\".format(dataset, metric)][\n", - " \"cbar_ylocation\"\n", - " ],\n", - " x_location=plot_params[\"{}_{}\".format(dataset, metric)][\n", - " \"cbar_xlocation\"\n", - " ],\n", - " vmin=plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][0],\n", - " vmax=plot_params[\"{}_{}\".format(dataset, metric)][\"var_lims\"][1],\n", - " cbar_label=plot_params[\"{}_{}\".format(dataset, metric)][\"label\"],\n", - " )\n", - "plt.tight_layout(pad=-6)\n", - "fig.savefig(\"Supplementary-Figure-05.svg\", format=\"svg\", bbox_inches=\"tight\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "underlying-correlation", - "metadata": {}, - "outputs": [], - "source": [ - "df_drought = pd.read_csv(\n", - " \"Drought_USwide_CrossValidationR2_4-21-21.csv\", header=None\n", - ")\n", - "df_insects = pd.read_csv(\n", - " \"Insect_USwide_CrossValidationR2_4-21-21.csv\", header=None\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "discrete-qatar", - "metadata": {}, - "outputs": [], - "source": [ - "print(\"drought: spatial cross validation R2 percentiles\")\n", - "print(np.percentile(df_drought[0], [5, 50, 95]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bulgarian-bracelet", - "metadata": {}, - "outputs": [], - "source": [ - "print(\"insects: spatial cross validation R2 percentiles\")\n", - "print(np.percentile(df_insects[0], [5, 50, 95]))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.8" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-06/Supplementary-Figure-06.ipynb b/notebooks/paper/Supplementary-Figure-06/Supplementary-Figure-06.ipynb index c51fc4a..a64ff9b 100644 --- a/notebooks/paper/Supplementary-Figure-06/Supplementary-Figure-06.ipynb +++ b/notebooks/paper/Supplementary-Figure-06/Supplementary-Figure-06.ipynb @@ -1,89 +1,89 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "40c064a4", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "40c064a4", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f29d0150", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e47be5a", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(\"noanalog_ssp370_4-5-22.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcbc3d1d", + "metadata": {}, + "outputs": [], + "source": [ + "fields = [\"tempall\", \"pcpall\", \"pdsiall\", \"cwdall\", \"petall\", \"vpdall\"]\n", + "labels = {\n", + " \"tempall\": {\"x\": \"Percent novel climate (temperature)\"},\n", + " \"pcpall\": {\"x\": \"Percent novel climate (precipitation)\"},\n", + " \"pdsiall\": {\"x\": \"Percent novel climate (PDSI)\"},\n", + " \"cwdall\": {\"x\": \"Percent novel climate (CWD)\"},\n", + " \"petall\": {\"x\": \"Percent novel climate (PET)\"},\n", + " \"vpdall\": {\"x\": \"Percent novel climate (VPD)\"},\n", + "}\n", + "plt.figure(figsize=(16, 8))\n", + "\n", + "for i, f in enumerate(fields):\n", + " ax = plt.subplot(2, 3, i + 1)\n", + " plt.hist(df[f], rwidth=0.8, bins=np.arange(0, 100, 5), color=\"black\")\n", + " ax.spines[\"top\"].set_visible(False)\n", + " ax.spines[\"right\"].set_visible(False)\n", + " if i == 0 or i == 3:\n", + " ax.set_ylabel(\"Frequency\")\n", + " ax.set_xlabel(labels[f][\"x\"])\n", + " ax.set_xlim((-5, 105))\n", + " ax.set_ylim((0, 100))\n", + "\n", + "plt.savefig(\"Supplementary-Figure-06.svg\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "f29d0150", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e47be5a", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(\"noanalog_ssp370_4-5-22.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcbc3d1d", - "metadata": {}, - "outputs": [], - "source": [ - "fields = [\"tempall\", \"pcpall\", \"pdsiall\", \"cwdall\", \"petall\", \"vpdall\"]\n", - "labels = {\n", - " \"tempall\": {\"x\": \"Percent novel climate (temperature)\"},\n", - " \"pcpall\": {\"x\": \"Percent novel climate (precipitation)\"},\n", - " \"pdsiall\": {\"x\": \"Percent novel climate (PDSI)\"},\n", - " \"cwdall\": {\"x\": \"Percent novel climate (CWD)\"},\n", - " \"petall\": {\"x\": \"Percent novel climate (PET)\"},\n", - " \"vpdall\": {\"x\": \"Percent novel climate (VPD)\"},\n", - "}\n", - "plt.figure(figsize=(16, 8))\n", - "\n", - "for i, f in enumerate(fields):\n", - " ax = plt.subplot(2, 3, i + 1)\n", - " plt.hist(df[f], rwidth=0.8, bins=np.arange(0, 100, 5), color=\"black\")\n", - " ax.spines[\"top\"].set_visible(False)\n", - " ax.spines[\"right\"].set_visible(False)\n", - " if i == 0 or i == 3:\n", - " ax.set_ylabel(\"Frequency\")\n", - " ax.set_xlabel(labels[f][\"x\"])\n", - " ax.set_xlim((-5, 105))\n", - " ax.set_ylim((0, 100))\n", - "\n", - "plt.savefig(\"Supplementary-Figure-06.svg\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-07/Supplementary-Figure-07.ipynb b/notebooks/paper/Supplementary-Figure-07/Supplementary-Figure-07.ipynb index f8e6f80..f086376 100644 --- a/notebooks/paper/Supplementary-Figure-07/Supplementary-Figure-07.ipynb +++ b/notebooks/paper/Supplementary-Figure-07/Supplementary-Figure-07.ipynb @@ -1,109 +1,109 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f4fb0c40", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import statsmodels.api as sm\n", - "from statsmodels.tools.tools import add_constant" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f4fb0c40", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import statsmodels.api as sm\n", + "from statsmodels.tools.tools import add_constant" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eea3a1a", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65d2e5e3", + "metadata": {}, + "outputs": [], + "source": [ + "df1 = pd.read_csv(\"BAcomparison_toCP.csv\")\n", + "df2 = pd.read_csv(\"cVegTreecomparison_toCP.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd94fbae", + "metadata": {}, + "outputs": [], + "source": [ + "model1 = sm.OLS(df1[\"Log_StatModelBA\"], add_constant(df1[\"Log_CMIPBA\"])).fit()\n", + "yhat1 = model1.predict(add_constant(df1[\"Log_CMIPBA\"]))\n", + "\n", + "x2 = np.asarray(\n", + " [\n", + " np.ones(df2[\"Log_DeltaBA\"].shape),\n", + " df2[\"Log_DeltaBA\"],\n", + " df2[\"Log_DeltaBA\"] ** 2,\n", + " ]\n", + ").T\n", + "model2 = sm.OLS(df2[\"CMIP_deltacVegTree\"], x2).fit()\n", + "yhat2 = model2.predict(x2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7aaf6de3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(13, 6))\n", + "ax1 = plt.subplot(1, 2, 1)\n", + "plt.plot(df1[\"Log_CMIPBA\"], df1[\"Log_StatModelBA\"], \"k.\")\n", + "plt.plot(df1[\"Log_CMIPBA\"], yhat1, \"red\", linewidth=2)\n", + "plt.xlabel(\"log(CMIP6 fire emissions)\")\n", + "plt.ylabel(\"log(Modeled burn area)\")\n", + "plt.xticks([-3.5, -3, -2.5])\n", + "ax1.spines[\"top\"].set_visible(False)\n", + "ax1.spines[\"right\"].set_visible(False)\n", + "\n", + "ax2 = plt.subplot(1, 2, 2)\n", + "plt.plot(df2[\"Log_DeltaBA\"], df2[\"CMIP_deltacVegTree\"], \"k.\")\n", + "plt.plot(df2[\"Log_DeltaBA\"], yhat2, \"red\", linewidth=2)\n", + "plt.xlabel(\"log(Future burn area - Historical burn area)\")\n", + "plt.ylabel(\"log(CMIP6 future cVegTree - Historical cVegTree)\")\n", + "ax2.spines[\"top\"].set_visible(False)\n", + "ax2.spines[\"right\"].set_visible(False)\n", + "\n", + "plt.savefig(\"Supplementary-Figure-07.svg\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "7eea3a1a", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "65d2e5e3", - "metadata": {}, - "outputs": [], - "source": [ - "df1 = pd.read_csv(\"BAcomparison_toCP.csv\")\n", - "df2 = pd.read_csv(\"cVegTreecomparison_toCP.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd94fbae", - "metadata": {}, - "outputs": [], - "source": [ - "model1 = sm.OLS(df1[\"Log_StatModelBA\"], add_constant(df1[\"Log_CMIPBA\"])).fit()\n", - "yhat1 = model1.predict(add_constant(df1[\"Log_CMIPBA\"]))\n", - "\n", - "x2 = np.asarray(\n", - " [\n", - " np.ones(df2[\"Log_DeltaBA\"].shape),\n", - " df2[\"Log_DeltaBA\"],\n", - " df2[\"Log_DeltaBA\"] ** 2,\n", - " ]\n", - ").T\n", - "model2 = sm.OLS(df2[\"CMIP_deltacVegTree\"], x2).fit()\n", - "yhat2 = model2.predict(x2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7aaf6de3", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(13, 6))\n", - "ax1 = plt.subplot(1, 2, 1)\n", - "plt.plot(df1[\"Log_CMIPBA\"], df1[\"Log_StatModelBA\"], \"k.\")\n", - "plt.plot(df1[\"Log_CMIPBA\"], yhat1, \"red\", linewidth=2)\n", - "plt.xlabel(\"log(CMIP6 fire emissions)\")\n", - "plt.ylabel(\"log(Modeled burn area)\")\n", - "plt.xticks([-3.5, -3, -2.5])\n", - "ax1.spines[\"top\"].set_visible(False)\n", - "ax1.spines[\"right\"].set_visible(False)\n", - "\n", - "ax2 = plt.subplot(1, 2, 2)\n", - "plt.plot(df2[\"Log_DeltaBA\"], df2[\"CMIP_deltacVegTree\"], \"k.\")\n", - "plt.plot(df2[\"Log_DeltaBA\"], yhat2, \"red\", linewidth=2)\n", - "plt.xlabel(\"log(Future burn area - Historical burn area)\")\n", - "plt.ylabel(\"log(CMIP6 future cVegTree - Historical cVegTree)\")\n", - "ax2.spines[\"top\"].set_visible(False)\n", - "ax2.spines[\"right\"].set_visible(False)\n", - "\n", - "plt.savefig(\"Supplementary-Figure-07.svg\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-08/Supplementary-Figure-08.ipynb b/notebooks/paper/Supplementary-Figure-08/Supplementary-Figure-08.ipynb index d392fe6..3b19028 100644 --- a/notebooks/paper/Supplementary-Figure-08/Supplementary-Figure-08.ipynb +++ b/notebooks/paper/Supplementary-Figure-08/Supplementary-Figure-08.ipynb @@ -1,417 +1,417 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "sustained-barrier", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import altair as alt\n", - "from carbonplan.data import cat\n", - "import rioxarray\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import cartopy.feature as cfeature\n", - "import matplotlib.pyplot as plt\n", - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "import matplotlib\n", - "from carbonplan_data import utils\n", - "alt.themes.enable(\"carbonplan_light\")" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "sustained-barrier", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import altair as alt\n", + "from carbonplan.data import cat\n", + "import rioxarray\n", + "import cartopy.crs as ccrs\n", + "import cartopy\n", + "import cartopy.feature as cfeature\n", + "import matplotlib.pyplot as plt\n", + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "import matplotlib\n", + "from carbonplan_data import utils\n", + "alt.themes.enable(\"carbonplan_light\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bizarre-semester", + "metadata": {}, + "outputs": [], + "source": [ + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "administrative-tours", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "mask = (\n", + " (\n", + " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + " )\n", + " .astype(\"float\")\n", + " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", + " .mean()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1d62ddf5-7993-4bde-ae05-eb6761b8472e", + "metadata": {}, + "source": [ + "### load in fire data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "black-michigan", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ").load()\n", + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "legendary-module", + "metadata": {}, + "outputs": [], + "source": [ + "ds = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", + " )\n", + " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", + " .where(fire_mask)\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .compute()\n", + ") * 100 # scale to percent" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "invalid-navigator", + "metadata": {}, + "outputs": [], + "source": [ + "maps = {}\n", + "maps[\"Fire\"] = (\n", + " ds.sel(scenario=\"ssp370\", year=slice(\"2080\", \"2099\"))\n", + " .mean(dim=\"year\")\n", + " .mean(dim=\"gcm\")\n", + " .drop(\"scenario\")\n", + " .compute()\n", + " .probability\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "focused-quest", + "metadata": {}, + "source": [ + "### load insects and drought\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "final-bailey", + "metadata": {}, + "outputs": [], + "source": [ + "for variable in [\"drought\", \"insects\"]:\n", + " maps[variable.capitalize()] = (\n", + " xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", + " variable\n", + " )\n", + " )\n", + " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", + " .sel(year=slice(2080, 2099))\n", + " .mean(dim=\"year\")\n", + " .probability.sel(scenario=\"ssp370\")\n", + " .mean(dim=\"gcm\")\n", + " .drop(\"scenario\")\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "photographic-label", + "metadata": {}, + "outputs": [], + "source": [ + "gcms = [\n", + " (\"MRI-ESM2-0\", (0, 0)),\n", + " (\"MIROC-ES2L\", (1, 0)),\n", + " (\"MPI-ESM1-2-LR\", (2, 0)),\n", + " (\"ACCESS-ESM1-5\", (3, 0)),\n", + " (\"ACCESS-CM2\", (4, 0)),\n", + " (\"CanESM5-CanOE\", (5, 0)),\n", + "]\n", + "titles = [\n", + " \"Burn area\\n[%/year]\",\n", + " \"Drought mortality\\n[]\",\n", + " \"Biotic agent mortality\\n[]\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "ca6db507-9958-460c-96c5-bf86d8ef4322", + "metadata": {}, + "source": [ + "### Load in biomass data from National biomass and carbon database\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e7baa3f-cca5-43f9-894f-2aba3939f6b7", + "metadata": {}, + "outputs": [], + "source": [ + "biomass = xr.open_rasterio(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-data/raw/nbcd/NBCD_countrywide_biomass_mosaic.tif\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a8a8263-dc0a-4d61-8a13-ef7ca000d58c", + "metadata": {}, + "outputs": [], + "source": [ + "reprojected_biomass = biomass.rio.reproject(plot.cartopy_proj_albers())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edea4770-28af-49ef-a94c-4f46b7cc87c7", + "metadata": {}, + "outputs": [], + "source": [ + "# convert from tons per pixel to tons/hectare by dividing by 5.76 (the # hectares in a 240 m pixel) per the readme\n", + "maps[\"Biomass\"] = reprojected_biomass / 5.76\n", + "# aggregate 64x64 240m cells for plotting\n", + "maps[\"Biomass\"] = (\n", + " maps[\"Biomass\"]\n", + " .coarsen(y=64, x=64, boundary=\"trim\")\n", + " .mean()\n", + " .isel(band=0)\n", + " .drop([\"band\", \"spatial_ref\"])\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9edc71f0-84d2-456a-8b50-086ab30d7459", + "metadata": {}, + "outputs": [], + "source": [ + "# for plotting purposes mask out anywhere that is less than 1 ton/ha\n", + "maps[\"Biomass\"] = maps[\"Biomass\"].where(maps[\"Biomass\"] > 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "absent-concern", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"Fire\": {\n", + " \"cmap\": get_colormap(\"reds\"),\n", + " \"var_lims\": (0, 3),\n", + " \"label\": \"Burn area\\n(%/year)\",\n", + " \"panel\": \"A\",\n", + " \"cbar_ylocation\": 0.55,\n", + " \"cbar_xlocation\": 0.44,\n", + " \"panel_location\": (0, 0),\n", + " },\n", + " \"Insects\": {\n", + " \"cmap\": get_colormap(\"blues\"),\n", + " \"var_lims\": (0, 0.8),\n", + " \"label\": \"Insect-related\\nmortality (%/year)\",\n", + " \"panel\": \"B\",\n", + " \"cbar_ylocation\": 0.55,\n", + " \"cbar_xlocation\": 0.96,\n", + " \"panel_location\": (0, 1),\n", + " },\n", + " \"Drought\": {\n", + " \"cmap\": get_colormap(\"pinks\"),\n", + " \"var_lims\": (0, 4),\n", + " \"label\": \"Drought-related\\nmortality (%/year)\",\n", + " \"panel\": \"C\",\n", + " \"cbar_ylocation\": 0.05,\n", + " \"cbar_xlocation\": 0.44,\n", + " \"panel_location\": (1, 0),\n", + " },\n", + " \"Biomass\": {\n", + " \"cmap\": get_colormap(\"greens\"),\n", + " \"var_lims\": (0, 300),\n", + " \"label\": \"Biomass (tons/ha)\",\n", + " \"panel\": \"D\",\n", + " \"cbar_ylocation\": 0.05,\n", + " \"cbar_xlocation\": 0.96,\n", + " \"panel_location\": (1, 1),\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e11da3f3-b015-4e1a-a825-6e6acab424e6", + "metadata": {}, + "outputs": [], + "source": [ + "def get_colormap(name):\n", + " if name == \"blues\":\n", + " return get_continuous_cmap([\"#CFE0F9\", \"#588EF9\", \"#0432A5\"])\n", + " elif name == \"pinks\":\n", + " return get_continuous_cmap([\"#F9C7ED\", \"#E563BA\", \"#770361\"])\n", + " elif name == \"reds\":\n", + " return get_continuous_cmap([\"#F9D3BD\", \"#E87A3D\", \"#752003\"])\n", + " elif name == \"greens\":\n", + " return get_continuous_cmap([\"#C9E4BF\", \"#76BA74\", \"#36763C\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sweet-arena", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "headed-employee", + "metadata": {}, + "outputs": [], + "source": [ + "state_borders, us_border = plot.cartopy_borders()\n", + "fig, axarr = plt.subplots(\n", + " nrows=2,\n", + " ncols=2,\n", + " figsize=(10, 6),\n", + " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", + ")\n", + "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", + "\n", + "for row, variable in enumerate([\"Fire\", \"Drought\", \"Insects\", \"Biomass\"]):\n", + " vmin, vmax = (\n", + " plot_params[variable][\"var_lims\"][0],\n", + " plot_params[variable][\"var_lims\"][1],\n", + " )\n", + " map_plot = maps[variable].plot.imshow(\n", + " ax=axarr[plot_params[variable][\"panel_location\"]],\n", + " cmap=plot_params[variable][\"cmap\"],\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " add_colorbar=False,\n", + " )\n", + " plot.map_pretty(axarr[plot_params[variable][\"panel_location\"]], title=\"\")\n", + "\n", + " axarr[plot_params[variable][\"panel_location\"]].text(\n", + " 0.12,\n", + " 1.05,\n", + " plot_params[variable][\"panel\"],\n", + " transform=axarr[plot_params[variable][\"panel_location\"]].transAxes,\n", + " fontsize=18,\n", + " )\n", + " cax = fig.add_axes(\n", + " [\n", + " plot_params[variable][\"cbar_xlocation\"],\n", + " plot_params[variable][\"cbar_ylocation\"],\n", + " 0.018,\n", + " 0.14,\n", + " ]\n", + " )\n", + " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", + "\n", + " cax.text(\n", + " 0.5,\n", + " -0.22,\n", + " plot_params[variable][\"var_lims\"][0],\n", + " transform=cax.transAxes,\n", + " horizontalalignment=\"center\",\n", + " )\n", + " cax.text(\n", + " 0.5,\n", + " 1.05,\n", + " plot_params[variable][\"var_lims\"][1],\n", + " transform=cax.transAxes,\n", + " horizontalalignment=\"center\",\n", + " )\n", + " cax.text(\n", + " 1.8,\n", + " 0.5,\n", + " plot_params[variable][\"label\"],\n", + " transform=cax.transAxes,\n", + " verticalalignment=\"center\",\n", + " multialignment=\"center\",\n", + " rotation=-90,\n", + " )\n", + " print(variable)\n", + " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", + " cbar.outline.set_visible(False)\n", + " cbar.set_ticks([])\n", + "plt.tight_layout(pad=-2)\n", + "for format_string in [\"svg\", \"png\"]:\n", + " fig.savefig(\n", + " \"Figure-Supp8.\" + format_string,\n", + " format=format_string,\n", + " bbox_inches=\"tight\",\n", + " )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "bizarre-semester", - "metadata": {}, - "outputs": [], - "source": [ - "alt.data_transformers.disable_max_rows()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "administrative-tours", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "mask = (\n", - " (\n", - " load.nlcd(store=\"az\", year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - " )\n", - " .astype(\"float\")\n", - " .coarsen(x=coarsen, y=coarsen, boundary=\"trim\")\n", - " .mean()\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "1d62ddf5-7993-4bde-ae05-eb6761b8472e", - "metadata": {}, - "source": [ - "### load in fire data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "black-michigan", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ").load()\n", - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "legendary-module", - "metadata": {}, - "outputs": [], - "source": [ - "ds = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_cmip.zarr\"\n", - " )\n", - " .assign_coords({\"x\": mask.x, \"y\": mask.y})\n", - " .where(fire_mask)\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .compute()\n", - ") * 100 # scale to percent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "invalid-navigator", - "metadata": {}, - "outputs": [], - "source": [ - "maps = {}\n", - "maps[\"Fire\"] = (\n", - " ds.sel(scenario=\"ssp370\", year=slice(\"2080\", \"2099\"))\n", - " .mean(dim=\"year\")\n", - " .mean(dim=\"gcm\")\n", - " .drop(\"scenario\")\n", - " .compute()\n", - " .probability\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "focused-quest", - "metadata": {}, - "source": [ - "### load insects and drought\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "final-bailey", - "metadata": {}, - "outputs": [], - "source": [ - "for variable in [\"drought\", \"insects\"]:\n", - " maps[variable.capitalize()] = (\n", - " xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/{}_cmip.zarr\".format(\n", - " variable\n", - " )\n", - " )\n", - " .assign_coords({\"year\": np.arange(1975, 2100, 10)})\n", - " .sel(year=slice(2080, 2099))\n", - " .mean(dim=\"year\")\n", - " .probability.sel(scenario=\"ssp370\")\n", - " .mean(dim=\"gcm\")\n", - " .drop(\"scenario\")\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "photographic-label", - "metadata": {}, - "outputs": [], - "source": [ - "gcms = [\n", - " (\"MRI-ESM2-0\", (0, 0)),\n", - " (\"MIROC-ES2L\", (1, 0)),\n", - " (\"MPI-ESM1-2-LR\", (2, 0)),\n", - " (\"ACCESS-ESM1-5\", (3, 0)),\n", - " (\"ACCESS-CM2\", (4, 0)),\n", - " (\"CanESM5-CanOE\", (5, 0)),\n", - "]\n", - "titles = [\n", - " \"Burn area\\n[%/year]\",\n", - " \"Drought mortality\\n[]\",\n", - " \"Biotic agent mortality\\n[]\",\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "ca6db507-9958-460c-96c5-bf86d8ef4322", - "metadata": {}, - "source": [ - "### Load in biomass data from National biomass and carbon database\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e7baa3f-cca5-43f9-894f-2aba3939f6b7", - "metadata": {}, - "outputs": [], - "source": [ - "biomass = xr.open_rasterio(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-data/raw/nbcd/NBCD_countrywide_biomass_mosaic.tif\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a8a8263-dc0a-4d61-8a13-ef7ca000d58c", - "metadata": {}, - "outputs": [], - "source": [ - "reprojected_biomass = biomass.rio.reproject(plot.cartopy_proj_albers())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "edea4770-28af-49ef-a94c-4f46b7cc87c7", - "metadata": {}, - "outputs": [], - "source": [ - "# convert from tons per pixel to tons/hectare by dividing by 5.76 (the # hectares in a 240 m pixel) per the readme\n", - "maps[\"Biomass\"] = reprojected_biomass / 5.76\n", - "# aggregate 64x64 240m cells for plotting\n", - "maps[\"Biomass\"] = (\n", - " maps[\"Biomass\"]\n", - " .coarsen(y=64, x=64, boundary=\"trim\")\n", - " .mean()\n", - " .isel(band=0)\n", - " .drop([\"band\", \"spatial_ref\"])\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9edc71f0-84d2-456a-8b50-086ab30d7459", - "metadata": {}, - "outputs": [], - "source": [ - "# for plotting purposes mask out anywhere that is less than 1 ton/ha\n", - "maps[\"Biomass\"] = maps[\"Biomass\"].where(maps[\"Biomass\"] > 1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "absent-concern", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"Fire\": {\n", - " \"cmap\": get_colormap(\"reds\"),\n", - " \"var_lims\": (0, 3),\n", - " \"label\": \"Burn area\\n(%/year)\",\n", - " \"panel\": \"A\",\n", - " \"cbar_ylocation\": 0.55,\n", - " \"cbar_xlocation\": 0.44,\n", - " \"panel_location\": (0, 0),\n", - " },\n", - " \"Insects\": {\n", - " \"cmap\": get_colormap(\"blues\"),\n", - " \"var_lims\": (0, 0.8),\n", - " \"label\": \"Insect-related\\nmortality (%/year)\",\n", - " \"panel\": \"B\",\n", - " \"cbar_ylocation\": 0.55,\n", - " \"cbar_xlocation\": 0.96,\n", - " \"panel_location\": (0, 1),\n", - " },\n", - " \"Drought\": {\n", - " \"cmap\": get_colormap(\"pinks\"),\n", - " \"var_lims\": (0, 4),\n", - " \"label\": \"Drought-related\\nmortality (%/year)\",\n", - " \"panel\": \"C\",\n", - " \"cbar_ylocation\": 0.05,\n", - " \"cbar_xlocation\": 0.44,\n", - " \"panel_location\": (1, 0),\n", - " },\n", - " \"Biomass\": {\n", - " \"cmap\": get_colormap(\"greens\"),\n", - " \"var_lims\": (0, 300),\n", - " \"label\": \"Biomass (tons/ha)\",\n", - " \"panel\": \"D\",\n", - " \"cbar_ylocation\": 0.05,\n", - " \"cbar_xlocation\": 0.96,\n", - " \"panel_location\": (1, 1),\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e11da3f3-b015-4e1a-a825-6e6acab424e6", - "metadata": {}, - "outputs": [], - "source": [ - "def get_colormap(name):\n", - " if name == \"blues\":\n", - " return get_continuous_cmap([\"#CFE0F9\", \"#588EF9\", \"#0432A5\"])\n", - " elif name == \"pinks\":\n", - " return get_continuous_cmap([\"#F9C7ED\", \"#E563BA\", \"#770361\"])\n", - " elif name == \"reds\":\n", - " return get_continuous_cmap([\"#F9D3BD\", \"#E87A3D\", \"#752003\"])\n", - " elif name == \"greens\":\n", - " return get_continuous_cmap([\"#C9E4BF\", \"#76BA74\", \"#36763C\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sweet-arena", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "headed-employee", - "metadata": {}, - "outputs": [], - "source": [ - "state_borders, us_border = plot.cartopy_borders()\n", - "fig, axarr = plt.subplots(\n", - " nrows=2,\n", - " ncols=2,\n", - " figsize=(10, 6),\n", - " subplot_kw={\"projection\": plot.cartopy_proj_albers()},\n", - ")\n", - "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", - "\n", - "for row, variable in enumerate([\"Fire\", \"Drought\", \"Insects\", \"Biomass\"]):\n", - " vmin, vmax = (\n", - " plot_params[variable][\"var_lims\"][0],\n", - " plot_params[variable][\"var_lims\"][1],\n", - " )\n", - " map_plot = maps[variable].plot.imshow(\n", - " ax=axarr[plot_params[variable][\"panel_location\"]],\n", - " cmap=plot_params[variable][\"cmap\"],\n", - " vmin=vmin,\n", - " vmax=vmax,\n", - " add_colorbar=False,\n", - " )\n", - " plot.map_pretty(axarr[plot_params[variable][\"panel_location\"]], title=\"\")\n", - "\n", - " axarr[plot_params[variable][\"panel_location\"]].text(\n", - " 0.12,\n", - " 1.05,\n", - " plot_params[variable][\"panel\"],\n", - " transform=axarr[plot_params[variable][\"panel_location\"]].transAxes,\n", - " fontsize=18,\n", - " )\n", - " cax = fig.add_axes(\n", - " [\n", - " plot_params[variable][\"cbar_xlocation\"],\n", - " plot_params[variable][\"cbar_ylocation\"],\n", - " 0.018,\n", - " 0.14,\n", - " ]\n", - " )\n", - " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", - "\n", - " cax.text(\n", - " 0.5,\n", - " -0.22,\n", - " plot_params[variable][\"var_lims\"][0],\n", - " transform=cax.transAxes,\n", - " horizontalalignment=\"center\",\n", - " )\n", - " cax.text(\n", - " 0.5,\n", - " 1.05,\n", - " plot_params[variable][\"var_lims\"][1],\n", - " transform=cax.transAxes,\n", - " horizontalalignment=\"center\",\n", - " )\n", - " cax.text(\n", - " 1.8,\n", - " 0.5,\n", - " plot_params[variable][\"label\"],\n", - " transform=cax.transAxes,\n", - " verticalalignment=\"center\",\n", - " multialignment=\"center\",\n", - " rotation=-90,\n", - " )\n", - " print(variable)\n", - " cbar = fig.colorbar(map_plot, cax=cax, orientation=\"vertical\")\n", - " cbar.outline.set_visible(False)\n", - " cbar.set_ticks([])\n", - "plt.tight_layout(pad=-2)\n", - "for format_string in [\"svg\", \"png\"]:\n", - " fig.savefig(\n", - " \"Figure-Supp8.\" + format_string,\n", - " format=format_string,\n", - " bbox_inches=\"tight\",\n", - " )" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-09/Supplementary-Figure-09.ipynb b/notebooks/paper/Supplementary-Figure-09/Supplementary-Figure-09.ipynb index b00383d..bcc40f1 100644 --- a/notebooks/paper/Supplementary-Figure-09/Supplementary-Figure-09.ipynb +++ b/notebooks/paper/Supplementary-Figure-09/Supplementary-Figure-09.ipynb @@ -1,136 +1,136 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "loaded-sullivan", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "loaded-sullivan", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "comparable-revision", + "metadata": {}, + "outputs": [], + "source": [ + "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "accessible-relations", + "metadata": {}, + "outputs": [], + "source": [ + "df_drought_coef = pd.read_csv(\n", + " \"SuppFig_DroughtModelCoefficients-WeightedUnweighted_4-26-21.csv\"\n", + ")\n", + "df_drought_pred = pd.read_csv(\n", + " \"SuppFig_DroughtModelMortPredictions-WeightedUnweighted_4-26-21.csv\"\n", + ")\n", + "df_insects_coef = pd.read_csv(\n", + " \"SuppFig_InsectModelCoefficients-WeightedUnweighted_4-26-21.csv\"\n", + ")\n", + "df_insects_pred = pd.read_csv(\n", + " \"SuppFig_InsectModelMortPredictions-WeightedUnweighted_4-26-21.csv\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "complimentary-efficiency", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(2, 2, figsize=(12, 12))\n", + "\n", + "\n", + "def style(ax):\n", + " ax.spines[\"right\"].set_visible(False)\n", + " ax.spines[\"top\"].set_visible(False)\n", + "\n", + "\n", + "axs[0][0].plot([-8, 8], [-8, 8], \"gray\")\n", + "axs[0][0].plot(df_drought_coef[\"V1\"], df_drought_coef[\"V2\"], \"k.\")\n", + "axs[0][0].set_ylim([-8, 8])\n", + "axs[0][0].set_xlim([-8, 8])\n", + "axs[0][0].set_xticks([-5, 0, 5])\n", + "axs[0][0].set_yticks([-5, 0, 5])\n", + "axs[0][0].set_ylabel(\"Weighted coefficient\")\n", + "style(axs[0][0])\n", + "\n", + "axs[0][1].plot([-0.005, 0.04], [-0.005, 0.04], \"gray\")\n", + "axs[0][1].plot(df_drought_pred[\"V1\"], df_drought_pred[\"V2\"], \"k.\")\n", + "axs[0][1].set_ylim([-0.005, 0.04])\n", + "axs[0][1].set_xlim([-0.005, 0.04])\n", + "axs[0][1].set_xticks([0, 0.01, 0.02, 0.03, 0.04])\n", + "axs[0][1].set_yticks([0, 0.01, 0.02, 0.03, 0.04])\n", + "axs[0][1].set_ylabel(\"Weighted mortality (%/year)\")\n", + "axs[0][1].plot([-8, -8], [8, 8], \"k\")\n", + "style(axs[0][1])\n", + "\n", + "axs[1][0].plot([-8, 8], [-8, 8], \"gray\")\n", + "axs[1][0].plot(df_insects_coef[\"V1\"], df_insects_coef[\"V2\"], \"k.\")\n", + "axs[1][0].set_ylim([-8, 8])\n", + "axs[1][0].set_xlim([-8, 8])\n", + "axs[1][0].set_xticks([-5, 0, 5])\n", + "axs[1][0].set_yticks([-5, 0, 5])\n", + "axs[1][0].set_ylabel(\"Weighted coefficient\")\n", + "axs[1][0].set_xlabel(\"Unweighted coefficient\")\n", + "style(axs[1][0])\n", + "\n", + "axs[1][1].plot([-0.005, 0.04], [-0.005, 0.04], \"gray\")\n", + "axs[1][1].plot(df_insects_pred[\"V1\"], df_insects_pred[\"V2\"], \"k.\")\n", + "axs[1][1].set_ylim([-0.005, 0.04])\n", + "axs[1][1].set_xlim([-0.005, 0.04])\n", + "axs[1][1].set_xticks([0, 0.01, 0.02, 0.03, 0.04])\n", + "axs[1][1].set_yticks([0, 0.01, 0.02, 0.03, 0.04])\n", + "axs[1][1].set_ylabel(\"Weighted mortality (%/year)\")\n", + "axs[1][1].set_xlabel(\"Unweighted mortality (%/year)\")\n", + "style(axs[1][1])\n", + "\n", + "plt.savefig(\"Supplementary-Figure-09.svg\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "whole-citation", + "metadata": {}, + "outputs": [], + "source": [ + "inds = ~np.isnan(df_insects_pred[\"V1\"]) & ~np.isnan(df_insects_pred[\"V2\"])\n", + "np.corrcoef(df_insects_pred[\"V1\"][inds], df_insects_pred[\"V2\"][inds]) ** 2" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "comparable-revision", - "metadata": {}, - "outputs": [], - "source": [ - "plt.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "accessible-relations", - "metadata": {}, - "outputs": [], - "source": [ - "df_drought_coef = pd.read_csv(\n", - " \"SuppFig_DroughtModelCoefficients-WeightedUnweighted_4-26-21.csv\"\n", - ")\n", - "df_drought_pred = pd.read_csv(\n", - " \"SuppFig_DroughtModelMortPredictions-WeightedUnweighted_4-26-21.csv\"\n", - ")\n", - "df_insects_coef = pd.read_csv(\n", - " \"SuppFig_InsectModelCoefficients-WeightedUnweighted_4-26-21.csv\"\n", - ")\n", - "df_insects_pred = pd.read_csv(\n", - " \"SuppFig_InsectModelMortPredictions-WeightedUnweighted_4-26-21.csv\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "complimentary-efficiency", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axs = plt.subplots(2, 2, figsize=(12, 12))\n", - "\n", - "\n", - "def style(ax):\n", - " ax.spines[\"right\"].set_visible(False)\n", - " ax.spines[\"top\"].set_visible(False)\n", - "\n", - "\n", - "axs[0][0].plot([-8, 8], [-8, 8], \"gray\")\n", - "axs[0][0].plot(df_drought_coef[\"V1\"], df_drought_coef[\"V2\"], \"k.\")\n", - "axs[0][0].set_ylim([-8, 8])\n", - "axs[0][0].set_xlim([-8, 8])\n", - "axs[0][0].set_xticks([-5, 0, 5])\n", - "axs[0][0].set_yticks([-5, 0, 5])\n", - "axs[0][0].set_ylabel(\"Weighted coefficient\")\n", - "style(axs[0][0])\n", - "\n", - "axs[0][1].plot([-0.005, 0.04], [-0.005, 0.04], \"gray\")\n", - "axs[0][1].plot(df_drought_pred[\"V1\"], df_drought_pred[\"V2\"], \"k.\")\n", - "axs[0][1].set_ylim([-0.005, 0.04])\n", - "axs[0][1].set_xlim([-0.005, 0.04])\n", - "axs[0][1].set_xticks([0, 0.01, 0.02, 0.03, 0.04])\n", - "axs[0][1].set_yticks([0, 0.01, 0.02, 0.03, 0.04])\n", - "axs[0][1].set_ylabel(\"Weighted mortality (%/year)\")\n", - "axs[0][1].plot([-8, -8], [8, 8], \"k\")\n", - "style(axs[0][1])\n", - "\n", - "axs[1][0].plot([-8, 8], [-8, 8], \"gray\")\n", - "axs[1][0].plot(df_insects_coef[\"V1\"], df_insects_coef[\"V2\"], \"k.\")\n", - "axs[1][0].set_ylim([-8, 8])\n", - "axs[1][0].set_xlim([-8, 8])\n", - "axs[1][0].set_xticks([-5, 0, 5])\n", - "axs[1][0].set_yticks([-5, 0, 5])\n", - "axs[1][0].set_ylabel(\"Weighted coefficient\")\n", - "axs[1][0].set_xlabel(\"Unweighted coefficient\")\n", - "style(axs[1][0])\n", - "\n", - "axs[1][1].plot([-0.005, 0.04], [-0.005, 0.04], \"gray\")\n", - "axs[1][1].plot(df_insects_pred[\"V1\"], df_insects_pred[\"V2\"], \"k.\")\n", - "axs[1][1].set_ylim([-0.005, 0.04])\n", - "axs[1][1].set_xlim([-0.005, 0.04])\n", - "axs[1][1].set_xticks([0, 0.01, 0.02, 0.03, 0.04])\n", - "axs[1][1].set_yticks([0, 0.01, 0.02, 0.03, 0.04])\n", - "axs[1][1].set_ylabel(\"Weighted mortality (%/year)\")\n", - "axs[1][1].set_xlabel(\"Unweighted mortality (%/year)\")\n", - "style(axs[1][1])\n", - "\n", - "plt.savefig(\"Supplementary-Figure-09.svg\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "whole-citation", - "metadata": {}, - "outputs": [], - "source": [ - "inds = ~np.isnan(df_insects_pred[\"V1\"]) & ~np.isnan(df_insects_pred[\"V2\"])\n", - "np.corrcoef(df_insects_pred[\"V1\"][inds], df_insects_pred[\"V2\"][inds]) ** 2" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-10/Supplementary-Figure-10.ipynb b/notebooks/paper/Supplementary-Figure-10/Supplementary-Figure-10.ipynb index d1c410e..ecc4631 100644 --- a/notebooks/paper/Supplementary-Figure-10/Supplementary-Figure-10.ipynb +++ b/notebooks/paper/Supplementary-Figure-10/Supplementary-Figure-10.ipynb @@ -1,173 +1,173 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "angry-journey", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import altair as alt\n", - "import rioxarray\n", - "from carbonplan.data import cat\n", - "from carbonplan_styles.mpl import get_colormap\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import cartopy.feature as cfeature\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib\n", - "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", - "\n", - "from carbonplan_data import utils\n", - "alt.data_transformers.disable_max_rows()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "angry-journey", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import altair as alt\n", + "import rioxarray\n", + "from carbonplan.data import cat\n", + "from carbonplan_styles.mpl import get_colormap\n", + "import cartopy.crs as ccrs\n", + "import cartopy\n", + "import cartopy.feature as cfeature\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", + "\n", + "from carbonplan_data import utils\n", + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "requested-jamaica", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "store = \"az\"\n", + "tlim = (\"1984\", \"2018\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "strong-freight", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "closed-jimmy", + "metadata": {}, + "outputs": [], + "source": [ + "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "broad-isolation", + "metadata": {}, + "outputs": [], + "source": [ + "historical_fire = (\n", + " historical_fire.groupby(\"time.month\")\n", + " .mean()\n", + " .where(fire_mask)\n", + " .compute()[\"historical\"]\n", + ") * 100 # cast to percent" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "plain-benefit", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"cmap\": get_colormap(\"reds\"),\n", + " \"var_lims\": (0, 0.03),\n", + " \"label\": \"Burn area\\n(%/year)\",\n", + " \"y_loc\": 0.2,\n", + " \"cbar_height\": 0.6,\n", + " \"x_loc\": 1.08,\n", + " \"width\": 0.03,\n", + "}\n", + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "contrary-employee", + "metadata": {}, + "outputs": [], + "source": [ + "months = [\"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "diverse-buddy", + "metadata": {}, + "outputs": [], + "source": [ + "p = historical_fire.sel(month=slice(3, 11)).plot.imshow(\n", + " col=\"month\",\n", + " col_wrap=3,\n", + " vmin=0,\n", + " vmax=0.1,\n", + " subplot_kws={\"projection\": plot.cartopy_proj_albers()},\n", + " add_colorbar=False,\n", + " cmap=plot_params[\"cmap\"],\n", + " figsize=(8, 6),\n", + ")\n", + "for i, ax in enumerate(p.axes.flat):\n", + " plot.map_pretty(ax, title=months[i])\n", + " ax.set_title(\"\")\n", + "\n", + "plot.add_colorbar(\n", + " p.fig,\n", + " y_location=plot_params[\"y_loc\"],\n", + " vmin=plot_params[\"var_lims\"][0],\n", + " vmax=plot_params[\"var_lims\"][1],\n", + " cbar_label=plot_params[\"label\"],\n", + " cmap=plot_params[\"cmap\"],\n", + " height=plot_params[\"cbar_height\"],\n", + " x_location=plot_params[\"x_loc\"],\n", + " width=plot_params[\"width\"],\n", + ")\n", + "plt.tight_layout(pad=-4)\n", + "plt.savefig(\"fig_supp7.svg\", format=\"svg\", bbox_inches=\"tight\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "requested-jamaica", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "store = \"az\"\n", - "tlim = (\"1984\", \"2018\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "strong-freight", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "closed-jimmy", - "metadata": {}, - "outputs": [], - "source": [ - "fire_mask = ~np.isnan(historical_fire.historical.isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "broad-isolation", - "metadata": {}, - "outputs": [], - "source": [ - "historical_fire = (\n", - " historical_fire.groupby(\"time.month\")\n", - " .mean()\n", - " .where(fire_mask)\n", - " .compute()[\"historical\"]\n", - ") * 100 # cast to percent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "plain-benefit", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"cmap\": get_colormap(\"reds\"),\n", - " \"var_lims\": (0, 0.03),\n", - " \"label\": \"Burn area\\n(%/year)\",\n", - " \"y_loc\": 0.2,\n", - " \"cbar_height\": 0.6,\n", - " \"x_loc\": 1.08,\n", - " \"width\": 0.03,\n", - "}\n", - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "contrary-employee", - "metadata": {}, - "outputs": [], - "source": [ - "months = [\"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "diverse-buddy", - "metadata": {}, - "outputs": [], - "source": [ - "p = historical_fire.sel(month=slice(3, 11)).plot.imshow(\n", - " col=\"month\",\n", - " col_wrap=3,\n", - " vmin=0,\n", - " vmax=0.1,\n", - " subplot_kws={\"projection\": plot.cartopy_proj_albers()},\n", - " add_colorbar=False,\n", - " cmap=plot_params[\"cmap\"],\n", - " figsize=(8, 6),\n", - ")\n", - "for i, ax in enumerate(p.axes.flat):\n", - " plot.map_pretty(ax, title=months[i])\n", - " ax.set_title(\"\")\n", - "\n", - "plot.add_colorbar(\n", - " p.fig,\n", - " y_location=plot_params[\"y_loc\"],\n", - " vmin=plot_params[\"var_lims\"][0],\n", - " vmax=plot_params[\"var_lims\"][1],\n", - " cbar_label=plot_params[\"label\"],\n", - " cmap=plot_params[\"cmap\"],\n", - " height=plot_params[\"cbar_height\"],\n", - " x_location=plot_params[\"x_loc\"],\n", - " width=plot_params[\"width\"],\n", - ")\n", - "plt.tight_layout(pad=-4)\n", - "plt.savefig(\"fig_supp7.svg\", format=\"svg\", bbox_inches=\"tight\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/paper/Supplementary-Figure-11/Supplementary-Figure-11.ipynb b/notebooks/paper/Supplementary-Figure-11/Supplementary-Figure-11.ipynb index a03f701..cb7570e 100644 --- a/notebooks/paper/Supplementary-Figure-11/Supplementary-Figure-11.ipynb +++ b/notebooks/paper/Supplementary-Figure-11/Supplementary-Figure-11.ipynb @@ -1,230 +1,230 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "utility-appeal", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "import scipy as sp\n", - "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", - "import xarray as xr\n", - "from carbonplan_forest_risks.utils import get_store\n", - "import altair as alt\n", - "import rioxarray\n", - "from carbonplan.data import cat\n", - "from carbonplan_styles.mpl import get_colormap\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import cartopy.feature as cfeature\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib\n", - "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", - "\n", - "from carbonplan_data import utils\n", - "alt.data_transformers.disable_max_rows()" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "utility-appeal", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from carbonplan_forest_risks import load, setup, plot, fit, utils, prepare, collect\n", + "import xarray as xr\n", + "from carbonplan_forest_risks.utils import get_store\n", + "import altair as alt\n", + "import rioxarray\n", + "from carbonplan.data import cat\n", + "from carbonplan_styles.mpl import get_colormap\n", + "import cartopy.crs as ccrs\n", + "import cartopy\n", + "import cartopy.feature as cfeature\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", + "\n", + "from carbonplan_data import utils\n", + "alt.data_transformers.disable_max_rows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "measured-switzerland", + "metadata": {}, + "outputs": [], + "source": [ + "coarsen = 4\n", + "store = \"az\"\n", + "tlim = (\"1984\", \"2018\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "comparable-insertion", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict = {\"Observed\": {}, \"Modeled\": {}}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "foster-nature", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict[\"Modeled\"][\"raw\"] = historical_fire = historical_fire = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "inner-kennedy", + "metadata": {}, + "outputs": [], + "source": [ + "fire_mask = ~np.isnan(ds_dict[\"Modeled\"][\"raw\"].isel(time=0).drop(\"time\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "integral-fireplace", + "metadata": {}, + "outputs": [], + "source": [ + "forest_mask = (\n", + " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", + " > 0.25\n", + ").astype(\"float\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "nearby-paradise", + "metadata": {}, + "outputs": [], + "source": [ + "ds_dict[\"Observed\"][\"raw\"] = load.mtbs(\n", + " store=store, coarsen=coarsen, tlim=tlim, mask=forest_mask\n", + ")\n", + "ds_dict[\"Observed\"][\"raw\"] = (\n", + " ds_dict[\"Observed\"][\"raw\"]\n", + " .assign_coords(\n", + " {\"x\": ds_dict[\"Modeled\"][\"raw\"].x, \"y\": ds_dict[\"Modeled\"][\"raw\"].y}\n", + " )\n", + " .assign_coords(\n", + " {\n", + " \"lat\": ds_dict[\"Modeled\"][\"raw\"].lat,\n", + " \"lon\": ds_dict[\"Modeled\"][\"raw\"].lon,\n", + " }\n", + " )[\"monthly\"]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "constitutional-pursuit", + "metadata": {}, + "outputs": [], + "source": [ + "for setup in [\"Observed\", \"Modeled\"]:\n", + " ds_dict[setup][\"annual\"] = (\n", + " ds_dict[setup][\"raw\"]\n", + " .groupby(\"time.year\")\n", + " .sum()\n", + " .where(fire_mask)\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .compute()\n", + " ) * 100\n", + " ds_dict[setup][\"seasonal\"] = (\n", + " ds_dict[setup][\"raw\"]\n", + " .groupby(\"time.month\")\n", + " .mean()\n", + " .where(fire_mask)\n", + " .mean(dim=[\"x\", \"y\"])\n", + " .compute()\n", + " ) * 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "vocal-earthquake", + "metadata": {}, + "outputs": [], + "source": [ + "plot_params = {\n", + " \"annual\": {\"y_label\": \"Annual burn area\\n(%/year)\"},\n", + " \"seasonal\": {\"y_label\": \"Monthly burn area\\n(%/month)\"},\n", + " \"colors\": {\"Modeled\": \"#E87A3D\", \"Observed\": \"grey\"},\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "challenging-progressive", + "metadata": {}, + "outputs": [], + "source": [ + "matplotlib.rc(\"font\", family=\"sans-serif\")\n", + "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", + "matplotlib.rc(\"text\", usetex=\"false\")\n", + "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "lucky-capability", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axarr = plt.subplots(nrows=2, figsize=(8, 8))\n", + "for setup in [\"Observed\", \"Modeled\"]:\n", + " ds_dict[setup][\"annual\"].historical.plot(\n", + " ax=axarr[0], color=plot_params[\"colors\"][setup], label=setup\n", + " )\n", + " axarr[0].set_ylabel(plot_params[\"annual\"][\"y_label\"])\n", + " ds_dict[setup][\"seasonal\"].historical.plot(\n", + " ax=axarr[1], color=plot_params[\"colors\"][setup], label=setup\n", + " )\n", + " axarr[1].set_ylabel(plot_params[\"seasonal\"][\"y_label\"])\n", + "axarr[0].set_xlabel(\"\")\n", + "axarr[1].set_xlabel(\"\")\n", + "axarr[0].legend()\n", + "axarr[1].set_xticks(np.arange(1, 13))\n", + "axarr[1].set_xticklabels(\n", + " [\n", + " \"Jan\",\n", + " \"Feb\",\n", + " \"Mar\",\n", + " \"Apr\",\n", + " \"May\",\n", + " \"Jun\",\n", + " \"Jul\",\n", + " \"Aug\",\n", + " \"Sep\",\n", + " \"Oct\",\n", + " \"Nov\",\n", + " \"Dec\",\n", + " ]\n", + ")\n", + "# plt.savefig(\"supp11.svg\", format=\"svg\", bbox_inches=\"tight\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "measured-switzerland", - "metadata": {}, - "outputs": [], - "source": [ - "coarsen = 4\n", - "store = \"az\"\n", - "tlim = (\"1984\", \"2018\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "comparable-insertion", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict = {\"Observed\": {}, \"Modeled\": {}}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "foster-nature", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict[\"Modeled\"][\"raw\"] = historical_fire = historical_fire = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/paper/fire_terraclimate.zarr\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "inner-kennedy", - "metadata": {}, - "outputs": [], - "source": [ - "fire_mask = ~np.isnan(ds_dict[\"Modeled\"][\"raw\"].isel(time=0).drop(\"time\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "integral-fireplace", - "metadata": {}, - "outputs": [], - "source": [ - "forest_mask = (\n", - " load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum(\"band\")\n", - " > 0.25\n", - ").astype(\"float\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "nearby-paradise", - "metadata": {}, - "outputs": [], - "source": [ - "ds_dict[\"Observed\"][\"raw\"] = load.mtbs(\n", - " store=store, coarsen=coarsen, tlim=tlim, mask=forest_mask\n", - ")\n", - "ds_dict[\"Observed\"][\"raw\"] = (\n", - " ds_dict[\"Observed\"][\"raw\"]\n", - " .assign_coords(\n", - " {\"x\": ds_dict[\"Modeled\"][\"raw\"].x, \"y\": ds_dict[\"Modeled\"][\"raw\"].y}\n", - " )\n", - " .assign_coords(\n", - " {\n", - " \"lat\": ds_dict[\"Modeled\"][\"raw\"].lat,\n", - " \"lon\": ds_dict[\"Modeled\"][\"raw\"].lon,\n", - " }\n", - " )[\"monthly\"]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "constitutional-pursuit", - "metadata": {}, - "outputs": [], - "source": [ - "for setup in [\"Observed\", \"Modeled\"]:\n", - " ds_dict[setup][\"annual\"] = (\n", - " ds_dict[setup][\"raw\"]\n", - " .groupby(\"time.year\")\n", - " .sum()\n", - " .where(fire_mask)\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .compute()\n", - " ) * 100\n", - " ds_dict[setup][\"seasonal\"] = (\n", - " ds_dict[setup][\"raw\"]\n", - " .groupby(\"time.month\")\n", - " .mean()\n", - " .where(fire_mask)\n", - " .mean(dim=[\"x\", \"y\"])\n", - " .compute()\n", - " ) * 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "vocal-earthquake", - "metadata": {}, - "outputs": [], - "source": [ - "plot_params = {\n", - " \"annual\": {\"y_label\": \"Annual burn area\\n(%/year)\"},\n", - " \"seasonal\": {\"y_label\": \"Monthly burn area\\n(%/month)\"},\n", - " \"colors\": {\"Modeled\": \"#E87A3D\", \"Observed\": \"grey\"},\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "challenging-progressive", - "metadata": {}, - "outputs": [], - "source": [ - "matplotlib.rc(\"font\", family=\"sans-serif\")\n", - "matplotlib.rc(\"font\", serif=\"Helvetica Neue\")\n", - "matplotlib.rc(\"text\", usetex=\"false\")\n", - "matplotlib.rcParams.update({\"font.size\": 14, \"svg.fonttype\": \"none\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "lucky-capability", - "metadata": {}, - "outputs": [], - "source": [ - "fig, axarr = plt.subplots(nrows=2, figsize=(8, 8))\n", - "for setup in [\"Observed\", \"Modeled\"]:\n", - " ds_dict[setup][\"annual\"].historical.plot(\n", - " ax=axarr[0], color=plot_params[\"colors\"][setup], label=setup\n", - " )\n", - " axarr[0].set_ylabel(plot_params[\"annual\"][\"y_label\"])\n", - " ds_dict[setup][\"seasonal\"].historical.plot(\n", - " ax=axarr[1], color=plot_params[\"colors\"][setup], label=setup\n", - " )\n", - " axarr[1].set_ylabel(plot_params[\"seasonal\"][\"y_label\"])\n", - "axarr[0].set_xlabel(\"\")\n", - "axarr[1].set_xlabel(\"\")\n", - "axarr[0].legend()\n", - "axarr[1].set_xticks(np.arange(1, 13))\n", - "axarr[1].set_xticklabels(\n", - " [\n", - " \"Jan\",\n", - " \"Feb\",\n", - " \"Mar\",\n", - " \"Apr\",\n", - " \"May\",\n", - " \"Jun\",\n", - " \"Jul\",\n", - " \"Aug\",\n", - " \"Sep\",\n", - " \"Oct\",\n", - " \"Nov\",\n", - " \"Dec\",\n", - " ]\n", - ")\n", - "# plt.savefig(\"supp11.svg\", format=\"svg\", bbox_inches=\"tight\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/notebooks/share/fire_risks_counties.ipynb b/notebooks/share/fire_risks_counties.ipynb index 88912f5..d581bc0 100644 --- a/notebooks/share/fire_risks_counties.ipynb +++ b/notebooks/share/fire_risks_counties.ipynb @@ -1,219 +1,219 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "947b0886", - "metadata": {}, - "outputs": [], - "source": [ - "import xarray as xr\n", - "import numpy as np\n", - "import pandas as pd\n", - "from showit import image\n", - "from scipy.stats import binom\n", - "import matplotlib.pyplot as plt\n", - "import geopandas\n", - "import rasterio\n", - "from rasterio import Affine\n", - "from tqdm import tqdm" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "947b0886", + "metadata": {}, + "outputs": [], + "source": [ + "import xarray as xr\n", + "import numpy as np\n", + "import pandas as pd\n", + "from showit import image\n", + "from scipy.stats import binom\n", + "import matplotlib.pyplot as plt\n", + "import geopandas\n", + "import rasterio\n", + "from rasterio import Affine\n", + "from tqdm import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efe27b80", + "metadata": {}, + "outputs": [], + "source": [ + "def integrated_risk(p):\n", + " return (1 - binom.cdf(0, 20, p)) * 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6da00394", + "metadata": {}, + "outputs": [], + "source": [ + "ds = xr.open_zarr(\n", + " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/web/fire.zarr\"\n", + ").load()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fa21c48", + "metadata": {}, + "outputs": [], + "source": [ + "counties = geopandas.read_file(\n", + " \"cb_2021_us_county_500k/cb_2021_us_county_500k.shp\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5bae54f7", + "metadata": {}, + "outputs": [], + "source": [ + "exclude = list([\"02\", \"15\", \"60\", \"66\", \"69\", \"72\", \"78\"])\n", + "counties = counties[list(map(lambda x: x not in exclude, counties[\"STATEFP\"]))]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e356a4d", + "metadata": {}, + "outputs": [], + "source": [ + "counties = counties.to_crs(\"EPSG:5070\").reset_index()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3363b94", + "metadata": {}, + "outputs": [], + "source": [ + "get_mask = lambda geometry: rasterio.features.geometry_mask(\n", + " [geometry],\n", + " out_shape=ds.lat.shape,\n", + " transform=Affine(4000, 0.0, -2493045.0, 0.0, -4000, 3310005.0),\n", + " all_touched=False,\n", + " invert=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd56a44c", + "metadata": {}, + "outputs": [], + "source": [ + "scenarios = [\"ssp245\", \"ssp370\", \"ssp585\"]\n", + "columns = [\"state_code\", \"state_name\", \"county_code\", \"county_name\"] + list(\n", + " ds.year.values\n", + ")\n", + "data = {key: [] for key in scenarios}\n", + "dfs = {key: [] for key in scenarios}\n", + "\n", + "for index, county in tqdm(counties.iterrows()):\n", + " mask = np.tile(get_mask(county[\"geometry\"]), [12, 1, 1])\n", + " means = ds.where(mask).mean([\"x\", \"y\"])\n", + " for scenario in scenarios:\n", + " values = integrated_risk(means[scenario].values)\n", + " data[scenario].append(\n", + " [\n", + " county[\"STATEFP\"],\n", + " county[\"STUSPS\"],\n", + " county[\"COUNTYFP\"],\n", + " county[\"NAME\"],\n", + " ]\n", + " + list(values)\n", + " )\n", + "\n", + "for scenario in scenarios:\n", + " dfs[scenario] = pd.DataFrame(data[scenario], columns=columns)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44b0b5be", + "metadata": {}, + "outputs": [], + "source": [ + "dfs[\"ssp245\"].dropna().reset_index(drop=True).to_csv(\n", + " \"CarbonPlan-Fire-Risk-SSP-4.5.csv\", index=False, header=True\n", + ")\n", + "dfs[\"ssp370\"].dropna().reset_index(drop=True).to_csv(\n", + " \"CarbonPlan-Fire-Risk-SSP-7.0.csv\", index=False, header=True\n", + ")\n", + "dfs[\"ssp585\"].dropna().reset_index(drop=True).to_csv(\n", + " \"CarbonPlan-Fire-Risk-SSP-8.5.csv\", index=False, header=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f433c8a2", + "metadata": {}, + "outputs": [], + "source": [ + "len(dfs[\"ssp585\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38573ebc", + "metadata": {}, + "outputs": [], + "source": [ + "counties[\"fire\"] = dfs[\"ssp585\"][\"2050\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e0701ae", + "metadata": {}, + "outputs": [], + "source": [ + "counties.plot(\n", + " column=\"fire\", cmap=\"hot\", vmin=0, vmax=50, figsize=(15, 10), legend=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7aa7c6a8", + "metadata": {}, + "outputs": [], + "source": [ + "counties.plot(column=\"fire\", cmap=\"hot\", figsize=(15, 10))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3f9b311", + "metadata": {}, + "outputs": [], + "source": [ + "counties.plot(column=\"fire\", vmin=0, vmax=25)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:notebook] *", + "language": "python", + "name": "conda-env-notebook-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "efe27b80", - "metadata": {}, - "outputs": [], - "source": [ - "def integrated_risk(p):\n", - " return (1 - binom.cdf(0, 20, p)) * 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6da00394", - "metadata": {}, - "outputs": [], - "source": [ - "ds = xr.open_zarr(\n", - " \"https://carbonplan.blob.core.windows.net/carbonplan-forests/risks/results/web/fire.zarr\"\n", - ").load()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0fa21c48", - "metadata": {}, - "outputs": [], - "source": [ - "counties = geopandas.read_file(\n", - " \"cb_2021_us_county_500k/cb_2021_us_county_500k.shp\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5bae54f7", - "metadata": {}, - "outputs": [], - "source": [ - "exclude = list([\"02\", \"15\", \"60\", \"66\", \"69\", \"72\", \"78\"])\n", - "counties = counties[list(map(lambda x: x not in exclude, counties[\"STATEFP\"]))]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e356a4d", - "metadata": {}, - "outputs": [], - "source": [ - "counties = counties.to_crs(\"EPSG:5070\").reset_index()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3363b94", - "metadata": {}, - "outputs": [], - "source": [ - "get_mask = lambda geometry: rasterio.features.geometry_mask(\n", - " [geometry],\n", - " out_shape=ds.lat.shape,\n", - " transform=Affine(4000, 0.0, -2493045.0, 0.0, -4000, 3310005.0),\n", - " all_touched=False,\n", - " invert=True,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd56a44c", - "metadata": {}, - "outputs": [], - "source": [ - "scenarios = [\"ssp245\", \"ssp370\", \"ssp585\"]\n", - "columns = [\"state_code\", \"state_name\", \"county_code\", \"county_name\"] + list(\n", - " ds.year.values\n", - ")\n", - "data = {key: [] for key in scenarios}\n", - "dfs = {key: [] for key in scenarios}\n", - "\n", - "for index, county in tqdm(counties.iterrows()):\n", - " mask = np.tile(get_mask(county[\"geometry\"]), [12, 1, 1])\n", - " means = ds.where(mask).mean([\"x\", \"y\"])\n", - " for scenario in scenarios:\n", - " values = integrated_risk(means[scenario].values)\n", - " data[scenario].append(\n", - " [\n", - " county[\"STATEFP\"],\n", - " county[\"STUSPS\"],\n", - " county[\"COUNTYFP\"],\n", - " county[\"NAME\"],\n", - " ]\n", - " + list(values)\n", - " )\n", - "\n", - "for scenario in scenarios:\n", - " dfs[scenario] = pd.DataFrame(data[scenario], columns=columns)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "44b0b5be", - "metadata": {}, - "outputs": [], - "source": [ - "dfs[\"ssp245\"].dropna().reset_index(drop=True).to_csv(\n", - " \"CarbonPlan-Fire-Risk-SSP-4.5.csv\", index=False, header=True\n", - ")\n", - "dfs[\"ssp370\"].dropna().reset_index(drop=True).to_csv(\n", - " \"CarbonPlan-Fire-Risk-SSP-7.0.csv\", index=False, header=True\n", - ")\n", - "dfs[\"ssp585\"].dropna().reset_index(drop=True).to_csv(\n", - " \"CarbonPlan-Fire-Risk-SSP-8.5.csv\", index=False, header=True\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f433c8a2", - "metadata": {}, - "outputs": [], - "source": [ - "len(dfs[\"ssp585\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38573ebc", - "metadata": {}, - "outputs": [], - "source": [ - "counties[\"fire\"] = dfs[\"ssp585\"][\"2050\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1e0701ae", - "metadata": {}, - "outputs": [], - "source": [ - "counties.plot(\n", - " column=\"fire\", cmap=\"hot\", vmin=0, vmax=50, figsize=(15, 10), legend=True\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7aa7c6a8", - "metadata": {}, - "outputs": [], - "source": [ - "counties.plot(column=\"fire\", cmap=\"hot\", figsize=(15, 10))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3f9b311", - "metadata": {}, - "outputs": [], - "source": [ - "counties.plot(column=\"fire\", vmin=0, vmax=25)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:notebook] *", - "language": "python", - "name": "conda-env-notebook-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/scripts/article_prep.py b/scripts/article_prep.py index 98149b6..790e492 100644 --- a/scripts/article_prep.py +++ b/scripts/article_prep.py @@ -28,124 +28,126 @@ def default(self, obj): return super(NpEncoder, self).default(obj) -warnings.filterwarnings('ignore') +warnings.filterwarnings("ignore") -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +account_key = os.environ.get("BLOB_ACCOUNT_KEY") gcms = [ - ('CanESM5-CanOE', 'r3i1p2f1'), - ('MIROC-ES2L', 'r1i1p1f2'), - ('ACCESS-CM2', 'r1i1p1f1'), - ('ACCESS-ESM1-5', 'r10i1p1f1'), - ('MRI-ESM2-0', 'r1i1p1f1'), - ('MPI-ESM1-2-LR', 'r10i1p1f1'), + ("CanESM5-CanOE", "r3i1p2f1"), + ("MIROC-ES2L", "r1i1p1f2"), + ("ACCESS-CM2", "r1i1p1f1"), + ("ACCESS-ESM1-5", "r10i1p1f1"), + ("MRI-ESM2-0", "r1i1p1f1"), + ("MPI-ESM1-2-LR", "r10i1p1f1"), ] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +scenarios = ["ssp245", "ssp370", "ssp585"] website_mask = ( load.nlcd(store="az", year=2016).sel(band=[41, 42, 43, 90]).sum("band") > 0.5 ).astype("float") region_bboxes = { - 'CONUS': {'x': slice(-3e6, 3e6), 'y': slice(4e6, 0)}, - 'PNW': {'x': slice(-2.5e6, -1e6), 'y': slice(3.5e6, 2.4e6)}, - 'Southwest': {'x': slice(-1.8e6, -0.9e6), 'y': slice(1.8e6, 0.9e6)}, - 'California': {'x': slice(-2.3e6, -1.8e6), 'y': slice(2.5e6, 1.2e6)}, - 'Southeast': { - 'x': slice(0.6e6, 1.8e6), - 'y': slice(1.6e6, 0.3e6), + "CONUS": {"x": slice(-3e6, 3e6), "y": slice(4e6, 0)}, + "PNW": {"x": slice(-2.5e6, -1e6), "y": slice(3.5e6, 2.4e6)}, + "Southwest": {"x": slice(-1.8e6, -0.9e6), "y": slice(1.8e6, 0.9e6)}, + "California": {"x": slice(-2.3e6, -1.8e6), "y": slice(2.5e6, 1.2e6)}, + "Southeast": { + "x": slice(0.6e6, 1.8e6), + "y": slice(1.6e6, 0.3e6), }, } def build_climate_cube( - tlim=(1970, 2099), variables=['tmean'], downscaling_method='quantile-mapping-v3' + tlim=(1970, 2099), variables=["tmean"], downscaling_method="quantile-mapping-v3" ): """""" gcms = [ - ('CanESM5-CanOE', 'r3i1p2f1'), - ('MIROC-ES2L', 'r1i1p1f2'), - ('ACCESS-CM2', 'r1i1p1f1'), - ('ACCESS-ESM1-5', 'r10i1p1f1'), - ('MRI-ESM2-0', 'r1i1p1f1'), - ('MPI-ESM1-2-LR', 'r10i1p1f1'), + ("CanESM5-CanOE", "r3i1p2f1"), + ("MIROC-ES2L", "r1i1p1f2"), + ("ACCESS-CM2", "r1i1p1f1"), + ("ACCESS-ESM1-5", "r10i1p1f1"), + ("MRI-ESM2-0", "r1i1p1f1"), + ("MPI-ESM1-2-LR", "r10i1p1f1"), ] - scenarios = ['ssp245', 'ssp370', 'ssp585'] + scenarios = ["ssp245", "ssp370", "ssp585"] all_scenarios = [] for scenario in scenarios: all_gcms = [] - for (gcm, ensemble_member) in gcms: + for gcm, ensemble_member in gcms: cmip = load.cmip( - store='az', + store="az", model=gcm, tlim=tlim, scenario=scenario, historical=True, member=ensemble_member, method=downscaling_method, - sampling='annual', + sampling="annual", variables=variables, ) all_gcms.append(cmip) - concatted = xr.concat(all_gcms, dim='gcm') - concatted = concatted.assign_coords({'gcm': [gcm[0] for gcm in gcms]}) + concatted = xr.concat(all_gcms, dim="gcm") + concatted = concatted.assign_coords({"gcm": [gcm[0] for gcm in gcms]}) all_scenarios.append(concatted) - ds = xr.concat(all_scenarios, dim='scenario') - ds = ds.assign_coords({'scenario': scenarios}) + ds = xr.concat(all_scenarios, dim="scenario") + ds = ds.assign_coords({"scenario": scenarios}) return ds def repackage_drought_insects(ds): gcms = [ - ('CanESM5-CanOE', 'r3i1p2f1'), - ('MIROC-ES2L', 'r1i1p1f2'), - ('ACCESS-CM2', 'r1i1p1f1'), - ('ACCESS-ESM1-5', 'r10i1p1f1'), - ('MRI-ESM2-0', 'r1i1p1f1'), - ('MPI-ESM1-2-LR', 'r10i1p1f1'), + ("CanESM5-CanOE", "r3i1p2f1"), + ("MIROC-ES2L", "r1i1p1f2"), + ("ACCESS-CM2", "r1i1p1f1"), + ("ACCESS-ESM1-5", "r10i1p1f1"), + ("MRI-ESM2-0", "r1i1p1f1"), + ("MPI-ESM1-2-LR", "r10i1p1f1"), ] - scenarios = ['ssp245', 'ssp370', 'ssp585'] + scenarios = ["ssp245", "ssp370", "ssp585"] all_gcms = [] - for (gcm, ensemble_member) in gcms: + for gcm, ensemble_member in gcms: all_gcms.append( - ds[['{}-{}'.format(gcm, scenario) for scenario in scenarios]] - .to_array(dim='scenario', name='probability') - .assign_coords({'scenario': scenarios}) + ds[["{}-{}".format(gcm, scenario) for scenario in scenarios]] + .to_array(dim="scenario", name="probability") + .assign_coords({"scenario": scenarios}) ) - full_ds = xr.concat(all_gcms, dim='gcm').to_dataset() - full_ds = full_ds.assign_coords({'gcm': [gcm for (gcm, ensemble_member) in gcms]}) + full_ds = xr.concat(all_gcms, dim="gcm").to_dataset() + full_ds = full_ds.assign_coords({"gcm": [gcm for (gcm, ensemble_member) in gcms]}) return full_ds -def timeseries_dict(ds, time_period='historical'): +def timeseries_dict(ds, time_period="historical"): gcms = [ - ('CanESM5-CanOE', 'r3i1p2f1'), - ('MIROC-ES2L', 'r1i1p1f2'), - ('ACCESS-CM2', 'r1i1p1f1'), - ('ACCESS-ESM1-5', 'r10i1p1f1'), - ('MRI-ESM2-0', 'r1i1p1f1'), - ('MPI-ESM1-2-LR', 'r10i1p1f1'), + ("CanESM5-CanOE", "r3i1p2f1"), + ("MIROC-ES2L", "r1i1p1f2"), + ("ACCESS-CM2", "r1i1p1f1"), + ("ACCESS-ESM1-5", "r10i1p1f1"), + ("MRI-ESM2-0", "r1i1p1f1"), + ("MPI-ESM1-2-LR", "r10i1p1f1"), ] mean = [] gcm_dict = {} - for (gcm, ensemble_member) in gcms: + for gcm, ensemble_member in gcms: gcm_dict[gcm] = [] - if time_period == 'historical': + if time_period == "historical": years = np.arange(1980, 2020, 10) - elif time_period == 'future': + elif time_period == "future": years = np.arange(2020, 2100, 10) for year in years: # average across scenarios mean.append( { - 'y': year, - 'r': ds.mean(dim='gcm').sel(year=year).values.item(), + "y": year, + "r": ds.mean(dim="gcm").sel(year=year).values.item(), } ) # then populate all the gcms - for (gcm, ensemble_member) in gcms: - gcm_dict[gcm].append({'y': year, 'r': ds.sel(gcm=gcm, year=year).values.item()}) + for gcm, ensemble_member in gcms: + gcm_dict[gcm].append( + {"y": year, "r": ds.sel(gcm=gcm, year=year).values.item()} + ) return mean, gcm_dict @@ -155,13 +157,13 @@ def timeseries_dict(ds, time_period='historical'): slice_to_list = lambda x: [x.start, x.stop] for region in region_bboxes: region_bboxes_lists[region] = {} - for coord in ['x', 'y']: + for coord in ["x", "y"]: region_bboxes_lists[region][coord] = slice_to_list(region_bboxes[region][coord]) with fsspec.open( - 'az://carbonplan-forests/risks/results/web/time-series-bounding-boxes.json', + "az://carbonplan-forests/risks/results/web/time-series-bounding-boxes.json", account_name="carbonplan", account_key=account_key, - mode='w', + mode="w", ) as f: json.dump(region_bboxes_lists, f, indent=2, cls=NpEncoder) @@ -171,25 +173,25 @@ def timeseries_dict(ds, time_period='historical'): results_dict = {} # select out bounding boxes # select out bounding boxes -for impact in ['insects', 'drought', 'fire', 'tmean']: +for impact in ["insects", "drought", "fire", "tmean"]: results_dict[impact] = {} # read in the temperature data from its different sources and create a datacube # of the same specs as the risks - if impact == 'tmean': + if impact == "tmean": ds = build_climate_cube() # grab the risks data else: - store_path = 'risks/results/web/{}_full.zarr'.format(impact) + store_path = "risks/results/web/{}_full.zarr".format(impact) ds = xr.open_zarr( get_store( - 'carbonplan-forests', + "carbonplan-forests", store_path, account_key=account_key, ) ) - ds = ds.assign_coords({'year': np.arange(1980, 2100, 10)}) + ds = ds.assign_coords({"year": np.arange(1980, 2100, 10)}) - if impact in ['insects', 'drought']: + if impact in ["insects", "drought"]: # restructure the insects/drought ones to align with the temp/fire ds = repackage_drought_insects(ds) @@ -203,11 +205,11 @@ def timeseries_dict(ds, time_period='historical'): } ) # align to the annual timesteps for tmean and fire - if impact == 'tmean': + if impact == "tmean": # calculate decadal mean ds = ds.coarsen(time=10).mean().compute() - ds = ds.rename({'time': 'year'}) - ds = ds.assign_coords({'year': np.arange(1970, 2100, 10)}) + ds = ds.rename({"time": "year"}) + ds = ds.assign_coords({"year": np.arange(1970, 2100, 10)}) # mask according to the mask we use for the web ds = ds.where(website_mask > 0).compute() # then do rolling mean for two decades (and drop the first timestep which only @@ -216,7 +218,7 @@ def timeseries_dict(ds, time_period='historical'): # loop through each of the regions of interest for region, bbox in region_bboxes.items(): - print('Calculating regional averages over the {} for {}'.format(region, impact)) + print("Calculating regional averages over the {} for {}".format(region, impact)) # initialize the dictionary results_dict[impact][region] = {} # select out the box you want @@ -224,26 +226,28 @@ def timeseries_dict(ds, time_period='historical'): # aggregate the different risks according to either 20 year integrated risk for fire # or just multiply by 100 to convert to percentage and then by 20 for the 20 year # total mortality for insects/drought - if impact == 'fire': + if impact == "fire": selected = selected.apply(utils.integrated_risk) - elif impact in ['drought', 'insects']: + elif impact in ["drought", "insects"]: selected *= 100 * 20 # calculate regional averages (these have already been masked) and then select the # appropriate variable - if impact == 'tmean': - selected = selected.mean(dim=['x', 'y']).compute().tmean + if impact == "tmean": + selected = selected.mean(dim=["x", "y"]).compute().tmean # create anomalies - selected -= selected.sel(year=[1980, 1990, 2000]).mean(dim='year') + selected -= selected.sel(year=[1980, 1990, 2000]).mean(dim="year") else: - selected = selected.mean(dim=['x', 'y']).compute().probability + selected = selected.mean(dim=["x", "y"]).compute().probability # first populate the historical values - results_dict[impact][region]['historical'] = {} + results_dict[impact][region]["historical"] = {} # initialize your dictionary with the gcm keys - mean, models = timeseries_dict(selected.mean(dim='scenario'), time_period='historical') - results_dict[impact][region]['historical']['mean'] = mean - results_dict[impact][region]['historical']['models'] = models + mean, models = timeseries_dict( + selected.mean(dim="scenario"), time_period="historical" + ) + results_dict[impact][region]["historical"]["mean"] = mean + results_dict[impact][region]["historical"]["models"] = models # then fill in each of the three different scenarios @@ -251,15 +255,17 @@ def timeseries_dict(ds, time_period='historical'): results_dict[impact][region][scenario] = {} # initialize your dictionary with the gcm keys - mean, models = timeseries_dict(selected.sel(scenario=scenario), time_period='future') - results_dict[impact][region][scenario]['mean'] = mean - results_dict[impact][region][scenario]['models'] = models + mean, models = timeseries_dict( + selected.sel(scenario=scenario), time_period="future" + ) + results_dict[impact][region][scenario]["mean"] = mean + results_dict[impact][region][scenario]["models"] = models # write out to dictionary to rendered within the explainer with fsspec.open( - 'az://carbonplan-forests/risks/results/web/time-series-hybrid.json', + "az://carbonplan-forests/risks/results/web/time-series-hybrid.json", account_name="carbonplan", account_key=account_key, - mode='w', + mode="w", ) as f: json.dump(results_dict, f, indent=2, cls=NpEncoder) diff --git a/scripts/biomass.py b/scripts/biomass.py index ff7fade..1b8e2d4 100644 --- a/scripts/biomass.py +++ b/scripts/biomass.py @@ -9,66 +9,66 @@ args = sys.argv if len(args) < 2: - store = 'local' + store = "local" else: store = args[1] -variables = ['tmean', 'ppt'] +variables = ["tmean", "ppt"] -print('[biomass] loading data') -df = load.fia(store=store, states='conus') +print("[biomass] loading data") +df = load.fia(store=store, states="conus") df = load.terraclim( store=store, tlim=(2000, 2020), variables=variables, remove_nans=True, - sampling='annual', + sampling="annual", df=df, ) -type_codes = df['type_code'].unique() +type_codes = df["type_code"].unique() -print('[biomass] fitting models') +print("[biomass] fitting models") models = {} for code in tqdm(type_codes): - df_type = df[df['type_code'] == code].reset_index() - x = df_type['age'] - y = df_type['biomass'] - f = [df_type['tmean_mean'], df_type['ppt_mean']] - model = fit.growth(x=x, y=y, f=f, noise='gamma') + df_type = df[df["type_code"] == code].reset_index() + x = df_type["age"] + y = df_type["biomass"] + f = [df_type["tmean_mean"], df_type["ppt_mean"]] + model = fit.growth(x=x, y=y, f=f, noise="gamma") models[code] = model -print('[biomass] preparing for evaluations') +print("[biomass] preparing for evaluations") pf = pd.DataFrame() -pf['lat'] = df['lat'] -pf['lon'] = df['lon'] -pf['type_code'] = df['type_code'] +pf["lat"] = df["lat"] +pf["lon"] = df["lon"] +pf["type_code"] = df["type_code"] -print('[biomass] evaluating predictions on training data') +print("[biomass] evaluating predictions on training data") for code in type_codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code - x = df[inds]['age'] - f = [df[inds]['tmean_mean'], df[inds]['ppt_mean']] - pf.loc[inds, 'historical'] = model.predict(x, f) + inds = df["type_code"] == code + x = df[inds]["age"] + f = [df[inds]["tmean_mean"], df[inds]["ppt_mean"]] + pf.loc[inds, "historical"] = model.predict(x, f) -print('[biomass] evaluating predictions on future climate models') +print("[biomass] evaluating predictions on future climate models") targets = list(map(lambda x: str(x), np.arange(2005, 2100, 10))) cmip_models = [ - 'CanESM5-CanOE', - 'MIROC-ES2L', - 'ACCESS-CM2', - 'ACCESS-ESM1-5', - 'MRI-ESM2-0', - 'MPI-ESM1-2-LR', + "CanESM5-CanOE", + "MIROC-ES2L", + "ACCESS-CM2", + "ACCESS-ESM1-5", + "MRI-ESM2-0", + "MPI-ESM1-2-LR", ] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +scenarios = ["ssp245", "ssp370", "ssp585"] for it in tqdm(range(len(targets))): target = targets[it] tlim = (str(int(target) - 10), str(int(target) + 9)) for cmip_model in cmip_models: for scenario in scenarios: - key = cmip_model + '_' + scenario + '_' + target + key = cmip_model + "_" + scenario + "_" + target df = load.cmip( store=store, tlim=(int(tlim[0]), int(tlim[1])), @@ -76,30 +76,30 @@ historical=True if int(tlim[0]) < 2015 else False, model=cmip_model, scenario=scenario, - sampling='annual', + sampling="annual", df=df, ) pf[key] = np.NaN for code in type_codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code - x = df[inds]['age'] - year = df[inds]['year'] - f = [df[inds]['tmean_mean'], df[inds]['ppt_mean']] + inds = df["type_code"] == code + x = df[inds]["age"] + year = df[inds]["year"] + f = [df[inds]["tmean_mean"], df[inds]["ppt_mean"]] if it == 0: pf.loc[inds, key] = model.predict( np.maximum(x + (float(target) - year), 0), f ) else: prev_target = targets[it - 1] - prev_key = cmip_model + '_' + scenario + '_' + prev_target - diff = model.predict(x + (float(target) - year), f) - model.predict( - x + (float(prev_target) - year), f - ) + prev_key = cmip_model + "_" + scenario + "_" + prev_target + diff = model.predict( + x + (float(target) - year), f + ) - model.predict(x + (float(prev_target) - year), f) pf.loc[inds, key] = np.maximum(pf[prev_key][inds] + diff, 0) -pf['r2'] = pf['type_code'].map(lambda k: models[k].train_r2) -pf['scale'] = pf['type_code'].map(lambda k: models[k].scale) +pf["r2"] = pf["type_code"].map(lambda k: models[k].train_r2) +pf["scale"] = pf["type_code"].map(lambda k: models[k].scale) -pf.to_parquet('data/biomass_v2.parquet', compression='gzip', engine='fastparquet') +pf.to_parquet("data/biomass_v2.parquet", compression="gzip", engine="fastparquet") diff --git a/scripts/convert.py b/scripts/convert.py index 9a0c17f..ab1801c 100644 --- a/scripts/convert.py +++ b/scripts/convert.py @@ -16,27 +16,27 @@ def integrated_risk(p): args = sys.argv if len(args) < 2: - raise ValueError('must specify dataset') + raise ValueError("must specify dataset") dataset = args[1] if len(args) > 2: coarsen = int(args[2]) - savename = f'{dataset}_d{coarsen}' + savename = f"{dataset}_d{coarsen}" res = 4000 * coarsen else: coarsen = 0 savename = dataset res = 4000 -print(f'[{dataset}] converting to geojson') +print(f"[{dataset}] converting to geojson") precision = 2 -store = utils.get_store('carbonplan-forests', f'risks/results/web/{dataset}.zarr') +store = utils.get_store("carbonplan-forests", f"risks/results/web/{dataset}.zarr") ds = xr.open_zarr(store) if coarsen > 0: - ds = ds.coarsen(x=coarsen, y=coarsen, boundary='trim').mean().compute() + ds = ds.coarsen(x=coarsen, y=coarsen, boundary="trim").mean().compute() # if dataset == 'fire': # scenarios = ['ssp245', 'ssp370', 'ssp585'] @@ -46,16 +46,16 @@ def integrated_risk(p): # ) # ds[scenario] = functools.reduce(lambda a, b: a + b, [ds[key] for key in keys]) / len(keys) -if 'fire' in dataset or 'biomass' in dataset: - scenarios = ['ssp245', 'ssp370', 'ssp585'] - ds = ds.sel(year=slice('2010', '2090')) - targets = ds['year'] +if "fire" in dataset or "biomass" in dataset: + scenarios = ["ssp245", "ssp370", "ssp585"] + ds = ds.sel(year=slice("2010", "2090")) + targets = ds["year"] a = np.concatenate([ds[scenario].values for scenario in scenarios], axis=0) - if 'fire' in dataset: + if "fire" in dataset: a = integrated_risk(a) - if 'insects' in dataset or 'drought' in dataset: + if "insects" in dataset or "drought" in dataset: a = a * 20 a[np.isnan(a)] = 0 @@ -66,32 +66,32 @@ def integrated_risk(p): for s, scenario in enumerate(scenarios): for y, year in enumerate(targets): - key = str(s) + '_' + str(y) + key = str(s) + "_" + str(y) a = ds[scenario].sel(year=year).values - if 'fire' in dataset: + if "fire" in dataset: a = integrated_risk(a) - if 'insects' in dataset or 'drought' in dataset: + if "insects" in dataset or "drought" in dataset: a = a * 20 a[np.isnan(a)] = 0 df[key] = np.round(a[r, c], precision) -if 'insects' in dataset or 'drought' in dataset: - a = ds['historical'].values +if "insects" in dataset or "drought" in dataset: + a = ds["historical"].values - if 'insects' in dataset or 'drought' in dataset: + if "insects" in dataset or "drought" in dataset: a = a * 100 * 20 a[np.isnan(a)] = 0 r, c = np.nonzero(a) lat, lon = utils.rowcol_to_latlon(r, c, res=res) df = pd.DataFrame() - key = '0_0' + key = "0_0" a[np.isnan(a)] = 0 df = pd.DataFrame() df[key] = np.round(a[r, c], precision) gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(lon, lat)) -gdf.to_file(f'data/{savename}.geojson', driver='GeoJSON') +gdf.to_file(f"data/{savename}.geojson", driver="GeoJSON") diff --git a/scripts/drought.py b/scripts/drought.py index 272796b..0275747 100644 --- a/scripts/drought.py +++ b/scripts/drought.py @@ -8,41 +8,41 @@ args = sys.argv if len(args) < 2: - store = 'local' + store = "local" else: store = args[1] -data_vars = ['ppt', 'tavg'] -data_aggs = ['sum', 'mean'] +data_vars = ["ppt", "tavg"] +data_aggs = ["sum", "mean"] -print('[drought] loading data') -df = load.fia(store=store, states='conus', group_repeats=True) +print("[drought] loading data") +df = load.fia(store=store, states="conus", group_repeats=True) df = load.terraclim( store=store, - tlim=(int(df['year_0'].min()), 2020), + tlim=(int(df["year_0"].min()), 2020), data_vars=data_vars, data_aggs=data_aggs, df=df, group_repeats=True, ) -print('[drought] prepare for fitting') +print("[drought] prepare for fitting") x, y, pf = prepare.drought(df) x_z, x_mean, x_std = utils.zscore_2d(x) -codes = pf['type_code'].unique() +codes = pf["type_code"].unique() -print('[drought] fit models') +print("[drought] fit models") models = {} for code in tqdm(codes): - inds = pf['type_code'] == code + inds = pf["type_code"] == code model = fit.hurdle(x=x_z[inds], y=y[inds]) models[code] = model -print('[drought] preparing for evaluations') -df = load.fia(store=store, states='conus') -pf = df[['lat', 'lon', 'type_code']].copy().reset_index(drop=True) +print("[drought] preparing for evaluations") +df = load.fia(store=store, states="conus") +pf = df[["lat", "lon", "type_code"]].copy().reset_index(drop=True) -print('[drought] evaluating predictions on historical data') +print("[drought] evaluating predictions on historical data") df = load.terraclim( store=store, tlim=(2005, 2014), @@ -55,19 +55,19 @@ for code in codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code - pf.loc[inds, 'historical'] = model.predict(x=x_z[inds]) + inds = df["type_code"] == code + pf.loc[inds, "historical"] = model.predict(x=x_z[inds]) -print('[drought] evaluating on future climate models') +print("[drought] evaluating on future climate models") targets = list(map(lambda x: str(x), np.arange(2020, 2120, 20))) -cmip_models = ['BCC-CSM2-MR', 'ACCESS-ESM1-5', 'CanESM5', 'MIROC6', 'MPI-ESM1-2-LR'] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +cmip_models = ["BCC-CSM2-MR", "ACCESS-ESM1-5", "CanESM5", "MIROC6", "MPI-ESM1-2-LR"] +scenarios = ["ssp245", "ssp370", "ssp585"] for it in tqdm(range(len(targets))): target = targets[it] tlim = (str(int(target) - 5), str(int(target) + 4)) for cmip_model in cmip_models: for scenario in scenarios: - key = cmip_model + '_' + scenario + '_' + target + key = cmip_model + "_" + scenario + "_" + target df = load.cmip( store=store, tlim=(int(tlim[0]), int(tlim[1])), @@ -84,9 +84,11 @@ for code in codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code + inds = df["type_code"] == code pf.loc[inds, key] = model.predict(x=x_z[inds]) -pf['r2'] = pf['type_code'].map(lambda k: models[k].train_r2 if k in models.keys() else np.NaN) +pf["r2"] = pf["type_code"].map( + lambda k: models[k].train_r2 if k in models.keys() else np.NaN +) -pf.to_parquet('data/drought.parquet', compression='gzip', engine='fastparquet') +pf.to_parquet("data/drought.parquet", compression="gzip", engine="fastparquet") diff --git a/scripts/export.py b/scripts/export.py index d2c8cf7..689f0ab 100755 --- a/scripts/export.py +++ b/scripts/export.py @@ -10,86 +10,86 @@ from carbonplan_forest_risks import load # parameters -variables = ['ppt', 'tmean', 'pdsi', 'cwd', 'pet', 'vpd', 'rh'] +variables = ["ppt", "tmean", "pdsi", "cwd", "pet", "vpd", "rh"] targets_future = list(map(lambda x: str(x), np.arange(2015, 2105, 10))) targets_historical = list(map(lambda x: str(x), np.arange(1955, 2015, 10))) targets_terraclimate = list(map(lambda x: str(x), np.arange(1965, 2020, 10))) -prefix = 'az://carbonplan-scratch/forests' -states = 'conus' -version = 'v18' -date = '05-03-2021' +prefix = "az://carbonplan-scratch/forests" +states = "conus" +version = "v18" +date = "05-03-2021" def write_df(df, name): - fname = f'{prefix}/{name}-{version}-{date}.csv' - print('writing', fname) + fname = f"{prefix}/{name}-{version}-{date}.csv" + print("writing", fname) with fsspec.open( fname, - 'w', - account_name='carbonplan', - account_key=os.environ['BLOB_ACCOUNT_KEY'], + "w", + account_name="carbonplan", + account_key=os.environ["BLOB_ACCOUNT_KEY"], ) as f: df.to_csv(f, index=False) def terraclimate_fia_wide(): # generate wide data w/ terraclim - df_fia = load.fia(store='az', states=states, group_repeats=True) + df_fia = load.fia(store="az", states=states, group_repeats=True) df = load.terraclim( - store='az', - tlim=(int(df_fia['year_0'].min()), 2020), + store="az", + tlim=(int(df_fia["year_0"].min()), 2020), variables=variables, df=df_fia, group_repeats=True, - sampling='annual', + sampling="annual", ) - write_df(df, 'FIA-TerraClim-Wide') + write_df(df, "FIA-TerraClim-Wide") def terraclimate_fia_long(): # generate long data w/ terraclim - df_fia = load.fia(store='az', states=states, clean=False) + df_fia = load.fia(store="az", states=states, clean=False) for target in targets_terraclimate: tlim = (str(int(target) - 5), str(int(target) + 4)) print(tlim) df = load.terraclim( - store='az', + store="az", tlim=(int(tlim[0]), int(tlim[1])), variables=variables, df=df_fia, - sampling='annual', + sampling="annual", ) - write_df(df, f'FIA-TerraClim-Long-{tlim[0]}.{tlim[1]}') + write_df(df, f"FIA-TerraClim-Long-{tlim[0]}.{tlim[1]}") def cmip_fia_long(cmip_table, method): # generate long data w/ cmip - df_fia = load.fia(store='az', states=states, clean=False) + df_fia = load.fia(store="az", states=states, clean=False) keep_vars = ( - ['lat', 'lon', 'plot_cn'] - + [var + '_min' for var in variables] - + [var + '_mean' for var in variables] - + [var + '_max' for var in variables] + ["lat", "lon", "plot_cn"] + + [var + "_min" for var in variables] + + [var + "_mean" for var in variables] + + [var + "_max" for var in variables] ) for i, row in cmip_table.iterrows(): - if 'hist' in row.scenario: + if "hist" in row.scenario: targets = targets_historical else: targets = targets_future for target in targets: - historical = target == '2015' + historical = target == "2015" tlim = (str(int(target) - 5), str(int(target) + 4)) print(tlim, row) df = load.cmip( - store='az', + store="az", tlim=(int(tlim[0]), int(tlim[1])), variables=variables, df=df_fia, @@ -98,20 +98,20 @@ def cmip_fia_long(cmip_table, method): member=row.member, historical=historical, method=method, - sampling='annual', + sampling="annual", ) df = df[keep_vars] write_df( df, - f'{method}/FIA-CMIP6-Long-{row.model}.{row.scenario}.{row.member}-{tlim[0]}.{tlim[1]}', + f"{method}/FIA-CMIP6-Long-{row.model}.{row.scenario}.{row.member}-{tlim[0]}.{tlim[1]}", ) -if __name__ == '__main__': +if __name__ == "__main__": df = get_cmip_runs(comp=True, unique=True).reset_index() - with dask.config.set(scheduler='processes'): + with dask.config.set(scheduler="processes"): with ProgressBar(): terraclimate_fia_long() terraclimate_fia_wide() - for method in ['quantile-mapping-v3']: # , 'bias-corrected']: + for method in ["quantile-mapping-v3"]: # , 'bias-corrected']: cmip_fia_long(df, method=method) diff --git a/scripts/fire.py b/scripts/fire.py index 657bbb0..fd6190a 100644 --- a/scripts/fire.py +++ b/scripts/fire.py @@ -9,28 +9,30 @@ from carbonplan_forest_risks import collect, fit, load, prepare, utils from carbonplan_forest_risks.utils import get_store -warnings.simplefilter('ignore', category=RuntimeWarning) -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +warnings.simplefilter("ignore", category=RuntimeWarning) +account_key = os.environ.get("BLOB_ACCOUNT_KEY") args = sys.argv if len(args) < 2: - store = 'local' + store = "local" else: store = args[1] run_name = args[2] - data_vars = args[3].strip('[]').split(',') + data_vars = args[3].strip("[]").split(",") coarsen_fit = int(args[4]) coarsen_predict = int(args[5]) tlim = (1983, 2018) -analysis_tlim = slice('1984', '2018') +analysis_tlim = slice("1984", "2018") -print('[fire] loading data') -mask = (load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum('band') > 0.25).astype( - 'float' +print("[fire] loading data") +mask = ( + load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum("band") > 0.25 +).astype("float") +nftd = load.nftd( + store=store, groups="all", coarsen=coarsen_fit, mask=mask, area_threshold=1500 ) -nftd = load.nftd(store=store, groups='all', coarsen=coarsen_fit, mask=mask, area_threshold=1500) climate = load.terraclim( store=store, @@ -41,28 +43,30 @@ sampling="monthly", ) mtbs = load.mtbs(store=store, coarsen=coarsen_fit, tlim=tlim, mask=mask) -mtbs = mtbs.assign_coords({'x': nftd.x, 'y': nftd.y}) +mtbs = mtbs.assign_coords({"x": nftd.x, "y": nftd.y}) -print('[fire] fitting model') -prepend = climate.sel(time=slice('1983', '1983')) +print("[fire] fitting model") +prepend = climate.sel(time=slice("1983", "1983")) x, y = prepare.fire( - climate.sel(time=slice('1984', '2018')), + climate.sel(time=slice("1984", "2018")), nftd, mtbs, add_global_climate_trends={ - 'tmean': {'climate_prepend': prepend, 'rolling_period': 12}, - 'ppt': {'climate_prepend': prepend, 'rolling_period': 12}, + "tmean": {"climate_prepend": prepend, "rolling_period": 12}, + "ppt": {"climate_prepend": prepend, "rolling_period": 12}, }, add_local_climate_trends=None, - analysis_tlim=slice('1984', '2018'), + analysis_tlim=slice("1984", "2018"), ) x_z, x_mean, x_std = utils.zscore_2d(x) model = fit.hurdle(x_z, y, log=False) yhat = model.predict(x_z) prediction = collect.fire(yhat, mtbs) -print('[fire] evaluating on training data') +print("[fire] evaluating on training data") # reload everything at the appropriate coarsen level (in this case no coarsening) -nftd = load.nftd(store=store, groups='all', mask=mask, coarsen=coarsen_predict, area_threshold=1500) +nftd = load.nftd( + store=store, groups="all", mask=mask, coarsen=coarsen_predict, area_threshold=1500 +) climate = load.terraclim( store=store, @@ -70,11 +74,11 @@ coarsen=coarsen_predict, variables=data_vars, mask=mask, - sampling='monthly', + sampling="monthly", ) for year in np.arange(1984, 2024, 10): ds = xr.Dataset() - print('[fire] evaluating on decade beginning in {}'.format(year)) + print("[fire] evaluating on decade beginning in {}".format(year)) prepend_time_slice = slice(str(year - 1), str(year - 1)) analysis_time_slice = slice(str(year), str(year + 9)) prepend = climate.sel(time=prepend_time_slice) @@ -83,8 +87,8 @@ nftd, mtbs, add_global_climate_trends={ - 'tmean': {'climate_prepend': prepend, 'rolling_period': 12}, - 'ppt': {'climate_prepend': prepend, 'rolling_period': 12}, + "tmean": {"climate_prepend": prepend, "rolling_period": 12}, + "ppt": {"climate_prepend": prepend, "rolling_period": 12}, }, add_local_climate_trends=None, analysis_tlim=analysis_time_slice, @@ -92,50 +96,50 @@ x_z = utils.zscore_2d(x, mean=x_mean, std=x_std) yhat = model.predict(x_z) prediction = collect.fire(yhat, climate.sel(time=analysis_time_slice)) - ds['historical'] = (['time', 'y', 'x'], prediction['prediction']) + ds["historical"] = (["time", "y", "x"], prediction["prediction"]) ds = ds.assign_coords( { - 'x': climate.x, - 'y': climate.y, - 'time': climate.sel(time=analysis_time_slice).time, - 'lat': climate.lat, - 'lon': climate.lon, + "x": climate.x, + "y": climate.y, + "time": climate.sel(time=analysis_time_slice).time, + "lat": climate.lat, + "lon": climate.lon, } ) - if store == 'local': - ds.to_zarr('data/fire_historical.zarr', mode='w') - elif store == 'az': + if store == "local": + ds.to_zarr("data/fire_historical.zarr", mode="w") + elif store == "az": path = get_store( - 'carbonplan-forests', - 'risks/results/paper/fire_terraclimate_{}.zarr'.format(run_name), + "carbonplan-forests", + "risks/results/paper/fire_terraclimate_{}.zarr".format(run_name), account_key=account_key, ) if year == 1984: - ds.to_zarr(path, consolidated=True, mode='w') + ds.to_zarr(path, consolidated=True, mode="w") else: - ds.to_zarr(path, consolidated=True, mode='a', append_dim='time') -print('[fire] evaluating on future climate') + ds.to_zarr(path, consolidated=True, mode="a", append_dim="time") +print("[fire] evaluating on future climate") cmip_models = [ - ('CanESM5-CanOE', 'r3i1p2f1'), - ('MIROC-ES2L', 'r1i1p1f2'), - ('ACCESS-CM2', 'r1i1p1f1'), - ('ACCESS-ESM1-5', 'r10i1p1f1'), - ('MRI-ESM2-0', 'r1i1p1f1'), - ('MPI-ESM1-2-LR', 'r10i1p1f1'), + ("CanESM5-CanOE", "r3i1p2f1"), + ("MIROC-ES2L", "r1i1p1f2"), + ("ACCESS-CM2", "r1i1p1f1"), + ("ACCESS-ESM1-5", "r10i1p1f1"), + ("MRI-ESM2-0", "r1i1p1f1"), + ("MPI-ESM1-2-LR", "r10i1p1f1"), ] -scenarios = ['ssp245', 'ssp370', 'ssp585'] -for (cmip_model, member) in cmip_models: +scenarios = ["ssp245", "ssp370", "ssp585"] +for cmip_model, member in cmip_models: for scenario in tqdm(scenarios): results = [] climate = load.cmip( store=store, model=cmip_model, coarsen=coarsen_predict, - method='quantile-mapping-v3', + method="quantile-mapping-v3", scenario=scenario, - tlim=('1969', '2099'), + tlim=("1969", "2099"), variables=data_vars, - sampling='monthly', + sampling="monthly", member=member, historical=True, mask=mask, @@ -145,7 +149,9 @@ ds_future = xr.Dataset() print( - '[fire] conducting prediction for {} {} {}'.format(cmip_model, scenario, year) + "[fire] conducting prediction for {} {} {}".format( + cmip_model, scenario, year + ) ) prepend_time_slice = slice(str(year - 1), str(year - 1)) analysis_time_slice = slice(str(year), str(year + 9)) @@ -155,8 +161,8 @@ climate.sel(time=analysis_time_slice), nftd, add_global_climate_trends={ - 'tmean': {'climate_prepend': prepend, 'rolling_period': 12}, - 'ppt': {'climate_prepend': prepend, 'rolling_period': 12}, + "tmean": {"climate_prepend": prepend, "rolling_period": 12}, + "ppt": {"climate_prepend": prepend, "rolling_period": 12}, }, add_local_climate_trends=None, eval_only=True, @@ -165,34 +171,36 @@ x_z = utils.zscore_2d(x, mean=x_mean, std=x_std) y_hat = model.predict(x_z) prediction = collect.fire(y_hat, climate.sel(time=analysis_time_slice)) - ds_future[cmip_model + '_' + scenario] = ( - ['time', 'y', 'x'], - prediction['prediction'], + ds_future[cmip_model + "_" + scenario] = ( + ["time", "y", "x"], + prediction["prediction"], ) ds_future = ds_future.assign_coords( { - 'x': climate.x, - 'y': climate.y, - 'time': climate.sel(time=analysis_time_slice).time, - 'lat': climate.lat, - 'lon': climate.lon, + "x": climate.x, + "y": climate.y, + "time": climate.sel(time=analysis_time_slice).time, + "lat": climate.lat, + "lon": climate.lon, } ) path = get_store( - 'carbonplan-scratch', - 'data/fire_future_{}_{}_{}.zarr'.format(run_name, cmip_model, scenario), + "carbonplan-scratch", + "data/fire_future_{}_{}_{}.zarr".format( + run_name, cmip_model, scenario + ), account_key=account_key, ) # if it's the first year then make a fresh store by overwriting; if it's later, append to existing file if year == 1970: - mode = 'w' + mode = "w" append_dim = None else: - mode = 'a' - append_dim = 'time' + mode = "a" + append_dim = "time" ds_future.to_zarr(path, mode=mode, append_dim=append_dim) print( - '[fire] completed future run for {}-{} and year {}'.format( + "[fire] completed future run for {}-{} and year {}".format( cmip_model, scenario, year ) ) @@ -206,11 +214,18 @@ ## combine all of the runs into a single zarr file for follow-on analysis postprocess = True -gcms = ['CanESM5-CanOE', 'MIROC-ES2L', 'ACCESS-CM2', 'ACCESS-ESM1-5', 'MRI-ESM2-0', 'MPI-ESM1-2-LR'] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +gcms = [ + "CanESM5-CanOE", + "MIROC-ES2L", + "ACCESS-CM2", + "ACCESS-ESM1-5", + "MRI-ESM2-0", + "MPI-ESM1-2-LR", +] +scenarios = ["ssp245", "ssp370", "ssp585"] out_path = get_store( - 'carbonplan-forests', - 'risks/results/paper/fire_cmip_{}.zarr'.format(run_name), + "carbonplan-forests", + "risks/results/paper/fire_cmip_{}.zarr".format(run_name), account_key=account_key, ) @@ -220,17 +235,19 @@ scenario_list = [] for scenario in scenarios: path = get_store( - 'carbonplan-scratch', - 'data/fire_future_{}_{}_{}.zarr'.format(run_name, gcm, scenario), + "carbonplan-scratch", + "data/fire_future_{}_{}_{}.zarr".format(run_name, gcm, scenario), account_key=account_key, ) scenario_list.append( - xr.open_zarr(path).rename({'{}_{}'.format(gcm, scenario): 'probability'}) + xr.open_zarr(path).rename( + {"{}_{}".format(gcm, scenario): "probability"} + ) ) - print('{} {} is done!'.format(scenario, gcm)) - ds = xr.concat(scenario_list, dim='scenario') - ds = ds.assign_coords({'scenario': scenarios}) + print("{} {} is done!".format(scenario, gcm)) + ds = xr.concat(scenario_list, dim="scenario") + ds = ds.assign_coords({"scenario": scenarios}) gcm_list.append(ds) - full_ds = xr.concat(gcm_list, dim='gcm') - full_ds = full_ds.assign_coords({'gcm': gcms}) - full_ds.to_zarr(out_path, consolidated=True, mode='w') + full_ds = xr.concat(gcm_list, dim="gcm") + full_ds = full_ds.assign_coords({"gcm": gcms}) + full_ds.to_zarr(out_path, consolidated=True, mode="w") diff --git a/scripts/insects.py b/scripts/insects.py index 5bf7fd1..0f765b8 100644 --- a/scripts/insects.py +++ b/scripts/insects.py @@ -8,42 +8,42 @@ args = sys.argv if len(args) < 2: - store = 'local' + store = "local" else: store = args[1] -data_vars = ['ppt', 'tavg'] -data_aggs = ['sum', 'mean'] +data_vars = ["ppt", "tavg"] +data_aggs = ["sum", "mean"] -print('[insects] loading data') -df = load.fia(store=store, states='conus', group_repeats=True) +print("[insects] loading data") +df = load.fia(store=store, states="conus", group_repeats=True) df = load.terraclim( store=store, - tlim=(int(df['year_0'].min()), 2020), + tlim=(int(df["year_0"].min()), 2020), data_vars=data_vars, data_aggs=data_aggs, df=df, group_repeats=True, ) -print('[insects] prepare for fitting') +print("[insects] prepare for fitting") x, y, pf = prepare.insects(df) x_z, x_mean, x_std = utils.zscore_2d(x) -codes = pf['type_code'].unique() +codes = pf["type_code"].unique() -print('[insects] fit models') +print("[insects] fit models") models = {} for code in tqdm(codes): - inds = pf['type_code'] == code + inds = pf["type_code"] == code if (y[inds].sum() > 1) & (y[inds].sum() > 1): model = fit.hurdle(x=x_z[inds], y=y[inds]) models[code] = model -print('[insects] preparing for evaluations') -df = load.fia(store=store, states='conus') -pf = df[['lat', 'lon', 'type_code']].copy().reset_index(drop=True) +print("[insects] preparing for evaluations") +df = load.fia(store=store, states="conus") +pf = df[["lat", "lon", "type_code"]].copy().reset_index(drop=True) -print('[insects] evaluating predictions on historical data') +print("[insects] evaluating predictions on historical data") df = load.terraclim( store=store, tlim=(2005, 2014), @@ -56,19 +56,19 @@ for code in codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code - pf.loc[inds, 'historical'] = model.predict(x=x_z[inds]) + inds = df["type_code"] == code + pf.loc[inds, "historical"] = model.predict(x=x_z[inds]) -print('[insects] evaluating on future climate models') +print("[insects] evaluating on future climate models") targets = list(map(lambda x: str(x), np.arange(2020, 2120, 20))) -cmip_models = ['BCC-CSM2-MR', 'ACCESS-ESM1-5', 'CanESM5', 'MIROC6', 'MPI-ESM1-2-LR'] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +cmip_models = ["BCC-CSM2-MR", "ACCESS-ESM1-5", "CanESM5", "MIROC6", "MPI-ESM1-2-LR"] +scenarios = ["ssp245", "ssp370", "ssp585"] for it in tqdm(range(len(targets))): target = targets[it] tlim = (str(int(target) - 5), str(int(target) + 4)) for cmip_model in cmip_models: for scenario in scenarios: - key = cmip_model + '_' + scenario + '_' + target + key = cmip_model + "_" + scenario + "_" + target df = load.cmip( store=store, tlim=(int(tlim[0]), int(tlim[1])), @@ -85,9 +85,11 @@ for code in codes: if code in models.keys(): model = models[code] - inds = df['type_code'] == code + inds = df["type_code"] == code pf.loc[inds, key] = model.predict(x=x_z[inds]) -pf['r2'] = pf['type_code'].map(lambda k: models[k].train_r2 if k in models.keys() else np.NaN) +pf["r2"] = pf["type_code"].map( + lambda k: models[k].train_r2 if k in models.keys() else np.NaN +) -pf.to_parquet('data/insects.parquet', compression='gzip', engine='fastparquet') +pf.to_parquet("data/insects.parquet", compression="gzip", engine="fastparquet") diff --git a/scripts/package_insects_drought.py b/scripts/package_insects_drought.py index 34fa090..7162703 100644 --- a/scripts/package_insects_drought.py +++ b/scripts/package_insects_drought.py @@ -10,7 +10,7 @@ # flake8: noqa -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +account_key = os.environ.get("BLOB_ACCOUNT_KEY") # this is only used to provide the x/y template for the insects/drought tifs grid_template = ( @@ -21,51 +21,55 @@ # # we'll pass a mask for when we do the webmap data prep cmip_insect_url_template = "https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/InsectProjections_Maps_5-5-21/InsectModelProjection_{}.{}.{}-{}.{}-v18climate_05-05-2021.tif" da = load.impacts(cmip_insect_url_template, grid_template, mask=None) * 100 -out_path = get_store('carbonplan-forests', 'risks/results/paper/insects_cmip_v5.zarr') +out_path = get_store("carbonplan-forests", "risks/results/paper/insects_cmip_v5.zarr") ds = xr.Dataset() -ds['probability'] = da.to_array(dim='vars').rename({'vars': 'gcm'}) -ds.to_zarr(out_path, mode='w', consolidated=True) +ds["probability"] = da.to_array(dim="vars").rename({"vars": "gcm"}) +ds.to_zarr(out_path, mode="w", consolidated=True) cmip_drought_url_template = "https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/DroughtProjections_Maps_5-5-21/DroughtModelProjection_{}.{}.{}-{}.{}-v18climate_05-05-2021.tif" da = load.impacts(cmip_drought_url_template, grid_template, mask=None) * 100 -out_path = get_store('carbonplan-forests', 'risks/results/paper/drought_cmip_v5.zarr') +out_path = get_store("carbonplan-forests", "risks/results/paper/drought_cmip_v5.zarr") ds = xr.Dataset() -ds['probability'] = da.to_array(dim='vars').rename({'vars': 'gcm'}) -ds.to_zarr(out_path, mode='w', consolidated=True) +ds["probability"] = da.to_array(dim="vars").rename({"vars": "gcm"}) +ds.to_zarr(out_path, mode="w", consolidated=True) # load in historical runs to create drought_terraclimate and insects_terraclimate terraclimate_insect_url_template = "https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/Fig2_TerraClimateHistModels_4-22-21/InsectModel_ModeledTerraClimateFIAlong_{}-{}_04-22-2021.tif" ds = xr.Dataset() -ds['probability'] = ( +ds["probability"] = ( load.impacts( terraclimate_insect_url_template, grid_template, mask=None, period_start=1990, period_end=2020, - met_data='terraclimate', + met_data="terraclimate", ) * 100 ) -out_path = get_store('carbonplan-forests', 'risks/results/paper/insects_terraclimate.zarr') -ds.to_zarr(out_path, mode='w', consolidated=True) +out_path = get_store( + "carbonplan-forests", "risks/results/paper/insects_terraclimate.zarr" +) +ds.to_zarr(out_path, mode="w", consolidated=True) terraclimate_drought_url_template = "https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-05-03-2021/Fig2_TerraClimateHistModels_4-22-21/DroughtModel_ModeledTerraClimateFIAlong_{}-{}_04-22-2021.tif" ds = xr.Dataset() -ds['probability'] = ( +ds["probability"] = ( load.impacts( terraclimate_drought_url_template, grid_template, mask=None, period_start=1990, period_end=2020, - met_data='terraclimate', + met_data="terraclimate", ) * 100 ) -out_path = get_store('carbonplan-forests', 'risks/results/paper/drought_terraclimate.zarr') -ds.to_zarr(out_path, mode='w', consolidated=True) +out_path = get_store( + "carbonplan-forests", "risks/results/paper/drought_terraclimate.zarr" +) +ds.to_zarr(out_path, mode="w", consolidated=True) diff --git a/scripts/regrid.py b/scripts/regrid.py index ace59ea..3e319cc 100644 --- a/scripts/regrid.py +++ b/scripts/regrid.py @@ -11,66 +11,69 @@ args = sys.argv if len(args) < 2: - raise ValueError('must specify dataset') + raise ValueError("must specify dataset") dataset = args[1] if len(args) == 2: - store = 'local' + store = "local" else: store = args[2] cmip_models = [ - 'CanESM5-CanOE', - 'MIROC-ES2L', - 'ACCESS-CM2', - 'ACCESS-ESM1-5', - 'MRI-ESM2-0', - 'MPI-ESM1-2-LR', + "CanESM5-CanOE", + "MIROC-ES2L", + "ACCESS-CM2", + "ACCESS-ESM1-5", + "MRI-ESM2-0", + "MPI-ESM1-2-LR", ] -scenarios = ['ssp245', 'ssp370', 'ssp585'] +scenarios = ["ssp245", "ssp370", "ssp585"] targets = list(map(lambda x: str(x), np.arange(2010, 2100, 10))) -pf = pd.read_parquet(f'data/{dataset}.parquet') +pf = pd.read_parquet(f"data/{dataset}.parquet") ds = xr.Dataset() -print(f'[{dataset}] filtering values') +print(f"[{dataset}] filtering values") pf = pf.dropna().reset_index(drop=True) -print(f'[{dataset}] computing multi model mean') +print(f"[{dataset}] computing multi model mean") for scenario in scenarios: for target in targets: keys = list( filter( lambda x: x is not None, - [key if ((scenario in key) & (target in key)) else None for key in pf.columns], + [ + key if ((scenario in key) & (target in key)) else None + for key in pf.columns + ], ) ) - pf[scenario + '_' + target] = pf[keys].mean(axis=1) + pf[scenario + "_" + target] = pf[keys].mean(axis=1) -print(f'[{dataset}] regridding predictions') +print(f"[{dataset}] regridding predictions") nlcd = load.nlcd(store=store, year=2016, classes=[41, 42, 43, 90]) -final_mask = nlcd.sum('band') -final_mask.attrs['crs'] = nlcd.attrs['crs'] +final_mask = nlcd.sum("band") +final_mask.attrs["crs"] = nlcd.attrs["crs"] -if 'biomass' in dataset: +if "biomass" in dataset: final_mask.values = final_mask.values * (final_mask.values > 0.5) else: final_mask.values = final_mask.values > 0.5 -ds['historical'] = fit.interp(pf, final_mask, var='historical') +ds["historical"] = fit.interp(pf, final_mask, var="historical") for scenario in tqdm(scenarios): results = [] for target in targets: - key = scenario + '_' + target + key = scenario + "_" + target gridded = fit.interp(pf, final_mask, var=key) results.append(gridded) - da = xr.concat(results, dim=xr.Variable('year', targets)) + da = xr.concat(results, dim=xr.Variable("year", targets)) ds[scenario] = da -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +account_key = os.environ.get("BLOB_ACCOUNT_KEY") path = utils.get_store( - 'carbonplan-forests', f'risks/results/web/{dataset}.zarr', account_key=account_key + "carbonplan-forests", f"risks/results/web/{dataset}.zarr", account_key=account_key ) -ds.to_zarr(path, mode='w') +ds.to_zarr(path, mode="w") diff --git a/scripts/regrid_bill.py b/scripts/regrid_bill.py index a217df8..de97d94 100644 --- a/scripts/regrid_bill.py +++ b/scripts/regrid_bill.py @@ -5,33 +5,33 @@ from carbonplan_forest_risks import fit, load, utils -store = 'az' +store = "az" df = pd.read_csv( - 'https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-04-14-2021/Fig1D_DroughtModel_ModeledFIAlongEnsembleHistMort_FIAlong_04-14-2021.csv' + "https://carbonplan.blob.core.windows.net/carbonplan-scratch/from-bill-04-14-2021/Fig1D_DroughtModel_ModeledFIAlongEnsembleHistMort_FIAlong_04-14-2021.csv" ) pf = pd.DataFrame() -pf['lat'] = df['V3'] -pf['lon'] = df['V2'] -pf['mortality'] = df['V6'] +pf["lat"] = df["V3"] +pf["lon"] = df["V2"] +pf["mortality"] = df["V6"] pf = pf.dropna().reset_index(drop=True) ds = xr.Dataset() nlcd = load.nlcd(store=store, year=2016, classes=[41, 42, 43, 90]) -final_mask = nlcd.sum('band') -final_mask.attrs['crs'] = nlcd.attrs['crs'] +final_mask = nlcd.sum("band") +final_mask.attrs["crs"] = nlcd.attrs["crs"] final_mask.values = final_mask.values > 0.5 -gridded = fit.interp(pf, final_mask, var='mortality') +gridded = fit.interp(pf, final_mask, var="mortality") -ds['historical'] = gridded +ds["historical"] = gridded -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +account_key = os.environ.get("BLOB_ACCOUNT_KEY") path = utils.get_store( - 'carbonplan-forests', 'risks/results/web/drought.zarr', account_key=account_key + "carbonplan-forests", "risks/results/web/drought.zarr", account_key=account_key ) -ds.to_zarr(path, mode='w') +ds.to_zarr(path, mode="w") diff --git a/scripts/stats.py b/scripts/stats.py index 06bada2..9acd165 100644 --- a/scripts/stats.py +++ b/scripts/stats.py @@ -31,16 +31,18 @@ def score(x, y, model, da, method): b = b - b.mean() spatial_r2 = 1 - np.sum((a - b) ** 2) / np.sum((a - np.mean(a)) ** 2) - bias = (prediction['prediction'].mean().values - da.mean().values) / da.mean().values + bias = ( + prediction["prediction"].mean().values - da.mean().values + ) / da.mean().values return { - 'method': method, - 'roc': roc, - 'r2': r2, - 'annual_r2': annual_r2, - 'seasonal_r2': seasonal_r2, - 'spatial_r2': spatial_r2, - 'bias': bias, + "method": method, + "roc": roc, + "r2": r2, + "annual_r2": annual_r2, + "seasonal_r2": seasonal_r2, + "spatial_r2": spatial_r2, + "bias": bias, } @@ -56,18 +58,18 @@ def crossval(x, y, selection, da, method): return score(test_x, test_y, model, da[selection], method) -def shuffle(x, y, da, method='months'): - if 'months' in method: - inds = np.arange(len(da['time'].values)).copy() +def shuffle(x, y, da, method="months"): + if "months" in method: + inds = np.arange(len(da["time"].values)).copy() np.random.shuffle(inds) y = y.copy().reshape(da.shape)[inds].flatten() - elif 'years' in method: - years = pd.to_datetime(da['time'].values).year + elif "years" in method: + years = pd.to_datetime(da["time"].values).year scrambled = years.unique().values.copy() np.random.shuffle(scrambled) inds = np.array([np.argwhere(years == y) for y in scrambled]).flatten() y = y.copy().reshape(da.shape)[inds].flatten() - elif 'all' in method: + elif "all" in method: y = y.copy() np.random.shuffle(y) else: @@ -86,10 +88,10 @@ def append(df, results): variables = ["ppt", "tmean", "cwd"] store = "az" -print('[stats] loading data') -mask = (load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum('band') > 0.25).astype( - 'float' -) +print("[stats] loading data") +mask = ( + load.nlcd(store=store, year=2001).sel(band=[41, 42, 43, 90]).sum("band") > 0.25 +).astype("float") nftd = load.nftd(store=store, area_threshold=1500, coarsen=coarsen, mask=mask) climate = load.terraclim( store=store, @@ -101,62 +103,64 @@ def append(df, results): ) mtbs = load.mtbs(store=store, coarsen=coarsen, tlim=tlim, mask=mask) -prepend = climate.sel(time=slice('1983', '1983')) +prepend = climate.sel(time=slice("1983", "1983")) x, y = prepare.fire( - climate.sel(time=slice('1984', '2018')), + climate.sel(time=slice("1984", "2018")), nftd, mtbs, add_global_climate_trends={ - 'tmean': {'climate_prepend': prepend, 'rolling_period': 12}, - 'ppt': {'climate_prepend': prepend, 'rolling_period': 12}, + "tmean": {"climate_prepend": prepend, "rolling_period": 12}, + "ppt": {"climate_prepend": prepend, "rolling_period": 12}, }, add_local_climate_trends=None, - analysis_tlim=slice('1984', '2018'), + analysis_tlim=slice("1984", "2018"), ) x_z, x_mean, x_std = utils.zscore_2d(x) df = pd.DataFrame() # same training and testing -print('[stats] same training and testing') +print("[stats] same training and testing") model = fit.hurdle(x_z, y, log=False) -results = score(x_z, y, model, mtbs['monthly'], 'training') +results = score(x_z, y, model, mtbs["monthly"], "training") df = append(df, results) # cross validation -years = pd.to_datetime(mtbs['time'].values).year +years = pd.to_datetime(mtbs["time"].values).year nruns = 10 -print('[stats] split halves cross validation') +print("[stats] split halves cross validation") for index in range(nruns): scrambled = years.unique().values.copy() np.random.shuffle(scrambled) subset = scrambled[0 : round(len(scrambled) / 2)] selection = [y in subset for y in years] - results = crossval(x_z, y, selection, mtbs['monthly'], f'split_halves_{index}') + results = crossval(x_z, y, selection, mtbs["monthly"], f"split_halves_{index}") df = append(df, results) -print('[stats] extrapolation cross validation') -for index, threshold in enumerate([2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014]): +print("[stats] extrapolation cross validation") +for index, threshold in enumerate( + [2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014] +): selection = years > threshold - results = crossval(x_z, y, selection, mtbs['monthly'], f'extrapolate_{index}') + results = crossval(x_z, y, selection, mtbs["monthly"], f"extrapolate_{index}") df = append(df, results) -print('[stats] shuffling') +print("[stats] shuffling") for index in range(nruns): - results = shuffle(x_z, y, mtbs['monthly'], f'shuffle_months_{index}') + results = shuffle(x_z, y, mtbs["monthly"], f"shuffle_months_{index}") df = append(df, results) for index in range(nruns): - results = shuffle(x_z, y, mtbs['monthly'], f'shuffle_years_{index}') + results = shuffle(x_z, y, mtbs["monthly"], f"shuffle_years_{index}") df = append(df, results) for index in range(nruns): - results = shuffle(x_z, y, mtbs['monthly'], f'shuffle_all_{index}') + results = shuffle(x_z, y, mtbs["monthly"], f"shuffle_all_{index}") df = append(df, results) -df = df[['method', 'roc', 'r2', 'annual_r2', 'seasonal_r2', 'spatial_r2', 'bias']] +df = df[["method", "roc", "r2", "annual_r2", "seasonal_r2", "spatial_r2", "bias"]] print(df) -df.to_csv('fire_stats.csv') +df.to_csv("fire_stats.csv") diff --git a/scripts/website_prep.py b/scripts/website_prep.py index 335097b..0d8416d 100644 --- a/scripts/website_prep.py +++ b/scripts/website_prep.py @@ -12,10 +12,10 @@ # flake8: noqa -warnings.filterwarnings('ignore') +warnings.filterwarnings("ignore") -impacts_to_process = ['fire'] -account_key = os.environ.get('BLOB_ACCOUNT_KEY') +impacts_to_process = ["fire"] +account_key = os.environ.get("BLOB_ACCOUNT_KEY") rolling = True # specify the kind of mask you want to use mask_for_website = True @@ -34,8 +34,8 @@ for impact in impacts_to_process: ds = xr.open_zarr( get_store( - 'carbonplan-forests', - 'risks/results/web/{}_cmip_high_res.zarr'.format(impact), + "carbonplan-forests", + "risks/results/web/{}_cmip_high_res.zarr".format(impact), account_key=account_key, ) ) @@ -45,30 +45,34 @@ "y": website_mask.y, } ) - if impact == 'fire': - ds = ds.groupby('time.year').sum().coarsen(year=10).mean().compute() - ds = ds.assign_coords({'year': np.arange(1970, 2100, 10)}) + if impact == "fire": + ds = ds.groupby("time.year").sum().coarsen(year=10).mean().compute() + ds = ds.assign_coords({"year": np.arange(1970, 2100, 10)}) ds = ds.rolling(year=2).mean().drop_sel(year=1970) else: - ds = ds.assign_coords({'year': np.arange(1970, 2100, 10)}) + ds = ds.assign_coords({"year": np.arange(1970, 2100, 10)}) ds = ds.rolling(year=2).mean().drop_sel(year=1970) - ds = ds.assign_coords({'year': list(map(lambda x: str(x), np.arange(1980, 2100, 10)))}) + ds = ds.assign_coords( + {"year": list(map(lambda x: str(x), np.arange(1980, 2100, 10)))} + ) if mask_for_website: ds = ds.where(website_mask) # first write out the full ds which will be used for the article out_path = get_store( - 'carbonplan-forests', - 'risks/results/web/{}_full.zarr'.format(impact), + "carbonplan-forests", + "risks/results/web/{}_full.zarr".format(impact), account_key=account_key, ) - ds.to_zarr(out_path, mode='w') + ds.to_zarr(out_path, mode="w") # then write out the ensemble-mean (which will be used for the webmap) - ds = ds.mean(dim='gcm').probability.to_dataset(dim='scenario') + ds = ds.mean(dim="gcm").probability.to_dataset(dim="scenario") out_path = get_store( - 'carbonplan-forests', 'risks/results/web/{}.zarr'.format(impact), account_key=account_key + "carbonplan-forests", + "risks/results/web/{}.zarr".format(impact), + account_key=account_key, ) - ds.to_zarr(out_path, mode='w') + ds.to_zarr(out_path, mode="w") diff --git a/tests/test_forests.py b/tests/test_forests.py index 85b8916..74742a6 100644 --- a/tests/test_forests.py +++ b/tests/test_forests.py @@ -1,4 +1,4 @@ def test_version(): from carbonplan_forest_risks import version - assert version != '0.0.0' + assert version != "0.0.0"