Skip to content

Commit 1c4be59

Browse files
committed
change to make append, select and clear valid for every exposures child class
1 parent 5e8a6ff commit 1c4be59

File tree

2 files changed

+98
-97
lines changed

2 files changed

+98
-97
lines changed

climada/entity/exposures/base.py

Lines changed: 96 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,31 @@ class Exposures():
5151
centroid(s) affecting each exposure. Filled in 'assign' method.
5252
"""
5353

54+
vars_oblig = {'tag',
55+
'ref_year',
56+
'value_unit',
57+
'_coord',
58+
'value',
59+
'impact_id',
60+
'id'
61+
}
62+
"""Name of the variables needed to compute the impact. Types: scalar, str,
63+
list, 1dim np.array of size num_exposures, GridPoints and Tag."""
64+
65+
vars_def = {'assigned'
66+
}
67+
"""Name of the variables used in impact calculation whose value is
68+
descriptive or can be recomputed. Types: dict.
69+
"""
70+
71+
vars_opt = {'deductible',
72+
'cover',
73+
'category_id',
74+
'region_id'
75+
}
76+
"""Name of the variables that aren't need to compute the impact. Types:
77+
scalar, string, list, 1dim np.array of size num_exposures."""
78+
5479
def __init__(self, file_name='', description=''):
5580
"""Fill values from file, if provided.
5681
@@ -73,20 +98,13 @@ def __init__(self, file_name='', description=''):
7398
>>> exp_city.id = np.array([11, 12, 13])
7499
>>> exp_city.check()
75100
76-
Read exposures from Zurich.mat and checks consistency data.
101+
Read exposures from ENT_TEMPLATE_XLS and checks consistency data.
77102
78103
>>> exp_city = Exposures(ENT_TEMPLATE_XLS)
79104
"""
80-
self.clear()
81-
if file_name != '':
82-
self.read(file_name, description)
83-
84-
def clear(self):
85-
"""Reinitialize attributes."""
86-
# Optional variables
87105
self.tag = Tag()
88106
self.ref_year = CONFIG['entity']['present_ref_year']
89-
self.value_unit = 'NA'
107+
self.value_unit = ''
90108
# Following values defined for each exposure
91109
# Obligatory variables
92110
self.coord = GridPoints()
@@ -100,9 +118,21 @@ def clear(self):
100118
self.region_id = np.array([], int)
101119
self.assigned = dict()
102120

121+
if file_name != '':
122+
self.read(file_name, description)
123+
124+
def clear(self):
125+
"""Reinitialize attributes."""
126+
for (var_name, var_val) in self.__dict__.items():
127+
if isinstance(var_val, np.ndarray) and var_val.ndim == 1:
128+
setattr(self, var_name, np.array([]))
129+
else:
130+
setattr(self, var_name, var_val.__class__())
131+
self.ref_year = CONFIG['entity']['present_ref_year']
132+
103133
def assign(self, hazard):
104-
"""Compute the hazard centroids affecting to each exposure. The
105-
position of the centroids are provided, not their ids.
134+
"""Compute the hazard centroids affecting to each exposure. Returns the
135+
position of the centroids, not their ids.
106136
107137
Parameters:
108138
hazard (subclass Hazard): one hazard
@@ -123,8 +153,18 @@ def check(self):
123153
if np.unique(self.id).size != num_exp:
124154
LOGGER.error("There are exposures with the same identifier.")
125155
raise ValueError
126-
self._check_obligatories(num_exp)
127-
self._check_optionals(num_exp)
156+
157+
check.check_oligatories(self.__dict__, self.vars_oblig, 'Exposures.',
158+
num_exp, num_exp, 2)
159+
check.check_optionals(self.__dict__, self.vars_opt, 'Exposures.', num_exp)
160+
161+
check.empty_optional(self.assigned, "Exposures.assigned")
162+
for (ass_haz, ass) in self.assigned.items():
163+
if not ass_haz:
164+
LOGGER.warning('Exposures.assigned: assigned hazard type ' \
165+
'not set.')
166+
check.array_optional(num_exp, ass, 'Exposures.assigned')
167+
128168

129169
def plot(self, ignore_zero=True, pop_name=True, buffer_deg=1.0,
130170
extend='neither', **kwargs):
@@ -192,18 +232,17 @@ def remove(self, exp_id):
192232
except IndexError:
193233
LOGGER.info('No exposure with id %s.', exp_id)
194234
return
195-
self.coord = np.delete(self.coord, pos_del, axis=0)
196-
self.value = np.delete(self.value, pos_del)
197-
self.impact_id = np.delete(self.impact_id, pos_del)
198-
self.id = np.delete(self.id, pos_del)
199-
if self.deductible.size:
200-
self.deductible = np.delete(self.deductible, pos_del)
201-
if self.cover.size:
202-
self.cover = np.delete(self.cover, pos_del)
203-
if self.category_id.size:
204-
self.category_id = np.delete(self.category_id, pos_del)
205-
if self.region_id.size:
206-
self.region_id = np.delete(self.region_id, pos_del)
235+
236+
for (var_name, var_val) in self.__dict__.items():
237+
if var_name in self.vars_oblig:
238+
if isinstance(var_val, np.ndarray) and var_val.ndim == 1:
239+
setattr(self, var_name, np.delete(var_val, pos_del))
240+
elif isinstance(var_val, np.ndarray) and var_val.ndim == 2:
241+
setattr(self, var_name, np.delete(var_val, pos_del, axis=0))
242+
else:
243+
if isinstance(var_val, np.ndarray) and var_val.size:
244+
setattr(self, var_name, np.delete(var_val, pos_del))
245+
207246
old_assigned = self.assigned.copy()
208247
for key, val in old_assigned.items():
209248
self.assigned[key] = np.delete(val, pos_del)
@@ -222,37 +261,37 @@ def append(self, exposures):
222261
self.__dict__ = exposures.__dict__.copy()
223262
return
224263

225-
self.tag.append(exposures.tag)
226264
if self.ref_year != exposures.ref_year:
227265
LOGGER.error("Append not possible. Different reference years.")
228266
raise ValueError
229-
if (self.value_unit == 'NA') and (exposures.value_unit != 'NA'):
267+
if not self.value_unit and exposures.value_unit:
230268
self.value_unit = exposures.value_unit
231269
LOGGER.info("Exposures units set to %s.", self.value_unit)
232-
elif exposures.value_unit == 'NA':
270+
elif not exposures.value_unit:
233271
LOGGER.info("Exposures units set to %s.", self.value_unit)
234272
elif self.value_unit != exposures.value_unit:
235273
LOGGER.error("Append not possible. Different units: %s != %s.", \
236274
self.value_unit, exposures.value_unit)
237275
raise ValueError
276+
self.tag.append(exposures.tag)
277+
278+
# append all 1-dim variables and 2-dim coordinate variable
279+
for (var_name, var_val), haz_val in zip(self.__dict__.items(),
280+
exposures.__dict__.values()):
281+
if isinstance(var_val, np.ndarray) and var_val.ndim == 1 \
282+
and var_val.size:
283+
setattr(self, var_name, np.append(var_val, haz_val). \
284+
astype(var_val.dtype, copy=False))
285+
elif isinstance(var_val, np.ndarray) and var_val.ndim == 2:
286+
setattr(self, var_name, np.append(var_val, haz_val, axis=0). \
287+
astype(var_val.dtype, copy=False))
288+
elif isinstance(var_val, list) and len(var_val):
289+
setattr(self, var_name, var_val + haz_val)
238290

239-
self.coord = np.append(self.coord, exposures.coord, axis=0)
240-
self.value = np.append(self.value, exposures.value)
241-
self.impact_id = np.append(self.impact_id, exposures.impact_id)
242-
self.id = np.append(self.id, exposures.id)
243-
self.deductible = self._append_optional(self.deductible,
244-
exposures.deductible)
245-
self.cover = self._append_optional(self.cover, exposures.cover)
246-
self.category_id = self._append_optional(self.category_id, \
247-
exposures.category_id)
248-
self.region_id = self._append_optional(self.region_id, \
249-
exposures.region_id)
250291
for (ass_haz, ass) in exposures.assigned.items():
251-
if ass_haz not in self.assigned:
252-
self.assigned[ass_haz] = ass
253-
else:
254-
self.assigned[ass_haz] = self._append_optional( \
255-
self.assigned[ass_haz], ass)
292+
if ass_haz in self.assigned:
293+
self.assigned[ass_haz] = np.append(
294+
self.assigned[ass_haz], ass).astype(ass.dtype, copy=False)
256295

257296
# provide new ids to repeated ones
258297
_, indices = np.unique(self.id, return_index=True)
@@ -278,24 +317,16 @@ def select(self, reg_id):
278317
return None
279318

280319
sel_exp = self.__class__()
281-
sel_exp.tag.file_name = self.tag.file_name
282-
sel_exp.tag.description = 'Region ' + str(reg_id)
283-
sel_exp.ref_year = self.ref_year
284-
sel_exp.value_unit = self.value_unit
285-
# Obligatory variables
286-
sel_exp.coord = self.coord[sel_idx, :]
287-
sel_exp.value = self.value[sel_idx]
288-
sel_exp.impact_id = self.impact_id[sel_idx]
289-
sel_exp.id = self.id[sel_idx]
290-
# Optional variables.
291-
if self.deductible.size:
292-
sel_exp.deductible = self.deductible[sel_idx]
293-
if self.cover.size:
294-
sel_exp.cover = self.cover[sel_idx]
295-
if self.category_id.size:
296-
sel_exp.category_id = self.category_id[sel_idx]
297-
if self.region_id.size:
298-
sel_exp.region_id = self.region_id[sel_idx]
320+
for (var_name, var_val) in self.__dict__.items():
321+
if isinstance(var_val, np.ndarray) and var_val.ndim == 1 and \
322+
var_val.size:
323+
setattr(sel_exp, var_name, var_val[sel_idx])
324+
elif isinstance(var_val, np.ndarray) and var_val.ndim == 2:
325+
setattr(sel_exp, var_name, var_val[sel_idx, :])
326+
elif isinstance(var_val, list) and var_val:
327+
setattr(sel_exp, var_name, [var_val[idx] for idx in sel_idx])
328+
else:
329+
setattr(sel_exp, var_name, var_val)
299330

300331
sel_exp.assigned = dict()
301332
for key, value in self.assigned.items():
@@ -359,8 +390,8 @@ def size(self):
359390
self.check()
360391
return self.value.size
361392

362-
@staticmethod
363-
def _read_one(file_name, description='', var_names=None):
393+
@classmethod
394+
def _read_one(cls, file_name, description='', var_names=None):
364395
"""Read one file and fill attributes.
365396
366397
Parameters:
@@ -375,7 +406,7 @@ def _read_one(file_name, description='', var_names=None):
375406
Exposures
376407
"""
377408
LOGGER.info('Reading file: %s', file_name)
378-
new_exp = Exposures()
409+
new_exp = cls()
379410
new_exp.tag = Tag(file_name, description)
380411

381412
extension = os.path.splitext(file_name)[1]
@@ -388,36 +419,6 @@ def _read_one(file_name, description='', var_names=None):
388419

389420
return new_exp
390421

391-
@staticmethod
392-
def _append_optional(ini, to_add):
393-
"""Append variable only if both are filled."""
394-
if (ini.size != 0) and (to_add.size != 0):
395-
ini = np.append(ini, to_add)
396-
else:
397-
ini = np.array([], float)
398-
return ini
399-
400-
def _check_obligatories(self, num_exp):
401-
"""Check coherence obligatory variables."""
402-
check.size(num_exp, self.value, 'Exposures.value')
403-
check.size(num_exp, self.impact_id, 'Exposures.impact_id')
404-
check.shape(num_exp, 2, self.coord, 'Exposures.coord')
405-
406-
def _check_optionals(self, num_exp):
407-
"""Check coherence optional variables. Warn if empty."""
408-
check.array_optional(num_exp, self.category_id, \
409-
'Exposures.category_id')
410-
check.array_optional(num_exp, self.region_id, \
411-
'Exposures.region_id')
412-
check.empty_optional(self.assigned, "Exposures.assigned")
413-
check.array_optional(num_exp, self.deductible, 'Exposures.deductible')
414-
check.array_optional(num_exp, self.cover, 'Exposures.cover')
415-
for (ass_haz, ass) in self.assigned.items():
416-
if ass_haz == 'NA':
417-
LOGGER.warning('Exposures.assigned: assigned hazard type ' \
418-
'not set.')
419-
check.array_optional(num_exp, ass, 'Exposures.assigned')
420-
421422
def __str__(self):
422423
return self.tag.__str__()
423424

climada/entity/exposures/test/test_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_assign_diff(self):
7575
expo_app.assigned['WS'] = np.ones((5, 2))
7676
expo.append(expo_app)
7777
self.assertTrue(len(expo.assigned['TC']), 8)
78-
self.assertTrue(len(expo.assigned['WS']), 5)
78+
self.assertTrue(len(expo.assigned), 1)
7979

8080
def test_append_to_empty_same(self):
8181
"""Append Exposure to empty one."""
@@ -268,7 +268,7 @@ def test_check_wrongCoord_fail(self):
268268
with self.assertLogs('climada.util.checker', level='ERROR') as cm:
269269
with self.assertRaises(ValueError):
270270
expo.check()
271-
self.assertIn('Invalid Exposures.coord row size: 3 != 1.', \
271+
self.assertIn('Invalid Exposures._coord row size: 3 != 1.', \
272272
cm.output[0])
273273

274274
def test_check_wrongDeduct_fail(self):

0 commit comments

Comments
 (0)