Пример #1
0
    def set_backend(self, backend, inplace=False, deep=False):
        """ Converts triangle array_backend.

        Parameters
        ----------
        backend : str
            Currently supported options are 'numpy', 'sparse', and 'cupy'
        inplace : bool
            Whether to mutate the existing Triangle instance or return a new
            one.

        Returns
        -------
            Triangle with updated array_backend
        """
        if hasattr(self, "array_backend"):
            old_backend = self.array_backend
        else:
            if hasattr(self, "ldf_"):
                old_backend = self.ldf_.array_backend
            else:
                raise ValueError("Unable to determine array backend.")
        if inplace:
            if backend in ["numpy", "sparse", "cupy"]:
                lookup = {
                    "numpy": {
                        "sparse": lambda x: x.todense(),
                        "cupy": lambda x: cp.asnumpy(x),
                    },
                    "cupy": {
                        "numpy": lambda x: cp.array(x),
                        "sparse": lambda x: cp.array(x.todense()),
                    },
                    "sparse": {
                        "numpy": lambda x: sp.array(x),
                        "cupy": lambda x: sp.array(cp.asnumpy(x)),
                    },
                }
                if hasattr(self, "values"):
                    self.values = lookup[backend].get(old_backend, lambda x: x)(
                        self.values
                    )
                if deep:
                    for k, v in vars(self).items():
                        if isinstance(v, Common):
                            v.set_backend(backend, inplace=True, deep=True)
                if hasattr(self, "array_backend"):
                    self.array_backend = backend
            else:
                raise AttributeError(backend, "backend is not supported.")
            return self
        else:
            obj = self.copy()
            return obj.set_backend(backend=backend, inplace=True, deep=deep)
Пример #2
0
    def set_backend(self, backend, inplace=False):
        ''' Converts triangle array_backend.

        Parameters
        ----------
        backend : str
            Currently supported options are 'numpy', 'sparse', and 'cupy'
        inplace : bool
            Whether to mutate the existing Triangle instance or return a new
            one.

        Returns
        -------
            Triangle with updated array_backend
        '''
        if hasattr(self, 'array_backend'):
            old_backend = self.array_backend
        else:
            if hasattr(self, 'ldf_'):
                old_backend = self.ldf_.array_backend
            else:
                raise ValueError('Unable to determine array backend.')
        if inplace:
            if backend in ['numpy', 'sparse', 'cupy']:
                lookup = {
                    'numpy': {
                        'sparse': lambda x: x.todense(),
                        'cupy': lambda x: cp.asnumpy(x)
                    },
                    'cupy': {
                        'numpy': lambda x: cp.array(x),
                        'sparse': lambda x: cp.array(x.todense())
                    },
                    'sparse': {
                        'numpy': lambda x: sp.array(x),
                        'cupy': lambda x: sp.array(cp.asnumpy(x))
                    }
                }
                if hasattr(self, 'values'):
                    self.values = lookup[backend].get(old_backend,
                                                      lambda x: x)(self.values)
                for k, v in vars(self).items():
                    if isinstance(v, Common):
                        v.set_backend(backend, inplace=True)
                if hasattr(self, 'array_backend'):
                    self.array_backend = backend
            else:
                raise AttributeError(backend, 'backend is not supported.')
            return self
        else:
            obj = copy.deepcopy(self)
            return obj.set_backend(backend=backend, inplace=True)
Пример #3
0
 def _slice_valuation(self, key):
     ''' private method for handling of valuation slicing '''
     obj = copy.deepcopy(self)
     obj.valuation_date = min(obj.valuation[key].max(), obj.valuation_date)
     key = key.reshape(self.shape[-2:], order='f')
     nan_tri = np.ones(self.shape[-2:])
     nan_tri = key * nan_tri
     nan_tri[nan_tri == 0] = np.nan
     o, d = nan_tri.shape
     o_idx = np.arange(o)[list(np.sum(np.isnan(nan_tri), 1) != d)]
     d_idx = np.arange(d)[list(np.sum(np.isnan(nan_tri), 0) != o)]
     obj.odims = obj.odims[np.sum(np.isnan(nan_tri), 1) != d]
     if len(obj.ddims) > 1:
         obj.ddims = obj.ddims[np.sum(np.isnan(nan_tri), 0) != o]
     xp = cp.get_array_module(obj.values)
     if xp == cp:
         nan_tri = cp.array(nan_tri)
     if xp == sp:
         nan_tri = sp(nan_tri)
     obj.values = (obj.values * nan_tri)
     if np.all(o_idx == np.array(range(o_idx[0], o_idx[-1] + 1))):
         o_idx = slice(o_idx[0], o_idx[-1] + 1)
     if np.all(d_idx == np.array(range(d_idx[0], d_idx[-1] + 1))):
         d_idx = slice(d_idx[0], d_idx[-1] + 1)
     if type(o_idx) is slice or type(d_idx) is slice:
         # If contiguous slices, this is faster
         obj.values = obj.values[..., o_idx, d_idx]
     else:
         obj.values = xp.take(xp.take(obj.values, o_idx, -2), d_idx, -1)
     return obj
Пример #4
0
 def _slice_development(self, key):
     ''' private method for handling of development slicing '''
     obj = copy.deepcopy(self)
     obj.ddims = obj.ddims[key]
     if cp.get_array_module(obj.values) == cp:
         key = cp.array(key)
     obj.values = obj.values[..., key]
     return self._cleanup_slice(obj)
def read_json(json_str, array_backend=None):
    def sparse_in(json_str, dtype, shape):
        k, v, o, d = shape
        x = json.loads(json_str)
        y = np.array([tuple([int(idx) for idx in item[1:-1].split(',')])
                      for item in x.keys()])
        new = coo_matrix(
            (np.array(list(x.values())), (y[:, 0], y[:, 1])),
            shape=(k*v*o, d), dtype=dtype).toarray().reshape(k,v,o,d)
        new[new==0] = np.nan
        return new

    if array_backend is None:
        from chainladder import ARRAY_BACKEND
        array_backend = ARRAY_BACKEND
    json_dict = json.loads(json_str)
    if type(json_dict) is list:
        import chainladder as cl
        return Pipeline(steps=[
            (item['name'],
             cl.__dict__[item['__class__']]().set_params(**item['params']))
            for item in json_dict])
    elif 'kdims' in json_dict.keys():
        tri = Triangle(array_backend=array_backend)
        arrays = ['kdims', 'vdims', 'odims', 'ddims']
        for array in arrays:
            setattr(tri, array, np.array(
                json_dict[array]['array'], dtype=json_dict[array]['dtype']))
        shape = (len(tri.kdims), len(tri.vdims), len(tri.odims), len(tri.ddims))
        properties = ['key_labels', 'origin_grain', 'development_grain',
                    'nan_override', 'is_cumulative']
        for prop in properties:
            setattr(tri, prop, json_dict[prop])
        if json_dict.get('is_val_tri', False):
            tri.ddims = pd.PeriodIndex(tri.ddims, freq=tri.development_grain).to_timestamp(how='e')
        tri.valuation_date = pd.to_datetime(
            json_dict['valuation_date'], format='%Y-%m-%d').to_period('M').to_timestamp(how='e')
        tri._set_slicers()
        tri.valuation = tri._valuation_triangle()
        if json_dict['values'].get('sparse', None):
            tri.values = sparse_in(json_dict['values']['array'],
                                   json_dict['values']['dtype'], shape)
            if tri.is_cumulative:
                tri.is_cumulative = False
                tri = tri.incr_to_cum()
        else:
            tri.values = np.array(json_dict['values']['array'],
                                  dtype=json_dict['values']['dtype'])
        if array_backend == 'cupy':
            tri.values = cp.array(tri.values)
        return tri
    else:
        import chainladder as cl
        return cl.__dict__[
            json_dict['__class__']]().set_params(**json_dict['params'])
Пример #6
0
 def _slice_valuation(self, key):
     ''' private method for handling of valuation slicing '''
     obj = copy.deepcopy(self)
     obj.valuation_date = min(obj.valuation[key].max(), obj.valuation_date)
     key = key.reshape(self.shape[-2:], order='f')
     nan_tri = np.ones(self.shape[-2:])
     nan_tri = key * nan_tri
     nan_tri[nan_tri == 0] = np.nan
     o, d = nan_tri.shape
     o_idx = np.arange(o)[list(np.sum(np.isnan(nan_tri), 1) != d)]
     d_idx = np.arange(d)[list(np.sum(np.isnan(nan_tri), 0) != o)]
     obj.odims = obj.odims[np.sum(np.isnan(nan_tri), 1) != d]
     if len(obj.ddims) > 1:
         obj.ddims = obj.ddims[np.sum(np.isnan(nan_tri), 0) != o]
     xp = cp.get_array_module(obj.values)
     if xp == cp:
         nan_tri = cp.array(nan_tri)
     obj.values = (obj.values * nan_tri)
     obj.values = xp.take(xp.take(obj.values, o_idx, -2), d_idx, -1)
     return self._cleanup_slice(obj)
Пример #7
0
    def _validate_arithmetic(self, other):
        ''' Common functionality BEFORE arithmetic operations '''
        obj = copy.deepcopy(self)
        xp = cp.get_array_module(obj.values)
        other = other if type(other) in [int, float] else copy.deepcopy(other)
        ddims = None
        odims = None
        if type(other) not in [int, float, np.float64, np.int64]:
            if len(self.vdims) != len(other.vdims):
                raise ValueError('Triangles must have the same number of ' +
                                 'columns')
            if len(self.kdims) != len(other.kdims):
                raise ValueError('Triangles must have the same number of ' +
                                 'index')
            if len(self.vdims) == 1:
                other.vdims = np.array([None])
            # If broadcasting doesn't work, then try union of origin/developments
            # before failure
            a, b = self.shape[-2:], other.shape[-2:]
            if not (a[0] == 1 or b[0] == 1 or a[0] == b[0]) or \
               not (a[1] == 1 or b[1] == 1 or a[1] == b[1]):
                ddims = pd.concat((pd.Series(self.ddims, index=self.ddims),
                                pd.Series(other.ddims, index=other.ddims)), axis=1)
                odims = pd.concat((pd.Series(self.odims, index=self.odims),
                                pd.Series(other.odims, index=other.odims)), axis=1)
                other_arr = xp.zeros(
                    (other.shape[0], other.shape[1], len(odims), len(ddims)))
                other_arr[:] = xp.nan
                o_arr1 = odims[1].isna().values
                o_arr0 = odims[0].isna().values
                d_arr1 = ddims[1].isna().values
                d_arr0 = ddims[0].isna().values
                if xp == cp:
                    o_arr1 = cp.array(o_arr1)
                    o_arr0 = cp.array(o_arr0)
                    d_arr1 = cp.array(d_arr1)
                    d_arr0 = cp.array(d_arr0)
                ol = int(xp.where(~o_arr1 == 1)[0].min())
                oh = int(xp.where(~o_arr1 == 1)[0].max()+1)
                if np.any(self.ddims != other.ddims):
                    dl = int(xp.where(~d_arr1 == 1)[0].min())
                    dh = int(xp.where(~d_arr1 == 1)[0].max()+1)
                    other_arr[:, :, ol:oh, dl:dh] = other.values
                else:
                    other_arr[:, :, ol:oh, :] = other.values

                obj_arr = xp.zeros(
                    (self.shape[0], self.shape[1], len(odims), len(ddims)))
                obj_arr[:] = xp.nan
                ol = int(xp.where(~o_arr0 == 1)[0].min())
                oh = int(xp.where(~o_arr0 == 1)[0].max()+1)
                if np.any(self.ddims != other.ddims):
                    dl = int(xp.where(~d_arr0 == 1)[0].min())
                    dh = int(xp.where(~d_arr0 == 1)[0].max()+1)
                    obj_arr[:, :, ol:oh, dl:dh] = self.values
                else:
                    obj_arr[:, :, ol:oh, :] = self.values

                odims = np.array(odims.index)
                ddims = np.array(ddims.index)
                obj.ddims =  ddims
                obj.odims =  odims
                obj.values = obj_arr
                other.values = other_arr
                obj._set_slicers()
                obj.valuation = obj._valuation_triangle()
                if hasattr(obj, '_nan_triangle_'):
                    # Force update on _nan_triangle at next access.
                    del obj._nan_triangle_
            other = other.values
        return obj, other
Пример #8
0
def read_json(json_str, array_backend=None):
    def sparse_in(json_str, dtype, shape):
        k, v, o, d = shape
        x = json.loads(json_str)
        y = np.array([
            tuple([int(idx) for idx in item[1:-1].split(",")])
            for item in x.keys()
        ])
        new = (coo_matrix(
            (np.array(list(x.values())), (y[:, 0], y[:, 1])),
            shape=(k * v * o, d),
            dtype=dtype,
        ).toarray().reshape(k, v, o, d))
        new[new == 0] = np.nan
        return new

    if array_backend is None:
        from chainladder import ARRAY_BACKEND

        array_backend = ARRAY_BACKEND
    json_dict = json.loads(json_str)
    if type(json_dict) is list:
        import chainladder as cl

        return Pipeline(steps=[(
            item["name"],
            cl.__dict__[item["__class__"]]().set_params(**item["params"]),
        ) for item in json_dict])
    elif "kdims" in json_dict.keys():
        tri = Triangle()
        tri.array_backend = array_backend
        arrays = ["kdims", "vdims", "odims", "ddims"]
        for array in arrays:
            setattr(
                tri,
                array,
                np.array(json_dict[array]["array"],
                         dtype=json_dict[array]["dtype"]),
            )
        shape = (len(tri.kdims), len(tri.vdims), len(tri.odims),
                 len(tri.ddims))
        properties = [
            "key_labels",
            "origin_grain",
            "development_grain",
            "is_cumulative",
        ]
        for prop in properties:
            setattr(tri, prop, json_dict[prop])
        if json_dict.get("is_val_tri", False):
            tri.ddims = pd.PeriodIndex(
                tri.ddims, freq=tri.development_grain).to_timestamp(how="e")
        tri.valuation_date = (pd.to_datetime(
            json_dict["valuation_date"],
            format="%Y-%m-%d").to_period("M").to_timestamp(how="e"))
        if json_dict["values"].get("sparse", None):
            tri.values = sparse_in(json_dict["values"]["array"],
                                   json_dict["values"]["dtype"], shape)
        else:
            tri.values = np.array(json_dict["values"]["array"],
                                  dtype=json_dict["values"]["dtype"])
        if array_backend == "cupy":
            tri.values = cp.array(tri.values)
        if tri.is_cumulative:
            tri.is_cumulative = False
            tri = tri.incr_to_cum()
        if "sub_tris" in json_dict.keys():
            for k, v in json_dict["sub_tris"].items():
                setattr(tri, k, read_json(v, array_backend))
        if "dfs" in json_dict.keys():
            for k, v in json_dict["dfs"].items():
                df = pd.read_json(v)
                if len(df.columns) == 1:
                    df = df.iloc[:, 0]
                setattr(tri, k, df)
        tri._set_slicers()
        return tri
    else:
        import chainladder as cl

        return cl.__dict__[json_dict["__class__"]]().set_params(
            **json_dict["params"])