Skip to content

Commit 27216c8

Browse files
committed
add excel writers and impact matrix
1 parent 2fe4883 commit 27216c8

2 files changed

Lines changed: 177 additions & 3 deletions

File tree

climada/engine/impact.py

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import csv
2626
from itertools import zip_longest
2727
import numpy as np
28+
from scipy import sparse
2829
import pandas as pd
30+
import xlsxwriter
2931

3032
from climada.entity.tag import Tag
3133
from climada.hazard.tag import Tag as TagHaz
@@ -53,6 +55,8 @@ class Impact():
5355
tot_value (float): total exposure value affected
5456
aai_agg (float): average annual impact (aggregated)
5557
unit (str): value unit used (given by exposures unit)
58+
imp_mat (sparse.csr_matrix): matrix num_events x num_exp with impacts.
59+
only filled if save_mat is True in calc()
5660
"""
5761

5862
def __init__(self):
@@ -68,6 +72,7 @@ def __init__(self):
6872
self.tot_value = 0
6973
self.aai_agg = 0
7074
self.unit = ''
75+
self.imp_mat = []
7176

7277
def calc_freq_curve(self, return_per=None):
7378
"""Compute impact exceedance frequency curve.
@@ -98,13 +103,14 @@ def calc_freq_curve(self, return_per=None):
98103

99104
return ifc
100105

101-
def calc(self, exposures, impact_funcs, hazard):
106+
def calc(self, exposures, impact_funcs, hazard, save_mat=False):
102107
"""Compute impact of an hazard to exposures.
103108
104109
Parameters:
105110
exposures (Exposures): exposures
106111
impact_funcs (ImpactFuncSet): impact functions
107112
hazard (Hazard): hazard
113+
self_mat (bool): self impact matrix: events x exposures
108114
109115
Examples:
110116
Use Entity class:
@@ -168,6 +174,9 @@ def calc(self, exposures, impact_funcs, hazard):
168174
and exposures.cover.max():
169175
insure_flag = True
170176

177+
if save_mat:
178+
self.imp_mat = sparse.lil_matrix((self.date.size, exposures.value.size))
179+
171180
# 3. Loop over exposures according to their impact function
172181
tot_exp = 0
173182
for imp_fun in haz_imp:
@@ -237,6 +246,9 @@ def _exp_impact(self, exp_iimp, exposures, hazard, imp_fun, insure_flag):
237246
imp_fun (ImpactFunc): impact function instance
238247
insure_flag (bool): consider deductible and cover of exposures
239248
"""
249+
if not exp_iimp.size:
250+
return
251+
240252
# get assigned centroids
241253
icens = exposures[INDICATOR_CENTR + hazard.tag.haz_type].values[exp_iimp]
242254

@@ -261,9 +273,11 @@ def _exp_impact(self, exp_iimp, exposures, hazard, imp_fun, insure_flag):
261273

262274
self.at_event += np.squeeze(np.asarray(np.sum(impact, axis=1)))
263275
self.tot_value += np.sum(exposures.value.values[exp_iimp])
276+
if not isinstance(self.imp_mat, list):
277+
self.imp_mat[:, exp_iimp] = impact
264278

265279
def write_csv(self, file_name):
266-
""" Write data into csv file.
280+
""" Write data into csv file. imp_mat is not saved.
267281
268282
Parameters:
269283
file_name (str): absolute path of the file
@@ -285,6 +299,60 @@ def write_csv(self, file_name):
285299
for values in zip_longest(*csv_data):
286300
imp_wr.writerow(values)
287301

302+
def write_excel(self, file_name):
303+
""" Write data into Excel file. imp_mat is not saved.
304+
305+
Parameters:
306+
file_name (str): absolute path of the file
307+
"""
308+
def write_col(i_col, imp_ws, xls_data):
309+
""" Write one measure """
310+
row_ini = 1
311+
for dat_row in xls_data:
312+
imp_ws.write(row_ini, i_col, dat_row)
313+
row_ini += 1
314+
315+
imp_wb = xlsxwriter.Workbook(file_name)
316+
imp_ws = imp_wb.add_worksheet()
317+
318+
header = ["tag_hazard", "tag_exposure", "tag_impact_func",
319+
"unit", "tot_value", "aai_agg", "event_id",
320+
"event_name", "event_date", "event_frequency",
321+
"at_event", "eai_exp", "exp_lat", "exp_lon"]
322+
for icol, head_dat in enumerate(header):
323+
imp_ws.write(0, icol, head_dat)
324+
data = [self.tag['haz'].haz_type, self.tag['haz'].file_name,
325+
self.tag['haz'].description]
326+
write_col(0, imp_ws, data)
327+
data = [self.tag['exp'].file_name, self.tag['exp'].description]
328+
write_col(1, imp_ws, data)
329+
data = [self.tag['if_set'].file_name, self.tag['if_set'].description]
330+
write_col(2, imp_ws, data)
331+
write_col(3, imp_ws, [self.unit])
332+
write_col(4, imp_ws, [self.tot_value])
333+
write_col(5, imp_ws, [self.aai_agg])
334+
write_col(6, imp_ws, self.event_id)
335+
write_col(7, imp_ws, self.event_name)
336+
write_col(8, imp_ws, self.date)
337+
write_col(9, imp_ws, self.frequency)
338+
write_col(10, imp_ws, self.at_event)
339+
write_col(11, imp_ws, self.eai_exp)
340+
write_col(12, imp_ws, self.coord_exp[:, 0])
341+
write_col(13, imp_ws, self.coord_exp[:, 1])
342+
343+
imp_wb.close()
344+
345+
def write_excel_imp_mat(self, file_name):
346+
""" Write imp_mat matrix """
347+
imp_wb = xlsxwriter.Workbook(file_name)
348+
imp_ws = imp_wb.add_worksheet()
349+
350+
imp_ws.write(0, 0, "imp_matrix")
351+
for irow in range(1, self.imp_mat.shape[0]+1):
352+
for icol in range(self.imp_mat.shape[1]):
353+
imp_ws.write(irow, icol, self.imp_mat[irow-1, icol])
354+
imp_wb.close()
355+
288356
def read_csv(self, file_name):
289357
""" Read csv file containing impact data generated by write_csv.
290358
@@ -315,6 +383,40 @@ def read_csv(self, file_name):
315383
self.tag['if_set'] = Tag(str(imp_df.tag_impact_func[0]),
316384
str(imp_df.tag_impact_func[1]))
317385

386+
def read_excel(self, file_name):
387+
""" Read excel file containing impact data generated by write_excel.
388+
389+
Parameters:
390+
file_name (str): absolute path of the file
391+
"""
392+
dfr = pd.read_excel(file_name)
393+
self.__init__()
394+
self.tag['haz'] = TagHaz()
395+
self.tag['haz'].haz_type = dfr['tag_hazard'][0]
396+
self.tag['haz'].file_name = dfr['tag_hazard'][1]
397+
self.tag['haz'].description = dfr['tag_hazard'][2]
398+
self.tag['exp'] = Tag()
399+
self.tag['exp'].file_name = dfr['tag_exposure'][0]
400+
self.tag['exp'].description = dfr['tag_exposure'][1]
401+
self.tag['if_set'] = Tag()
402+
self.tag['if_set'].file_name = dfr['tag_impact_func'][0]
403+
self.tag['if_set'].description = dfr['tag_impact_func'][1]
404+
405+
self.unit = dfr.unit[0]
406+
self.tot_value = dfr.tot_value[0]
407+
self.aai_agg = dfr.aai_agg[0]
408+
409+
self.event_id = dfr.event_id[~np.isnan(dfr.event_id.values)].values
410+
self.event_name = dfr.event_name[:self.event_id.size].values
411+
self.date = dfr.event_date[:self.event_id.size].values
412+
self.frequency = dfr.event_frequency[:self.event_id.size].values
413+
self.at_event = dfr.at_event[:self.event_id.size].values
414+
415+
self.eai_exp = dfr.eai_exp[~np.isnan(dfr.eai_exp.values)].values
416+
self.coord_exp = np.zeros((self.eai_exp.size, 2))
417+
self.coord_exp[:, 0] = dfr.exp_lat.values[:self.eai_exp.size]
418+
self.coord_exp[:, 1] = dfr.exp_lon.values[:self.eai_exp.size]
419+
318420
@property
319421
def coord_exp(self):
320422
""" Return coord"""

climada/engine/test/test_impact.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import os
2222
import unittest
2323
import numpy as np
24+
from scipy import sparse
25+
import pandas as pd
2426

2527
from climada.entity.tag import Tag
2628
from climada.hazard.tag import Tag as TagHaz
@@ -210,6 +212,31 @@ def test_ref_value_pass(self):
210212
self.assertAlmostEqual(6.512201157564421e+09, impact.aai_agg, 5)
211213
self.assertTrue(np.isclose(6.512201157564421e+09, impact.aai_agg))
212214

215+
def test_calc_imp_mat_pass(self):
216+
""" Test save imp_mat """
217+
# Read default entity values
218+
ent = Entity()
219+
ent.read_excel(ENT_DEMO_TODAY)
220+
ent.check()
221+
222+
# Read default hazard file
223+
hazard = Hazard('TC', HAZ_TEST_MAT)
224+
# Create impact object
225+
impact = Impact()
226+
227+
# Assign centroids to exposures
228+
ent.exposures.assign_centroids(hazard)
229+
230+
# Compute the impact over the whole exposures
231+
impact.calc(ent.exposures, ent.impact_funcs, hazard, save_mat=True)
232+
self.assertTrue(isinstance(impact.imp_mat, sparse.lil_matrix))
233+
self.assertEqual(impact.imp_mat.shape, (hazard.event_id.size,
234+
ent.exposures.value.size))
235+
self.assertTrue(np.allclose(np.sum(impact.imp_mat, axis=1).reshape(-1),
236+
impact.at_event))
237+
self.assertTrue(np.allclose(np.array(np.sum(np.multiply(impact.imp_mat.todense(),
238+
impact.frequency.reshape(-1, 1)), axis=0)).reshape(-1), impact.eai_exp))
239+
213240
class TestIO(unittest.TestCase):
214241
''' Test impact calc method.'''
215242

@@ -291,10 +318,55 @@ def test_write_read_exp_test(self):
291318
self.assertEqual(0, len([i for i, j in
292319
zip(imp_write.event_name, imp_read.event_name) if i != j]))
293320

321+
def test_write_read_excel_pass(self):
322+
""" Test write and read in excel """
323+
ent = Entity()
324+
ent.read_excel(ENT_DEMO_TODAY)
325+
ent.check()
326+
327+
hazard = Hazard('TC', HAZ_TEST_MAT)
328+
imp_write = Impact()
329+
ent.exposures.assign_centroids(hazard)
330+
imp_write.calc(ent.exposures, ent.impact_funcs, hazard)
331+
file_name = os.path.dirname(__file__) + 'test.xlsx'
332+
imp_write.write_excel(file_name)
333+
334+
imp_read = Impact()
335+
imp_read.read_excel(file_name)
336+
337+
self.assertTrue(np.array_equal(imp_write.event_id, imp_read.event_id))
338+
self.assertTrue(np.array_equal(imp_write.date, imp_read.date))
339+
self.assertTrue(np.array_equal(imp_write.coord_exp, imp_read.coord_exp))
340+
self.assertTrue(np.allclose(imp_write.eai_exp, imp_read.eai_exp))
341+
self.assertTrue(np.allclose(imp_write.at_event, imp_read.at_event))
342+
self.assertTrue(np.array_equal(imp_write.frequency, imp_read.frequency))
343+
self.assertEqual(imp_write.tot_value, imp_read.tot_value)
344+
self.assertEqual(imp_write.aai_agg, imp_read.aai_agg)
345+
self.assertEqual(imp_write.unit, imp_read.unit)
346+
self.assertEqual(0, len([i for i, j in
347+
zip(imp_write.event_name, imp_read.event_name) if i != j]))
348+
349+
def test_write_imp_mat(self):
350+
""" Test write_excel_imp_mat function """
351+
impact = Impact()
352+
impact.imp_mat = sparse.lil_matrix(np.zeros((5, 4)))
353+
impact.imp_mat[0, :] = np.arange(4)
354+
impact.imp_mat[1, :] = np.arange(4)*2
355+
impact.imp_mat[2, :] = np.arange(4)*3
356+
impact.imp_mat[3, :] = np.arange(4)*4
357+
impact.imp_mat[4, :] = np.arange(4)*5
358+
359+
file_name = os.path.dirname(__file__) + 'test_imp_mat.xlsx'
360+
impact.write_excel_imp_mat(file_name)
361+
dfr = pd.read_excel(file_name)
362+
for irow in range(5):
363+
self.assertTrue(np.array_equal(dfr.iloc[irow].values,
364+
np.array(impact.imp_mat[irow, :].todense()).reshape(-1)))
365+
366+
294367
# Execute Tests
295368
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestOneExposure)
296369
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCalc))
297370
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFreqCurve))
298371
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestIO))
299372
unittest.TextTestRunner(verbosity=2).run(TESTS)
300-

0 commit comments

Comments
 (0)