Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions chainladder/utils/cupy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
import numpy as np

from chainladder import options
from chainladder.utils.sparse import sp

try:
import cupy as cp

cp.array([1])
module = "cupy"
except:
Comment thread
cursor[bot] marked this conversation as resolved.
except (ImportError, RuntimeError):
# RuntimeError covers e.g. cupy.cuda.runtime.CUDARuntimeError, raised
# by cp.array([1]) when CuPy is installed but the GPU/CUDA runtime
# is unusable.
if options.ARRAY_BACKEND == "cupy":
import warnings

Expand All @@ -22,29 +24,29 @@


def nansum(a, *args, **kwargs):
""" For cupy v0.6.0 compatibility """
"""For cupy v0.6.0 compatibility"""
return cp.sum(cp.nan_to_num(a), *args, **kwargs)


def nanmean(a, *args, **kwargs):
""" For cupy v0.6.0 compatibility """
"""For cupy v0.6.0 compatibility"""
return cp.sum(cp.nan_to_num(a), *args, **kwargs) / cp.sum(
~cp.isnan(a), *args, **kwargs
)


def nanmedian(a, *args, **kwargs):
""" For cupy v0.6.0 compatibility """
"""For cupy v0.6.0 compatibility"""
return cp.array(np.nanmedian(cp.asnumpy(a), *args, **kwargs))


def nanquantile(a, *args, **kwargs):
""" For cupy v0.6.0 compatibility """
"""For cupy v0.6.0 compatibility"""
return cp.array(np.nanquantile(cp.asnumpy(a), *args, **kwargs))


def unique(ar, axis=None, *args, **kwargs):
""" For cupy v0.6.0 compatibility """
"""For cupy v0.6.0 compatibility"""
return cp.array(np.unique(cp.asnumpy(ar), axis=axis, *args, **kwargs))


Expand Down
15 changes: 10 additions & 5 deletions chainladder/utils/dask.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,31 @@

try:
import dask.array as dp

dp.array([1])
module = "dask"
except:
except (ImportError, RuntimeError):
# RuntimeError covers the equivalent case of Dask being installed but
# its runtime being unusable when the dp.array([1]) probe runs.
if options.ARRAY_BACKEND == "dask":
import warnings

warnings.warn("Unable to load Dask. Using numpy instead.")
import numpy as dp

module = "numpy"

dp.nan = np.nan


def expand_dims(a, axis=0):
l = []
slices = []
for i in range(len(a.shape)):
if i == axis:
l.append(None)
l.append(slice(None))
return a.__getitem__(tuple(l))
slices.append(None)
slices.append(slice(None))
return a.__getitem__(tuple(slices))


if dp != np:
dp.expand_dims = expand_dims
25 changes: 14 additions & 11 deletions chainladder/utils/sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from sparse import COO as COO
from sparse import elemwise

def _setitem_not_supported(self, key, value) -> None: # noqa

def _setitem_not_supported(self, key, value) -> None: # noqa
raise TypeError(
"""
In-place item assignment (e.g. `triangle.values[...] = value`) is not
Expand All @@ -18,20 +19,20 @@ def _setitem_not_supported(self, key, value) -> None: # noqa
sp.isnan = np.isnan
COO.nan = np.array([1.0, np.nan])[-1]
COO.__setitem__ = _setitem_not_supported
setattr(sp, 'testing', np.testing)
setattr(sp, "testing", np.testing)
sp.sqrt = np.sqrt
sp.log = np.log
sp.exp = np.exp
sp.abs = np.abs


def nan_to_num(a, nan = 0.0):
def nan_to_num(a, nan=0.0):
if type(a) in [int, float, np.int64, np.float64]:
return np.nan_to_num(a)
if hasattr(a, "fill_value"):
a = a.copy()
a.data[np.isnan(a.data)] = nan
return COO(coords=a.coords, data=a.data, fill_value = nan, shape = a.shape)
return COO(coords=a.coords, data=a.data, fill_value=nan, shape=a.shape)


def ones(*args, **kwargs):
Expand All @@ -43,6 +44,7 @@ def nansum(a, axis=None, keepdims=None, *args, **kwargs):
axis=axis, keepdims=keepdims, *args, **kwargs
)


def nanquantile(a: COO, q: float, axis: int = 0, keepdims: bool = False):
"""
mimics np.nanquantile
Expand Down Expand Up @@ -71,15 +73,13 @@ def nanquantile(a: COO, q: float, axis: int = 0, keepdims: bool = False):
if not keep_axes:
out = np.nanquantile(a.data, q)
if keepdims:
out = np.asarray(out).reshape(
tuple(1 for _ in range(a.ndim))
)
out = np.asarray(out).reshape(tuple(1 for _ in range(a.ndim)))
return COO(out)

# map every stored value to an output location
keep_coords = a.coords[list(keep_axes)]
group_ids = np.ravel_multi_index(keep_coords, keep_shape)

# sort by group
order = np.argsort(group_ids)
group_ids = group_ids[order]
Expand All @@ -97,10 +97,11 @@ def nanquantile(a: COO, q: float, axis: int = 0, keepdims: bool = False):
out = out.reshape(keep_shape)

if keepdims:
out = np.expand_dims(out,axis)
out = np.expand_dims(out, axis)

return COO(out)


def nanmedian(a: COO, axis: int = 0, keepdims: bool = False):
"""
mimics np.nanmean
Expand All @@ -121,6 +122,7 @@ def nanmedian(a: COO, axis: int = 0, keepdims: bool = False):
"""
return nanquantile(a, 0.5, axis, keepdims)


def nanmean(a, axis=None, keepdims=None):
n = nansum(a, axis=axis, keepdims=keepdims)
d = nansum(nan_to_num(a) != 0, axis=axis, keepdims=keepdims).astype(n.dtype)
Expand All @@ -129,12 +131,13 @@ def nanmean(a, axis=None, keepdims=None):
out = n / d
return COO(data=out.data, coords=out.coords, fill_value=0, shape=out.shape)


def array(a, *args, **kwargs):
if kwargs.get("fill_value", None) is not None:
fill_value = kwargs.pop("fill_value")
else:
fill_value = COO.nan
if type(a) == sp.COO:
if isinstance(a, sp.COO):
return COO(a, *args, **kwargs, fill_value=fill_value)
else:
return COO(np.array(a, *args, **kwargs), fill_value=fill_value)
Expand Down Expand Up @@ -172,4 +175,4 @@ def floor(x: COO) -> COO:
sp.nanmean = nanmean
sp.sum = COO.sum
sp.nanquantile = nanquantile
sp.nanmedian = nanmedian
sp.nanmedian = nanmedian
19 changes: 12 additions & 7 deletions chainladder/utils/tests/test_sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
floor,
COO,
where,
nanquantile
nanquantile,
)

from sparse import all as sparse_all


def test_array_from_list_default_fill_value() -> None:
"""
Tests chainladder.utils.sparse.array() when no fill value is provided.
Expand Down Expand Up @@ -114,17 +115,19 @@ def test_floor_returns_copy() -> None:
np.testing.assert_array_equal(result.todense(), [1.0, 2.0, -1.0])
np.testing.assert_array_equal(a.todense(), [1.2, 2.7, -0.3])

def test_1D_nanquantile() -> None:

def test_1d_nanquantile() -> None:
"""
Checks that nanquantile performs in 1D special case.

Returns
-------
None
"""
a = COO(np.array([1,2,3,4]))
assert nanquantile(a,0.5) == 2.5
assert sparse_all(nanquantile(a,0.5,keepdims = True) == COO(np.array([2.5])))
a = COO(np.array([1, 2, 3, 4]))
assert nanquantile(a, 0.5) == 2.5
assert sparse_all(nanquantile(a, 0.5, keepdims=True) == COO(np.array([2.5])))


def test_keepdims_nanquantile() -> None:
"""
Expand All @@ -134,5 +137,7 @@ def test_keepdims_nanquantile() -> None:
-------
None
"""
a = COO(np.array([[1,2,3,4],[3,4,5,6]]))
assert sparse_all(nanquantile(a,0.5,keepdims = True) == COO(np.array([[2,3,4,5]])))
a = COO(np.array([[1, 2, 3, 4], [3, 4, 5, 6]]))
assert sparse_all(
nanquantile(a, 0.5, keepdims=True) == COO(np.array([[2, 3, 4, 5]]))
)
7 changes: 5 additions & 2 deletions chainladder/utils/tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import pandas as pd

from chainladder import __dt64_unit__

from chainladder.utils.data._manifest import SAMPLES
from chainladder.utils.utility_functions import date_delta_adjustment, maximum, minimum
from chainladder.utils.utility_functions import (
date_delta_adjustment,
maximum,
minimum,
)

from pathlib import Path
from typing import TYPE_CHECKING
Expand Down
Loading
Loading