def test_append_equal_same(self):
        """Appends an other tag correctly."""
        tag1 = Tag('file_name1.mat', 'dummy file 1')
        tag2 = Tag('file_name1.mat', 'dummy file 1')

        tag1.append(tag2)
        self.assertEqual('file_name1.mat', tag1.file_name)
        self.assertEqual('dummy file 1', tag1.description)
    def test_append_different_increase(self):
        """Appends an other tag correctly."""
        tag1 = Tag('file_name1.mat', 'dummy file 1')
        self.assertEqual('file_name1.mat', tag1.file_name)
        self.assertEqual('dummy file 1', tag1.description)

        tag2 = Tag('file_name2.mat', 'dummy file 2')

        tag1.append(tag2)

        self.assertEqual('file_name1.mat + file_name2.mat', tag1.file_name)
        self.assertEqual('dummy file 1 + dummy file 2', tag1.description)
Exemple #3
0
class MeasureSet():
    """Contains measures of type Measure. Loads from
    files with format defined in FILE_EXT.

    Attributes:
        tag (Tag): information about the source data
        _data (dict): cotains Measure classes. It's not suppossed to be
            directly accessed. Use the class methods instead.
    """
    def __init__(self):
        """Empty initialization.

        Examples:
            Fill MeasureSet with values and check consistency data:

            >>> act_1 = Measure()
            >>> act_1.name = 'Seawall'
            >>> act_1.color_rgb = np.array([0.1529, 0.2510, 0.5451])
            >>> act_1.hazard_intensity = (1, 0)
            >>> act_1.mdd_impact = (1, 0)
            >>> act_1.paa_impact = (1, 0)
            >>> meas = MeasureSet()
            >>> meas.append(act_1)
            >>> meas.tag.description = "my dummy MeasureSet."
            >>> meas.check()

            Read measures from file and checks consistency data:

            >>> meas = MeasureSet()
            >>> meas.read_excel(ENT_TEMPLATE_XLS)
        """
        self.clear()

    def clear(self):
        """Reinitialize attributes."""
        self.tag = Tag()
        self._data = dict()  # {hazard_type : {name: Measure()}}

    def append(self, meas):
        """Append an Measure. Override if same name and haz_type.

        Parameters:
            meas (Measure): Measure instance

        Raises:
            ValueError
        """
        if not isinstance(meas, Measure):
            LOGGER.error("Input value is not of type Measure.")
            raise ValueError
        if not meas.haz_type:
            LOGGER.warning("Input Measure's hazard type not set.")
        if not meas.name:
            LOGGER.warning("Input Measure's name not set.")
        if meas.haz_type not in self._data:
            self._data[meas.haz_type] = dict()
        self._data[meas.haz_type][meas.name] = meas

    def remove_measure(self, haz_type=None, name=None):
        """Remove impact function(s) with provided hazard type and/or id.
        If no input provided, all impact functions are removed.

        Parameters:
            haz_type (str, optional): all impact functions with this hazard
            name (str, optional): measure name
        """
        if (haz_type is not None) and (name is not None):
            try:
                del self._data[haz_type][name]
            except KeyError:
                LOGGER.info("No Measure with hazard %s and id %s.", haz_type,
                            name)
        elif haz_type is not None:
            try:
                del self._data[haz_type]
            except KeyError:
                LOGGER.info("No Measure with hazard %s.", haz_type)
        elif name is not None:
            haz_remove = self.get_hazard_types(name)
            if not haz_remove:
                LOGGER.info("No Measure with name %s.", name)
            for haz in haz_remove:
                del self._data[haz][name]
        else:
            self._data = dict()

    def get_measure(self, haz_type=None, name=None):
        """Get ImpactFunc(s) of input hazard type and/or id.
        If no input provided, all impact functions are returned.

        Parameters:
            haz_type (str, optional): hazard type
            name (str, optional): measure name

        Returns:
            Measure (if haz_type and name),
            list(Measure) (if haz_type or name),
            {Measure.haz_type: {Measure.name : Measure}} (if None)
        """
        if (haz_type is not None) and (name is not None):
            try:
                return self._data[haz_type][name]
            except KeyError:
                LOGGER.info("No Measure with hazard %s and id %s.", haz_type,
                            name)
                return list()
        elif haz_type is not None:
            try:
                return list(self._data[haz_type].values())
            except KeyError:
                LOGGER.info("No Measure with hazard %s.", haz_type)
                return list()
        elif name is not None:
            haz_return = self.get_hazard_types(name)
            if not haz_return:
                LOGGER.info("No Measure with name %s.", name)
            meas_return = []
            for haz in haz_return:
                meas_return.append(self._data[haz][name])
            return meas_return
        else:
            return self._data

    def get_hazard_types(self, meas=None):
        """Get measures hazard types contained for the name provided.
        Return all hazard types if no input name.

        Parameters:
            name (str, optional): measure name

        Returns:
            list(str)
        """
        if meas is None:
            return list(self._data.keys())

        haz_return = []
        for haz, haz_dict in self._data.items():
            if meas in haz_dict:
                haz_return.append(haz)
        return haz_return

    def get_names(self, haz_type=None):
        """Get measures names contained for the hazard type provided.
        Return all names for each hazard type if no input hazard type.

        Parameters:
            haz_type (str, optional): hazard type from which to obtain the names

        Returns:
            list(Measure.name) (if haz_type provided),
            {Measure.haz_type : list(Measure.name)} (if no haz_type)
        """
        if haz_type is None:
            out_dict = dict()
            for haz, haz_dict in self._data.items():
                out_dict[haz] = list(haz_dict.keys())
            return out_dict

        try:
            return list(self._data[haz_type].keys())
        except KeyError:
            LOGGER.info("No Measure with hazard %s.", haz_type)
            return list()

    def size(self, haz_type=None, name=None):
        """Get number of measures contained with input hazard type and
        /or id. If no input provided, get total number of impact functions.

        Parameters:
            haz_type (str, optional): hazard type
            name (str, optional): measure name

        Returns:
            int
        """
        if (haz_type is not None) and (name is not None) and \
        (isinstance(self.get_measure(haz_type, name), Measure)):
            return 1
        if (haz_type is not None) or (name is not None):
            return len(self.get_measure(haz_type, name))
        return sum(len(meas_list) for meas_list in self.get_names().values())

    def check(self):
        """Check instance attributes.

        Raises:
            ValueError
        """
        for key_haz, meas_dict in self._data.items():
            def_color = plt.cm.get_cmap('Greys', len(meas_dict))
            for i_meas, (name, meas) in enumerate(meas_dict.items()):
                if (name != meas.name) | (name == ''):
                    LOGGER.error("Wrong Measure.name: %s != %s.", name,
                                 meas.name)
                    raise ValueError
                if key_haz != meas.haz_type:
                    LOGGER.error("Wrong Measure.haz_type: %s != %s.", key_haz,
                                 meas.haz_type)
                    raise ValueError
                # set default color if not set
                if np.array_equal(meas.color_rgb, np.zeros(3)):
                    meas.color_rgb = def_color(i_meas)
                meas.check()

    def extend(self, meas_set):
        """Extend measures of input MeasureSet to current
        MeasureSet. Overwrite Measure if same name and haz_type.

        Parameters:
            impact_funcs (MeasureSet): ImpactFuncSet instance to extend

        Raises:
            ValueError
        """
        meas_set.check()
        if self.size() == 0:
            self.__dict__ = copy.deepcopy(meas_set.__dict__)
            return

        self.tag.append(meas_set.tag)

        new_func = meas_set.get_measure()
        for _, meas_dict in new_func.items():
            for _, meas in meas_dict.items():
                self.append(meas)

    def read_mat(self, file_name, description='', var_names=DEF_VAR_MAT):
        """Read MATLAB file generated with previous MATLAB CLIMADA version.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        def read_att_mat(measures, data, file_name, var_names):
            """Read MATLAB measures attributes"""
            num_mes = len(data[var_names['var_name']['name']])
            for idx in range(0, num_mes):
                meas = Measure()

                meas.name = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['name']][idx][0])

                color_str = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['color']][idx][0])
                meas.color_rgb = np.fromstring(color_str, dtype=float, sep=' ')
                meas.cost = data[var_names['var_name']['cost']][idx][0]
                meas.haz_type = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['haz']][idx][0])
                meas.hazard_freq_cutoff = data[var_names['var_name']
                                               ['haz_frq']][idx][0]
                meas.hazard_set = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['haz_set']][idx][0])
                try:
                    meas.hazard_inten_imp = (
                        data[var_names['var_name']['haz_int_a']][idx][0],
                        data[var_names['var_name']['haz_int_b']][0][idx])
                except KeyError:
                    meas.hazard_inten_imp = (
                        data[var_names['var_name']['haz_int_a'][:-2]][idx][0],
                        0)

                # different convention of signes followed in MATLAB!
                meas.mdd_impact = (
                    data[var_names['var_name']['mdd_a']][idx][0],
                    data[var_names['var_name']['mdd_b']][idx][0])
                meas.paa_impact = (
                    data[var_names['var_name']['paa_a']][idx][0],
                    data[var_names['var_name']['paa_b']][idx][0])
                meas.imp_fun_map = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['fun_map']][idx][0])

                meas.exposures_set = hdf5.get_str_from_ref(
                    file_name, data[var_names['var_name']['exp_set']][idx][0])
                exp_region_id = data[var_names['var_name']['exp_reg']][idx][0]
                if exp_region_id:
                    meas.exp_region_id = [exp_region_id]
                meas.risk_transf_attach = data[var_names['var_name']
                                               ['risk_att']][idx][0]
                meas.risk_transf_cover = data[var_names['var_name']
                                              ['risk_cov']][idx][0]

                measures.append(meas)

        data = hdf5.read(file_name)
        self.clear()
        self.tag.file_name = file_name
        self.tag.description = description
        try:
            data = data[var_names['sup_field_name']]
        except KeyError:
            pass

        try:
            data = data[var_names['field_name']]
            read_att_mat(self, data, file_name, var_names)
        except KeyError as var_err:
            LOGGER.error("Not existing variable %s", str(var_err))
            raise var_err

    def read_excel(self, file_name, description='', var_names=DEF_VAR_EXCEL):
        """Read excel file following template and store variables.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        def read_att_excel(measures, dfr, var_names):
            """Read Excel measures attributes"""
            num_mes = len(dfr.index)
            for idx in range(0, num_mes):
                meas = Measure()

                meas.name = dfr[var_names['col_name']['name']][idx]
                try:
                    meas.haz_type = dfr[var_names['col_name']['haz']][idx]
                except KeyError:
                    pass
                meas.color_rgb = np.fromstring(
                    dfr[var_names['col_name']['color']][idx],
                    dtype=float,
                    sep=' ')
                meas.cost = dfr[var_names['col_name']['cost']][idx]

                meas.hazard_freq_cutoff = dfr[var_names['col_name']
                                              ['haz_frq']][idx]
                meas.hazard_set = dfr[var_names['col_name']['haz_set']][idx]
                # Search for (a, b) values, put a = 1 otherwise
                try:
                    meas.hazard_inten_imp = (
                        dfr[var_names['col_name']['haz_int_a']][idx],
                        dfr[var_names['col_name']['haz_int_b']][idx])
                except KeyError:
                    meas.hazard_inten_imp = (
                        1, dfr['hazard intensity impact'][idx])

                try:
                    meas.exposures_set = dfr[var_names['col_name']
                                             ['exp_set']][idx]
                    meas.exp_region_id = ast.literal_eval(
                        dfr[var_names['col_name']['exp_reg']][idx])
                except KeyError:
                    pass
                except ValueError:
                    meas.exp_region_id = dfr[var_names['col_name']
                                             ['exp_reg']][idx]

                meas.mdd_impact = (dfr[var_names['col_name']['mdd_a']][idx],
                                   dfr[var_names['col_name']['mdd_b']][idx])
                meas.paa_impact = (dfr[var_names['col_name']['paa_a']][idx],
                                   dfr[var_names['col_name']['paa_b']][idx])
                meas.imp_fun_map = dfr[var_names['col_name']['fun_map']][idx]
                meas.risk_transf_attach = dfr[var_names['col_name']
                                              ['risk_att']][idx]
                meas.risk_transf_cover = dfr[var_names['col_name']
                                             ['risk_cov']][idx]
                try:
                    meas.risk_transf_cost_factor = dfr[var_names['col_name']
                                                       ['risk_fact']][idx]
                except KeyError:
                    pass

                measures.append(meas)

        dfr = pd.read_excel(file_name, var_names['sheet_name'])
        dfr = dfr.fillna('')
        self.clear()
        self.tag.file_name = file_name
        self.tag.description = description
        try:
            read_att_excel(self, dfr, var_names)
        except KeyError as var_err:
            LOGGER.error("Not existing variable: %s", str(var_err))
            raise var_err

    def write_excel(self, file_name, var_names=DEF_VAR_EXCEL):
        """Write excel file following template.

        Parameters:
            file_name (str): absolute file name to write
            var_names (dict, optional): name of the variables in the file
        """
        def write_meas(row_ini, imp_ws, xls_data):
            """Write one measure"""
            for icol, col_dat in enumerate(xls_data):
                imp_ws.write(row_ini, icol, col_dat)

        meas_wb = xlsxwriter.Workbook(file_name)
        mead_ws = meas_wb.add_worksheet(var_names['sheet_name'])

        header = [
            var_names['col_name']['name'], var_names['col_name']['color'],
            var_names['col_name']['cost'], var_names['col_name']['haz_int_a'],
            var_names['col_name']['haz_int_b'],
            var_names['col_name']['haz_frq'], var_names['col_name']['haz_set'],
            var_names['col_name']['mdd_a'], var_names['col_name']['mdd_b'],
            var_names['col_name']['paa_a'], var_names['col_name']['paa_b'],
            var_names['col_name']['fun_map'], var_names['col_name']['exp_set'],
            var_names['col_name']['exp_reg'],
            var_names['col_name']['risk_att'],
            var_names['col_name']['risk_cov'], var_names['col_name']['haz']
        ]
        for icol, head_dat in enumerate(header):
            mead_ws.write(0, icol, head_dat)
        for row_ini, (_, haz_dict) in enumerate(self._data.items(), 1):
            for meas_name, meas in haz_dict.items():
                xls_data = [
                    meas_name, ' '.join(list(map(str,
                                                 meas.color_rgb))), meas.cost,
                    meas.hazard_inten_imp[0], meas.hazard_inten_imp[1],
                    meas.hazard_freq_cutoff, meas.hazard_set,
                    meas.mdd_impact[0], meas.mdd_impact[1], meas.paa_impact[0],
                    meas.paa_impact[1], meas.imp_fun_map, meas.exposures_set,
                    str(meas.exp_region_id), meas.risk_transf_attach,
                    meas.risk_transf_cover, meas.haz_type
                ]
            write_meas(row_ini, mead_ws, xls_data)
        meas_wb.close()
Exemple #4
0
class ImpactFuncSet():
    """Contains impact functions of type ImpactFunc. Loads from
    files with format defined in FILE_EXT.

    Attributes:
        tag (Tag): information about the source data
        _data (dict): contains ImpactFunc classes. It's not suppossed to be
            directly accessed. Use the class methods instead.
    """

    def __init__(self):
        """Empty initialization.

        Examples:
            Fill impact functions with values and check consistency data:

            >>> fun_1 = ImpactFunc()
            >>> fun_1.haz_type = 'TC'
            >>> fun_1.id = 3
            >>> fun_1.intensity = np.array([0, 20])
            >>> fun_1.paa = np.array([0, 1])
            >>> fun_1.mdd = np.array([0, 0.5])
            >>> imp_fun = ImpactFuncSet()
            >>> imp_fun.append(fun_1)
            >>> imp_fun.check()

            Read impact functions from file and checks consistency data.

            >>> imp_fun = ImpactFuncSet()
            >>> imp_fun.read(ENT_TEMPLATE_XLS)
        """
        self.clear()

    def clear(self):
        """Reinitialize attributes."""
        self.tag = Tag()
        self._data = dict()  # {hazard_type : {id:ImpactFunc}}

    def append(self, func):
        """Append a ImpactFunc. Overwrite existing if same id and haz_type.

        Parameters:
            func (ImpactFunc): ImpactFunc instance

        Raises:
            ValueError
        """
        if not isinstance(func, ImpactFunc):
            LOGGER.error("Input value is not of type ImpactFunc.")
            raise ValueError
        if not func.haz_type:
            LOGGER.warning("Input ImpactFunc's hazard type not set.")
        if not func.id:
            LOGGER.warning("Input ImpactFunc's id not set.")
        if func.haz_type not in self._data:
            self._data[func.haz_type] = dict()
        self._data[func.haz_type][func.id] = func

    def remove_func(self, haz_type=None, fun_id=None):
        """Remove impact function(s) with provided hazard type and/or id.
        If no input provided, all impact functions are removed.

        Parameters:
            haz_type (str, optional): all impact functions with this hazard
            fun_id (int, optional): all impact functions with this id
        """
        if (haz_type is not None) and (fun_id is not None):
            try:
                del self._data[haz_type][fun_id]
            except KeyError:
                LOGGER.warning("No ImpactFunc with hazard %s and id %s.",
                               haz_type, fun_id)
        elif haz_type is not None:
            try:
                del self._data[haz_type]
            except KeyError:
                LOGGER.warning("No ImpactFunc with hazard %s.", haz_type)
        elif fun_id is not None:
            haz_remove = self.get_hazard_types(fun_id)
            if not haz_remove:
                LOGGER.warning("No ImpactFunc with id %s.", fun_id)
            for vul_haz in haz_remove:
                del self._data[vul_haz][fun_id]
        else:
            self._data = dict()

    def get_func(self, haz_type=None, fun_id=None):
        """Get ImpactFunc(s) of input hazard type and/or id.
        If no input provided, all impact functions are returned.

        Parameters:
            haz_type (str, optional): hazard type
            fun_id (int, optional): ImpactFunc id

        Returns:
            ImpactFunc (if haz_type and fun_id),
            list(ImpactFunc) (if haz_type or fun_id),
            {ImpactFunc.haz_type: {ImpactFunc.id : ImpactFunc}} (if None)
        """
        if (haz_type is not None) and (fun_id is not None):
            try:
                return self._data[haz_type][fun_id]
            except KeyError:
                return list()
        elif haz_type is not None:
            try:
                return list(self._data[haz_type].values())
            except KeyError:
                return list()
        elif fun_id is not None:
            haz_return = self.get_hazard_types(fun_id)
            vul_return = []
            for vul_haz in haz_return:
                vul_return.append(self._data[vul_haz][fun_id])
            return vul_return
        else:
            return self._data

    def get_hazard_types(self, fun_id=None):
        """Get impact functions hazard types contained for the id provided.
        Return all hazard types if no input id.

        Parameters:
            fun_id (int, optional): id of an impact function

        Returns:
            list(str)
        """
        if fun_id is None:
            return list(self._data.keys())

        haz_types = []
        for vul_haz, vul_dict in self._data.items():
            if fun_id in vul_dict:
                haz_types.append(vul_haz)
        return haz_types

    def get_ids(self, haz_type=None):
        """Get impact functions ids contained for the hazard type provided.
        Return all ids for each hazard type if no input hazard type.

        Parameters:
            haz_type (str, optional): hazard type from which to obtain the ids

        Returns:
            list(ImpactFunc.id) (if haz_type provided),
            {ImpactFunc.haz_type : list(ImpactFunc.id)} (if no haz_type)
        """
        if haz_type is None:
            out_dict = dict()
            for vul_haz, vul_dict in self._data.items():
                out_dict[vul_haz] = list(vul_dict.keys())
            return out_dict

        try:
            return list(self._data[haz_type].keys())
        except KeyError:
            return list()

    def size(self, haz_type=None, fun_id=None):
        """Get number of impact functions contained with input hazard type and
        /or id. If no input provided, get total number of impact functions.

        Parameters:
            haz_type (str, optional): hazard type
            fun_id (int, optional): ImpactFunc id

        Returns:
            int
        """
        if (haz_type is not None) and (fun_id is not None) and \
        (isinstance(self.get_func(haz_type, fun_id), ImpactFunc)):
            return 1
        if (haz_type is not None) or (fun_id is not None):
            return len(self.get_func(haz_type, fun_id))
        return sum(len(vul_list) for vul_list in self.get_ids().values())

    def check(self):
        """Check instance attributes.

        Raises:
            ValueError
        """
        for key_haz, vul_dict in self._data.items():
            for fun_id, vul in vul_dict.items():
                if (fun_id != vul.id) | (fun_id == ''):
                    LOGGER.error("Wrong ImpactFunc.id: %s != %s.", fun_id,
                                 vul.id)
                    raise ValueError
                if (key_haz != vul.haz_type) | (key_haz == ''):
                    LOGGER.error("Wrong ImpactFunc.haz_type: %s != %s.",
                                 key_haz, vul.haz_type)
                    raise ValueError
                vul.check()

    def extend(self, impact_funcs):
        """Append impact functions of input ImpactFuncSet to current
        ImpactFuncSet. Overwrite ImpactFunc if same id and haz_type.

        Parameters:
            impact_funcs (ImpactFuncSet): ImpactFuncSet instance to extend

        Raises:
            ValueError
        """
        impact_funcs.check()
        if self.size() == 0:
            self.__dict__ = copy.deepcopy(impact_funcs.__dict__)
            return

        self.tag.append(impact_funcs.tag)

        new_func = impact_funcs.get_func()
        for _, vul_dict in new_func.items():
            for _, vul in vul_dict.items():
                self.append(vul)

    def plot(self, haz_type=None, fun_id=None, axis=None, **kwargs):
        """Plot impact functions of selected hazard (all if not provided) and
        selected function id (all if not provided).

        Parameters:
            haz_type (str, optional): hazard type
            fun_id (int, optional): id of the function

        Returns:
            matplotlib.axes._subplots.AxesSubplot
        """
        num_plts = self.size(haz_type, fun_id)
        num_row, num_col = u_plot._get_row_col_size(num_plts)
        # Select all hazard types to plot
        if haz_type is not None:
            hazards = [haz_type]
        else:
            hazards = self._data.keys()

        if not axis:
            _, axis = plt.subplots(num_row, num_col)
        if num_plts > 1:
            axes = axis.flatten()
        else:
            axes = [axis]

        i_axis = 0
        for sel_haz in hazards:
            if fun_id is not None:
                self._data[sel_haz][fun_id].plot(axis=axes[i_axis], **kwargs)
                i_axis += 1
            else:
                for sel_id in self._data[sel_haz].keys():
                    self._data[sel_haz][sel_id].plot(axis=axes[i_axis], **kwargs)
                    i_axis += 1
        return axis

    def read_excel(self, file_name, description='', var_names=DEF_VAR_EXCEL):
        """Read excel file following template and store variables.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        dfr = pd.read_excel(file_name, var_names['sheet_name'])

        self.clear()
        self.tag.file_name = str(file_name)
        self.tag.description = description
        self._fill_dfr(dfr, var_names)

    def read_mat(self, file_name, description='', var_names=DEF_VAR_MAT):
        """Read MATLAB file generated with previous MATLAB CLIMADA version.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        def _get_hdf5_funcs(imp, file_name, var_names):
            """Get rows that fill every impact function and its name."""
            func_pos = dict()
            for row, (fun_id, fun_type) in enumerate(
                    zip(imp[var_names['var_name']['fun_id']].squeeze(),
                        imp[var_names['var_name']['peril']].squeeze())):
                type_str = u_hdf5.get_str_from_ref(file_name, fun_type)
                key = (type_str, int(fun_id))
                if key not in func_pos:
                    func_pos[key] = list()
                func_pos[key].append(row)
            return func_pos

        def _get_hdf5_str(imp, idxs, file_name, var_name):
            """Get rows with same string in var_name."""
            prev_str = ""
            for row in idxs:
                cur_str = u_hdf5.get_str_from_ref(file_name, imp[var_name][row][0])
                if prev_str == "":
                    prev_str = cur_str
                elif prev_str != cur_str:
                    LOGGER.error("Impact function with two different %s.", var_name)
                    raise ValueError
            return prev_str

        imp = u_hdf5.read(file_name)
        self.clear()
        self.tag.file_name = str(file_name)
        self.tag.description = description

        try:
            imp = imp[var_names['sup_field_name']]
        except KeyError:
            pass
        try:
            imp = imp[var_names['field_name']]
            funcs_idx = _get_hdf5_funcs(imp, file_name, var_names)
            for imp_key, imp_rows in funcs_idx.items():
                func = ImpactFunc()
                func.haz_type = imp_key[0]
                func.id = imp_key[1]
                # check that this function only has one intensity unit, if provided
                try:
                    func.intensity_unit = _get_hdf5_str(imp, imp_rows,
                                                        file_name,
                                                        var_names['var_name']['unit'])
                except KeyError:
                    pass
                # check that this function only has one name
                try:
                    func.name = _get_hdf5_str(imp, imp_rows, file_name,
                                              var_names['var_name']['name'])
                except KeyError:
                    func.name = str(func.id)
                func.intensity = np.take(imp[var_names['var_name']['inten']], imp_rows)
                func.mdd = np.take(imp[var_names['var_name']['mdd']], imp_rows)
                func.paa = np.take(imp[var_names['var_name']['paa']], imp_rows)
                self.append(func)
        except KeyError as err:
            LOGGER.error("Not existing variable: %s", str(err))
            raise err

    def write_excel(self, file_name, var_names=DEF_VAR_EXCEL):
        """Write excel file following template.

        Parameters:
            file_name (str): absolute file name to write
            var_names (dict, optional): name of the variables in the file
        """
        def write_if(row_ini, imp_ws, xls_data):
            """Write one impact function"""
            for icol, col_dat in enumerate(xls_data):
                for irow, data in enumerate(col_dat, row_ini):
                    imp_ws.write(irow, icol, data)

        imp_wb = xlsxwriter.Workbook(file_name)
        imp_ws = imp_wb.add_worksheet(var_names['sheet_name'])

        header = [var_names['col_name']['func_id'], var_names['col_name']['inten'],
                  var_names['col_name']['mdd'], var_names['col_name']['paa'],
                  var_names['col_name']['peril'], var_names['col_name']['unit'],
                  var_names['col_name']['name']]
        for icol, head_dat in enumerate(header):
            imp_ws.write(0, icol, head_dat)
        row_ini = 1
        for fun_haz_id, fun_haz in self._data.items():
            for fun_id, fun in fun_haz.items():
                n_inten = fun.intensity.size
                xls_data = [repeat(fun_id, n_inten), fun.intensity, fun.mdd,
                            fun.paa, repeat(fun_haz_id, n_inten),
                            repeat(fun.intensity_unit, n_inten),
                            repeat(fun.name, n_inten)]
                write_if(row_ini, imp_ws, xls_data)
                row_ini += n_inten
        imp_wb.close()

    def _fill_dfr(self, dfr, var_names):

        def _get_xls_funcs(dfr, var_names):
            """Parse individual impact functions."""
            dist_func = []
            for (haz_type, imp_id) in zip(dfr[var_names['col_name']['peril']],
                                          dfr[var_names['col_name']['func_id']]):
                if (haz_type, imp_id) not in dist_func:
                    dist_func.append((haz_type, imp_id))
            return dist_func

        try:
            dist_func = _get_xls_funcs(dfr, var_names)
            for haz_type, imp_id in dist_func:
                df_func = dfr[dfr[var_names['col_name']['peril']] == haz_type]
                df_func = df_func[df_func[var_names['col_name']['func_id']]
                                  == imp_id]

                func = ImpactFunc()
                func.haz_type = haz_type
                func.id = imp_id
                # check that the unit of the intensity is the same
                try:
                    if len(df_func[var_names['col_name']['name']].unique()) != 1:
                        raise ValueError('Impact function with two different names.')
                    func.name = df_func[var_names['col_name']['name']].values[0]
                except KeyError:
                    func.name = str(func.id)

                # check that the unit of the intensity is the same, if provided
                try:
                    if len(df_func[var_names['col_name']['unit']].unique()) != 1:
                        raise ValueError('Impact function with two different \
                                         intensity units.')
                    func.intensity_unit = \
                                    df_func[var_names['col_name']['unit']].values[0]
                except KeyError:
                    pass

                func.intensity = df_func[var_names['col_name']['inten']].values
                func.mdd = df_func[var_names['col_name']['mdd']].values
                func.paa = df_func[var_names['col_name']['paa']].values

                self.append(func)

        except KeyError as err:
            LOGGER.error("Not existing variable: %s", str(err))
            raise err
Exemple #5
0
class DiscRates():
    """Defines discount rates and basic methods. Loads from
    files with format defined in FILE_EXT.

    Attributes:
        tag (Tag): information about the source data
        years (np.array): years
        rates (np.array): discount rates for each year (between 0 and 1)
    """
    def __init__(self):
        """Empty initialization.

        Examples:
            Fill discount rates with values and check consistency data:

            >>> disc_rates = DiscRates()
            >>> disc_rates.years = np.array([2000, 2001])
            >>> disc_rates.rates = np.array([0.02, 0.02])
            >>> disc_rates.check()

            Read discount rates from year_2050.mat and checks consistency data.

            >>> disc_rates = DiscRates(ENT_TEMPLATE_XLS)
        """
        self.clear()

    def clear(self):
        """Reinitialize attributes."""
        self.tag = Tag()
        # Following values are given for each defined year
        self.years = np.array([], int)
        self.rates = np.array([], float)

    def check(self):
        """Check attributes consistency.

        Raises:
            ValueError
        """
        check.size(len(self.years), self.rates, 'DiscRates.rates')

    def select(self, year_range):
        """Select discount rates in given years.

        Parameters:
            year_range (np.array): continuous sequence of selected years.

        Returns:
            DiscRates
        """
        pos_year = np.isin(year_range, self.years)
        if not np.all(pos_year):
            LOGGER.info('No discount rates for given years.')
            return None
        pos_year = np.isin(self.years, year_range)
        sel_disc = self.__class__()
        sel_disc.tag = self.tag
        sel_disc.years = self.years[pos_year]
        sel_disc.rates = self.rates[pos_year]

        return sel_disc

    def append(self, disc_rates):
        """Check and append discount rates to current DiscRates. Overwrite
        discount rate if same year.

        Parameters:
            disc_rates (DiscRates): DiscRates instance to append

        Raises:
            ValueError
        """
        disc_rates.check()
        if self.years.size == 0:
            self.__dict__ = copy.deepcopy(disc_rates.__dict__)
            return

        self.tag.append(disc_rates.tag)

        new_year = array('l')
        new_rate = array('d')
        for year, rate in zip(disc_rates.years, disc_rates.rates):
            found = np.where(year == self.years)[0]
            if found.size > 0:
                self.rates[found[0]] = rate
            else:
                new_year.append(year)
                new_rate.append(rate)

        self.years = np.append(self.years, new_year).astype(int, copy=False)
        self.rates = np.append(self.rates, new_rate)

    def net_present_value(self, ini_year, end_year, val_years):
        """Compute net present value between present year and future year.

        Parameters:
            ini_year (float): initial year
            end_year (float): end year
            val_years (np.array): cash flow at each year btw ini_year and
                end_year (both included)
        Returns:
            float
        """
        year_range = np.arange(ini_year, end_year + 1)
        if year_range.size != val_years.size:
            LOGGER.error('Wrong size of yearly values.')
            raise ValueError
        sel_disc = self.select(year_range)
        if sel_disc is None:
            LOGGER.error('No information of discount rates for provided years:'\
                        ' %s - %s', ini_year, end_year)
            raise ValueError
        return u_fin.net_present_value(sel_disc.years, sel_disc.rates,
                                       val_years)

    def plot(self, axis=None, **kwargs):
        """Plot discount rates per year.

        Parameters:
            axis (matplotlib.axes._subplots.AxesSubplot, optional): axis to use
            kwargs (optional): arguments for plot matplotlib function, e.g. marker='x'

        Returns:
            matplotlib.axes._subplots.AxesSubplot
        """
        if not axis:
            _, axis = plt.subplots(1, 1)

        axis.set_title('Discount rates')
        axis.set_xlabel('Year')
        axis.set_ylabel('discount rate (%)')
        axis.plot(self.years, self.rates * 100, **kwargs)
        axis.set_xlim((self.years.min(), self.years.max()))
        return axis

    def read_mat(self, file_name, description='', var_names=DEF_VAR_MAT):
        """Read MATLAB file generated with previous MATLAB CLIMADA version.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        disc = hdf5.read(file_name)
        self.clear()
        self.tag.file_name = file_name
        self.tag.description = description
        try:
            disc = disc[var_names['sup_field_name']]
        except KeyError:
            pass

        try:
            disc = disc[var_names['field_name']]
            self.years = np.squeeze(disc[var_names['var_name']['year']]). \
                astype(int, copy=False)
            self.rates = np.squeeze(disc[var_names['var_name']['disc']])
        except KeyError as err:
            LOGGER.error("Not existing variable: %s", str(err))
            raise err

    def read_excel(self, file_name, description='', var_names=DEF_VAR_EXCEL):
        """Read excel file following template and store variables.

        Parameters:
            file_name (str): absolute file name
            description (str, optional): description of the data
            var_names (dict, optional): name of the variables in the file
        """
        dfr = pd.read_excel(file_name, var_names['sheet_name'])
        self.clear()
        self.tag.file_name = file_name
        self.tag.description = description
        try:
            self.years = dfr[var_names['col_name']['year']].values. \
                astype(int, copy=False)
            self.rates = dfr[var_names['col_name']['disc']].values
        except KeyError as err:
            LOGGER.error("Not existing variable: %s", str(err))
            raise err

    def write_excel(self, file_name, var_names=DEF_VAR_EXCEL):
        """ Write excel file following template.

        Parameters:
            file_name (str): absolute file name to write
            var_names (dict, optional): name of the variables in the file
        """
        disc_wb = xlsxwriter.Workbook(file_name)
        disc_ws = disc_wb.add_worksheet(var_names['sheet_name'])

        header = [var_names['col_name']['year'], var_names['col_name']['disc']]
        for icol, head_dat in enumerate(header):
            disc_ws.write(0, icol, head_dat)
        for i_yr, (disc_yr, disc_rt) in enumerate(zip(self.years, self.rates),
                                                  1):
            disc_ws.write(i_yr, 0, disc_yr)
            disc_ws.write(i_yr, 1, disc_rt)
        disc_wb.close()
class DiscRates():
    """
    Defines discount rates and basic methods. Loads from
    files with format defined in FILE_EXT.

    Attributes
    ---------
    tag: Tag
        information about the source data
    years: np.array
        list of years
    rates: np.array
        list of discount rates for each year (between 0 and 1)
    """
    def __init__(self, years=None, rates=None, tag=None):
        """
        Fill discount rates with values and check consistency data

        Parameters
        ----------
        years : numpy.ndarray(int)
            Array of years. Default is numpy.array([]).
        rates : numpy.ndarray(float)
            Discount rates for each year in years.
            Default is numpy.array([]).
            Note: rates given in float, e.g., to set 1% rate use 0.01
        tag : climate.entity.tag
            Metadata. Default is None.
        """
        years = np.array([]) if years is None else years
        rates = np.array([]) if rates is None else rates

        self.years = years
        self.rates = rates
        tag = Tag() if tag is None else tag
        self.tag = tag

    def clear(self):
        """Reinitialize attributes."""

        self.tag = Tag()
        # Following values are given for each defined year
        self.years = np.array([], int)
        self.rates = np.array([], float)

    def check(self):
        """
        Check attributes consistency.

        Raises
        ------
        ValueError
        """
        u_check.size(len(self.years), self.rates, 'DiscRates.rates')

    def select(self, year_range):
        """
        Select discount rates in given years.

        Parameters
        ----------
        year_range: np.array(int)
            continuous sequence of selected years.

        Returns: climada.entity.DiscRates
            The selected discrates in the year_range
        """
        pos_year = np.isin(year_range, self.years)
        if not np.all(pos_year):
            LOGGER.info('No discount rates for given years.')
            return None
        pos_year = np.isin(self.years, year_range)

        return DiscRates(years=self.years[pos_year],
                         rates=self.rates[pos_year],
                         tag=self.tag)

    def append(self, disc_rates):
        """
        Check and append discount rates to current DiscRates. Overwrite
        discount rate if same year.

        Parameters
        ----------
        disc_rates: climada.entity.DiscRates
            DiscRates instance to append

        Raises
        ------
        ValueError
        """
        disc_rates.check()
        if self.years.size == 0:
            self.__dict__ = copy.deepcopy(disc_rates.__dict__)
            return

        self.tag.append(disc_rates.tag)

        new_year = array('l')
        new_rate = array('d')
        for year, rate in zip(disc_rates.years, disc_rates.rates):
            found = np.where(year == self.years)[0]
            if found.size > 0:
                self.rates[found[0]] = rate
            else:
                new_year.append(year)
                new_rate.append(rate)

        self.years = np.append(self.years, new_year).astype(int, copy=False)
        self.rates = np.append(self.rates, new_rate)

    def net_present_value(self, ini_year, end_year, val_years):
        """
        Compute net present value between present year and future year.

        Parameters
        ----------
        ini_year: float
            initial year
        end_year: float
            end year
        val_years: np.array
            cash flow at each year btw ini_year and end_year (both included)

        Returns
        -------
            net_present_value: float
                net present value between present year and future year.

        """
        year_range = np.arange(ini_year, end_year + 1)
        if year_range.size != val_years.size:
            raise ValueError('Wrong size of yearly values.')
        sel_disc = self.select(year_range)
        if sel_disc is None:
            raise ValueError(
                'No information of discount rates for provided years:'
                f' {ini_year} - {end_year}')
        return u_fin.net_present_value(sel_disc.years, sel_disc.rates,
                                       val_years)

    def plot(self, axis=None, figsize=(6, 8), **kwargs):
        """
        Plot discount rates per year.

        Parameters
        ----------
        axis: matplotlib.axes._subplots.AxesSubplot, optional
            axis to use
        figsize: tuple(int, int), optional
            size of the figure. The default is (6,8)
        kwargs: optional
            keyword arguments  passed to plotting function axis.plot

        Returns
        -------
        axis: matplotlib.axes._subplots.AxesSubplot
            axis handles of the plot
        """
        if not axis:
            _, axis = plt.subplots(1, 1, figsize=figsize)

        axis.set_title('Discount rates')
        axis.set_xlabel('Year')
        axis.set_ylabel('discount rate (%)')
        axis.plot(self.years, self.rates * 100, **kwargs)
        axis.set_xlim((self.years.min(), self.years.max()))
        return axis

    @classmethod
    def from_mat(cls, file_name, description='', var_names=None):
        """
        Read MATLAB file generated with previous MATLAB CLIMADA version.

        Parameters
        ----------
        file_name: str
            filename including path and extension
        description: str, optional
            description of the data. The default is ''
        var_names: dict, optional
            name of the variables in the file. The Default is
            DEF_VAR_MAT = {'sup_field_name': 'entity', 'field_name': 'discount',
               'var_name': {'year': 'year', 'disc': 'discount_rate'}}

        Returns
        -------
        climada.entity.DiscRates :
            The disc rates from matlab
        """
        if var_names is None:
            var_names = DEF_VAR_MAT
        disc = u_hdf5.read(file_name)
        tag = Tag(file_name=str(file_name), description=description)
        try:
            disc = disc[var_names['sup_field_name']]
        except KeyError:
            pass

        try:
            disc = disc[var_names['field_name']]
            years = np.squeeze(disc[var_names['var_name']['year']]). \
                astype(int, copy=False)
            rates = np.squeeze(disc[var_names['var_name']['disc']])
        except KeyError as err:
            raise KeyError("Not existing variable: %s" % str(err)) from err

        return cls(years=years, rates=rates, tag=tag)

    def read_mat(self, *args, **kwargs):
        """This function is deprecated, use DiscRates.from_mats instead."""
        LOGGER.warning("The use of DiscRates.read_mats is deprecated."
                       "Use DiscRates.from_mats instead.")
        self.__dict__ = DiscRates.from_mat(*args, **kwargs).__dict__

    @classmethod
    def from_excel(cls, file_name, description='', var_names=None):
        """
        Read excel file following template and store variables.

        Parameters
        ----------
        file_name: str
            filename including path and extension
        description: str, optional
            description of the data. The default is ''
        var_names: dict, optional
            name of the variables in the file. The Default is
            DEF_VAR_EXCEL = {'sheet_name': 'discount',
               'col_name': {'year': 'year', 'disc': 'discount_rate'}}

        Returns
        -------
        climada.entity.DiscRates :
            The disc rates from excel
        """
        if var_names is None:
            var_names = DEF_VAR_EXCEL
        dfr = pd.read_excel(file_name, var_names['sheet_name'])
        tag = Tag(file_name=str(file_name), description=description)
        try:
            years = dfr[var_names['col_name']['year']].values. \
                astype(int, copy=False)
            rates = dfr[var_names['col_name']['disc']].values
        except KeyError as err:
            raise KeyError("Not existing variable: %s" % str(err)) from err

        return cls(years=years, rates=rates, tag=tag)

    def read_excel(self, *args, **kwargs):
        """This function is deprecated, use DiscRates.from_excel instead."""
        LOGGER.warning("The use of DiscRates.read_excel is deprecated."
                       "Use DiscRates.from_excel instead.")
        self.__dict__ = DiscRates.from_mat(*args, **kwargs).__dict__

    def write_excel(self, file_name, var_names=None):
        """
        Write excel file following template.

        Parameters
        ----------
        file_name: str
            filename including path and extension
        var_names: dict, optional
            name of the variables in the file. The Default is
            DEF_VAR_EXCEL = {'sheet_name': 'discount',
               'col_name': {'year': 'year', 'disc': 'discount_rate'}}
        """
        if var_names is None:
            var_names = DEF_VAR_EXCEL
        disc_wb = xlsxwriter.Workbook(file_name)
        disc_ws = disc_wb.add_worksheet(var_names['sheet_name'])

        header = [var_names['col_name']['year'], var_names['col_name']['disc']]
        for icol, head_dat in enumerate(header):
            disc_ws.write(0, icol, head_dat)
        for i_yr, (disc_yr, disc_rt) in enumerate(zip(self.years, self.rates),
                                                  1):
            disc_ws.write(i_yr, 0, disc_yr)
            disc_ws.write(i_yr, 1, disc_rt)
        disc_wb.close()