Skip to content

Commit d9b925c

Browse files
Remove Tag from Exposures (#756)
* exposures.base: remove tag from Exposures * doc: remove tag from Exposures tutorial * Entity: no more __str__ overriding * remove tag from Exposures * remove dead code * cosmetics * LitPop remove tag, replaced by novel _metadata attribute `description` Overriding plot_scatter and plot_hexbin to keep custom plot titles unchanged * PEP8 * reaction to out of the blue failing of readthedocs to build * adjust readthedocs environment * add output secction to prevent readthedocs from buildiing * changelog updated * chanchangelog updated * Make description an attribute of Exposures, not just LitPop crop_production, black_marble and spam_agrar also depend on it * exposures.plots: skip standard title formatting * forcast: revert dead code removal * exposures.base.plots: title must not be None * remove remaining traces of Exposures.tag * remove remaining traces of Exposures.tag
1 parent c7157aa commit d9b925c

File tree

15 files changed

+116
-350
lines changed

15 files changed

+116
-350
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Removed:
3030
- Added method `Exposures.centroids_total_value` to replace the functionality of `Exposures.affected_total_value`. This method is temporary and deprecated. [#702](https://github.com/CLIMADA-project/climada_python/pull/702)
3131
- New method `climada.util.api_client.Client.purge_cache`: utility function to remove outdated files from the local file system to free disk space.
3232
([#737](https://github.com/CLIMADA-project/climada_python/pull/737))
33+
- New attribute `climada.entity.exposures.Exposures.description`: used for setting the default title in plots from plotting mathods `plot_hexbin` and `plot_scatter`. [#756](https://github.com/CLIMADA-project/climada_python/pull/756)
3334
- Added advanced examples in unsequa tutorial for coupled input variables and for handling efficiently the loading of multiple large files [#766](https://github.com/CLIMADA-project/climada_python/pull/766)
3435

3536
### Changed
@@ -58,6 +59,7 @@ Removed:
5859
- `list_dataset_infos` from `climada.util.api_client.Client`: the `properties` argument, a `dict`, can now have `None` as values. Before, only strings and lists of strings were allowed. Setting a particular property to `None` triggers a search for datasets where this property is not assigned. [#752](https://github.com/CLIMADA-project/climada_python/pull/752)
5960
- Reduce memory requirements of `TropCyclone.from_tracks` [#749](https://github.com/CLIMADA-project/climada_python/pull/749)
6061
- Support for different wind speed and pressure units in `TCTracks` when running `TropCyclone.from_tracks` [#749](https://github.com/CLIMADA-project/climada_python/pull/749)
62+
- The title of plots created by the `Exposures` methods `plot_hexbin` and `plot_scatter` can be set as a method argument. [#756](https://github.com/CLIMADA-project/climada_python/pull/756)
6163
- Changed the parallel package from Pathos to Multiproess in the unsequa module [#763](https://github.com/CLIMADA-project/climada_python/pull/763)
6264
- Updated installation instructions to use conda for core and petals [#776](https://github.com/CLIMADA-project/climada_python/pull/776)
6365

@@ -83,6 +85,7 @@ Removed:
8385
- `Centroids.set_raster_from_pix_bounds` [#721](https://github.com/CLIMADA-project/climada_python/pull/721)
8486
- `requirements/env_developer.yml` environment specs. Use 'extra' requirements when installing the Python package instead [#712](https://github.com/CLIMADA-project/climada_python/pull/712)
8587
- `Impact.tag` attribute. This change is not backwards-compatible with respect to the files written and read by the `Impact` class [#743](https://github.com/CLIMADA-project/climada_python/pull/743)
88+
- `Exposures.tag` attribute. This change is not backwards-compatible with respect to the files written and read by the `Exposures` class [#756](https://github.com/CLIMADA-project/climada_python/pull/756)
8689
- `Hazard.tag` attribute. This change is not backwards-compatible with respect to the files written and read by the `Hazard` class [#767](https://github.com/CLIMADA-project/climada_python/pull/767)
8790
- `impact.tot_value ` attribute removed from unsequa module [#763](https://github.com/CLIMADA-project/climada_python/pull/763)
8891

climada/entity/entity_def.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from typing import Optional
2626
import pandas as pd
2727

28-
from climada.util.tag import Tag
2928
from climada.entity.impact_funcs.impact_func_set import ImpactFuncSet
3029
from climada.entity.disc_rates.base import DiscRates
3130
from climada.entity.measures.measure_set import MeasureSet
@@ -106,7 +105,7 @@ def read_mat(self, *args, **kwargs):
106105
self.__dict__ = Entity.from_mat(*args, **kwargs).__dict__
107106

108107
@classmethod
109-
def from_excel(cls, file_name, description=''):
108+
def from_excel(cls, file_name):
110109
"""Read csv or xls or xlsx file following climada's template.
111110
112111
Parameters
@@ -125,15 +124,14 @@ def from_excel(cls, file_name, description=''):
125124
"""
126125

127126
exp = Exposures(pd.read_excel(file_name))
128-
exp.tag = Tag(file_name=file_name, description=description)
129127

130-
dr = DiscRates.from_excel(file_name)
128+
disc_rates = DiscRates.from_excel(file_name)
131129
impf_set = ImpactFuncSet.from_excel(file_name)
132130
meas_set = MeasureSet.from_excel(file_name)
133131

134132
return cls(
135133
exposures=exp,
136-
disc_rates=dr,
134+
disc_rates=disc_rates,
137135
impact_func_set=impf_set,
138136
measure_set=meas_set,
139137
)
@@ -178,8 +176,3 @@ def __setattr__(self, name, value):
178176
if not isinstance(value, DiscRates):
179177
raise ValueError("Input value is not (sub)class of DiscRates.")
180178
super().__setattr__(name, value)
181-
182-
def __str__(self):
183-
return 'Exposures: \n' + self.exposures.tag.__str__()
184-
185-
__repr__ = __str__

climada/entity/exposures/base.py

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import cartopy.crs as ccrs
3838

3939
from climada.hazard import Hazard
40-
from climada.util.tag import Tag
4140
import climada.util.hdf5_handler as u_hdf5
4241
from climada.util.constants import ONE_LAT_KM, DEF_CRS, CMAP_RASTER
4342
import climada.util.coordinates as u_coord
@@ -84,8 +83,8 @@ class Exposures():
8483
8584
Attributes
8685
----------
87-
tag : climada.util.tag.Tag
88-
metada - information about the source data
86+
description : str
87+
metadata - description of content and origin of the data
8988
ref_year : int
9089
metada - reference year
9190
value_unit : str
@@ -121,7 +120,7 @@ class Exposures():
121120
TC. There might be different hazards defined: centr_TC, centr_FL, ...
122121
Computed in method assign_centroids().
123122
"""
124-
_metadata = ['tag', 'ref_year', 'value_unit', 'meta', 'description']
123+
_metadata = ['description', 'ref_year', 'value_unit', 'meta']
125124

126125
vars_oblig = ['value', 'latitude', 'longitude']
127126
"""Name of the variables needed to compute the impact."""
@@ -142,7 +141,7 @@ def crs(self):
142141
# In case of gdf without geometry, empty or before set_geometry_points was called
143142
return self.meta.get('crs')
144143

145-
def __init__(self, *args, meta=None, tag=None, ref_year=DEF_REF_YEAR,
144+
def __init__(self, *args, meta=None, description=None, ref_year=DEF_REF_YEAR,
146145
value_unit=DEF_VALUE_UNIT, crs=None, **kwargs):
147146
"""Creates an Exposures object from a GeoDataFrame
148147
@@ -154,8 +153,8 @@ def __init__(self, *args, meta=None, tag=None, ref_year=DEF_REF_YEAR,
154153
Named arguments of the GeoDataFrame constructor, additionally
155154
meta : dict, optional
156155
Metadata dictionary. Default: {} (empty dictionary)
157-
tag : climada.entity.exposures.tag.Tag, optional
158-
Exposures tag. Defaults to the entry of the same name in `meta` or an empty Tag object.
156+
description : str, optional
157+
Default: None
159158
ref_year : int, optional
160159
Reference Year. Defaults to the entry of the same name in `meta` or 2018.
161160
value_unit : str, optional
@@ -168,11 +167,10 @@ def __init__(self, *args, meta=None, tag=None, ref_year=DEF_REF_YEAR,
168167
self.meta = {} if meta is None else meta
169168
if not isinstance(self.meta, dict):
170169
raise ValueError("meta must be a dictionary")
171-
self.tag = self.meta.get('tag', Tag()) if tag is None else tag
170+
self.description = self.meta.get('description') if description is None else description
172171
self.ref_year = self.meta.get('ref_year', DEF_REF_YEAR) if ref_year is None else ref_year
173172
self.value_unit = (self.meta.get('value_unit', DEF_VALUE_UNIT)
174173
if value_unit is None else value_unit)
175-
self.description = kwargs.pop('description') if 'description' in kwargs else None
176174

177175
# remaining generic attributes from derived classes
178176
for mda in type(self)._metadata:
@@ -501,7 +499,6 @@ def from_raster(cls, file_name, band=1, src_crs=None, window=None,
501499
Exposures
502500
"""
503501
exp = cls()
504-
exp.tag = Tag(file_name=file_name)
505502
meta, value = u_coord.read_raster(file_name, [band], src_crs, window,
506503
geometry, dst_crs, transform, width,
507504
height, resampling)
@@ -521,7 +518,7 @@ def from_raster(cls, file_name, band=1, src_crs=None, window=None,
521518

522519
def plot_scatter(self, mask=None, ignore_zero=False, pop_name=True,
523520
buffer=0.0, extend='neither', axis=None, figsize=(9, 13),
524-
adapt_fontsize=True, **kwargs):
521+
adapt_fontsize=True, title=None, **kwargs):
525522
"""Plot exposures geometry's value sum scattered over Earth's map.
526523
The plot will we projected according to the current crs.
527524
@@ -546,17 +543,19 @@ def plot_scatter(self, mask=None, ignore_zero=False, pop_name=True,
546543
adapt_fontsize : bool, optional
547544
If set to true, the size of the fonts will be adapted to the size of the figure.
548545
Otherwise the default matplotlib font size is used. Default is True.
546+
title : str, optional
547+
a title for the plot. If not set `self.description` is used.
549548
kwargs : optional
550549
arguments for scatter matplotlib function, e.g.
551-
cmap='Greys'. Default: 'Wistia'
550+
cmap='Greys'
552551
553552
Returns
554553
-------
555554
cartopy.mpl.geoaxes.GeoAxesSubplot
556555
"""
557556
crs_epsg, _ = u_plot.get_transformation(self.crs)
558-
title = "\n".join(self.tag.description)
559-
cbar_label = f'Value ({self.value_unit})'
557+
if title is None:
558+
title = self.description or ""
560559
if mask is None:
561560
mask = np.ones((self.gdf.shape[0],), dtype=bool)
562561
if ignore_zero:
@@ -566,8 +565,13 @@ def plot_scatter(self, mask=None, ignore_zero=False, pop_name=True,
566565
value = self.gdf.value[mask][pos_vals].values
567566
coord = np.stack([self.gdf.latitude[mask][pos_vals].values,
568567
self.gdf.longitude[mask][pos_vals].values], axis=1)
569-
return u_plot.geo_scatter_from_array(value, coord, cbar_label, title,
570-
pop_name, buffer, extend,
568+
return u_plot.geo_scatter_from_array(array_sub=value,
569+
geo_coord=coord,
570+
var_name=f'Value ({self.value_unit})',
571+
title=title,
572+
pop_name=pop_name,
573+
buffer=buffer,
574+
extend=extend,
571575
proj=crs_epsg,
572576
axes=axis,
573577
figsize=figsize,
@@ -576,7 +580,7 @@ def plot_scatter(self, mask=None, ignore_zero=False, pop_name=True,
576580

577581
def plot_hexbin(self, mask=None, ignore_zero=False, pop_name=True,
578582
buffer=0.0, extend='neither', axis=None, figsize=(9, 13),
579-
adapt_fontsize=True, **kwargs):
583+
adapt_fontsize=True, title=None, **kwargs):
580584
"""Plot exposures geometry's value sum binned over Earth's map.
581585
An other function for the bins can be set through the key reduce_C_function.
582586
The plot will we projected according to the current crs.
@@ -605,6 +609,8 @@ def plot_hexbin(self, mask=None, ignore_zero=False, pop_name=True,
605609
If set to true, the size of the fonts will be adapted to the size of the figure.
606610
Otherwise the default matplotlib font size is used.
607611
Default is True.
612+
title : str, optional
613+
a title for the plot. If not set `self.description` is used.
608614
kwargs : optional
609615
arguments for hexbin matplotlib function, e.g.
610616
`reduce_C_function=np.average`.
@@ -615,8 +621,8 @@ def plot_hexbin(self, mask=None, ignore_zero=False, pop_name=True,
615621
cartopy.mpl.geoaxes.GeoAxesSubplot
616622
"""
617623
crs_epsg, _ = u_plot.get_transformation(self.crs)
618-
title = "\n".join(self.tag.description)
619-
cbar_label = f'Value ({self.value_unit})'
624+
if title is None:
625+
title = self.description or ""
620626
if 'reduce_C_function' not in kwargs:
621627
kwargs['reduce_C_function'] = np.sum
622628
if mask is None:
@@ -628,9 +634,17 @@ def plot_hexbin(self, mask=None, ignore_zero=False, pop_name=True,
628634
value = self.gdf.value[mask][pos_vals].values
629635
coord = np.stack([self.gdf.latitude[mask][pos_vals].values,
630636
self.gdf.longitude[mask][pos_vals].values], axis=1)
631-
return u_plot.geo_bin_from_array(value, coord, cbar_label, title,
632-
pop_name, buffer, extend, proj=crs_epsg,
633-
axes=axis, figsize=figsize, adapt_fontsize=adapt_fontsize,
637+
return u_plot.geo_bin_from_array(array_sub=value,
638+
geo_coord=coord,
639+
var_name=f'Value ({self.value_unit})',
640+
title=title,
641+
pop_name=pop_name,
642+
buffer=buffer,
643+
extend=extend,
644+
proj=crs_epsg,
645+
axes=axis,
646+
figsize=figsize,
647+
adapt_fontsize=adapt_fontsize,
634648
**kwargs)
635649

636650
def plot_raster(self, res=None, raster_res=None, save_tiff=None,
@@ -842,6 +856,9 @@ def from_hdf5(cls, file_name):
842856
for key, val in metadata.items():
843857
if key in type(exp)._metadata: # pylint: disable=protected-access
844858
setattr(exp, key, val)
859+
if key == 'tag': # for backwards compatitbility with climada <= 3.x
860+
descriptions = [u_hdf5.to_string(x) for x in getattr(val, 'description', [])]
861+
exp.description = "\n".join(descriptions) if descriptions else None
845862
return exp
846863

847864
def read_mat(self, *args, **kwargs):
@@ -1158,7 +1175,7 @@ def add_sea(exposures, sea_res, scheduler=None):
11581175
ref_year=exposures.ref_year,
11591176
value_unit=exposures.value_unit,
11601177
meta=exposures.meta,
1161-
tag=exposures.tag
1178+
description=exposures.description,
11621179
)
11631180

11641181

@@ -1217,5 +1234,3 @@ def _read_mat_metadata(exposures, data, file_name, var_names):
12171234
file_name, data[var_names['var_name']['uni']][0][0])
12181235
except KeyError:
12191236
exposures.value_unit = DEF_VALUE_UNIT
1220-
1221-
exposures.tag = Tag(file_name)

climada/entity/exposures/litpop/litpop.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import climada.util.coordinates as u_coord
3030
import climada.util.finance as u_fin
3131

32-
from climada.util.tag import Tag
3332
from climada.entity.exposures.litpop import nightlight as nl_util
3433
from climada.entity.exposures.litpop import gpw_population as pop_util
3534
from climada.entity.exposures.base import Exposures, INDICATOR_IMPF, DEF_REF_YEAR
@@ -81,7 +80,7 @@ def from_countries(cls, countries, res_arcsec=30, exponents=(1,1),
8180
data_dir=SYSTEM_DIR):
8281
"""Init new LitPop exposure object for a list of countries (admin 0).
8382
84-
Sets attributes `ref_year`, `tag`, `crs`, `value`, `geometry`, `meta`,
83+
Sets attributes `ref_year`, `crs`, `value`, `geometry`, `meta`,
8584
`value_unit`, `exponents`,`fin_mode`, `gpw_version`, and `admin1_calc`.
8685
8786
Parameters
@@ -183,19 +182,19 @@ def from_countries(cls, countries, res_arcsec=30, exponents=(1,1),
183182
LOGGER.warning('Some countries could not be identified and are ignored: '
184183
'%s. Litpop only initiated for: %s', countries_out, countries_in)
185184

186-
tag = Tag(description=f'LitPop Exposure for {countries_in} at {res_arcsec} as, '
187-
f'year: {reference_year}, financial mode: {fin_mode}, '
188-
f'exp: {exponents}, admin1_calc: {admin1_calc}')
185+
description = (f'LitPop Exposure for {countries_in} at {res_arcsec} as,'
186+
f' year: {reference_year}, financial mode: {fin_mode},'
187+
f' exp: {exponents}, admin1_calc: {admin1_calc}')
189188

190189
exp = cls(
191190
data=Exposures.concat(litpop_list).gdf,
192191
crs=litpop_list[0].crs,
193192
ref_year=reference_year,
194-
tag=tag,
195193
value_unit=get_value_unit(fin_mode),
196-
exponents = exponents,
197-
gpw_version = gpw_version,
198-
fin_mode = fin_mode,
194+
exponents=exponents,
195+
gpw_version=gpw_version,
196+
fin_mode=fin_mode,
197+
description=description
199198
)
200199

201200
try:
@@ -429,9 +428,9 @@ def from_shape_and_countries(cls, shape, countries, res_arcsec=30, exponents=(1,
429428
else:
430429
raise NotImplementedError('Not implemented for `shape` of type {type(shape)}')
431430

432-
exp.tag.append(Tag(description=f'LitPop Exposure for custom shape in {countries} at '
433-
f'{res_arcsec} as, year: {reference_year}, financial mode: '
434-
f'{fin_mode}, exp: {exponents}, admin1_calc: {admin1_calc}'))
431+
exp.description = (f'LitPop Exposure for custom shape in {countries} at'
432+
f' {res_arcsec} as, year: {reference_year}, financial mode:'
433+
f' {fin_mode}, exp: {exponents}, admin1_calc: {admin1_calc}')
435434
exp.set_gdf(gdf.reset_index())
436435

437436
try:
@@ -473,7 +472,7 @@ def from_shape(
473472
"""init LitPop exposure object for a custom shape.
474473
Requires user input regarding the total value to be disaggregated.
475474
476-
Sets attributes `ref_year`, `tag`, `crs`, `value`, `geometry`, `meta`,
475+
Sets attributes `ref_year`, `crs`, `value`, `geometry`, `meta`,
477476
`value_unit`, `exponents`,`fin_mode`, `gpw_version`, and `admin1_calc`.
478477
479478
This method can be used to initiated LitPop Exposure for sub-national
@@ -542,21 +541,21 @@ def from_shape(
542541
elif total_value is not None:
543542
raise TypeError("total_value must be int, float or None.")
544543

545-
tag = Tag(description = f'LitPop Exposure for custom shape at {res_arcsec} as, ' \
546-
f'year: {reference_year}, exp: {exponents}')
544+
description = (f'LitPop Exposure for custom shape at {res_arcsec} as,'
545+
f' year: {reference_year}, exp: {exponents}')
547546

548547
litpop_gdf[INDICATOR_IMPF] = 1
549548

550549
exp = cls(
551-
data=litpop_gdf,
552-
crs=litpop_gdf.crs,
553-
ref_year=reference_year,
554-
tag=tag,
555-
value_unit=value_unit,
556-
exponents = exponents,
557-
gpw_version = gpw_version,
558-
fin_mode = None,
559-
)
550+
data=litpop_gdf,
551+
crs=litpop_gdf.crs,
552+
ref_year=reference_year,
553+
value_unit=value_unit,
554+
exponents=exponents,
555+
gpw_version=gpw_version,
556+
fin_mode=None,
557+
description=description
558+
)
560559

561560
if min(len(exp.gdf.latitude.unique()), len(exp.gdf.longitude.unique())) > 1:
562561
#if exp.gdf.shape[0] > 1 and len(exp.gdf.latitude.unique()) > 1:

0 commit comments

Comments
 (0)