From c9edf4ee0384a533608eb17425baf9e2b2aa7fdb Mon Sep 17 00:00:00 2001 From: John S Bogaardt Date: Sat, 7 Mar 2020 12:50:22 -0700 Subject: [PATCH] resolves #59 Add cl.concat functionality --- chainladder/utils/__init__.py | 3 +- chainladder/utils/tests/test_utilities.py | 5 ++++ chainladder/utils/utility_functions.py | 35 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/chainladder/utils/__init__.py b/chainladder/utils/__init__.py index ecf395f2..cb17e645 100644 --- a/chainladder/utils/__init__.py +++ b/chainladder/utils/__init__.py @@ -6,6 +6,5 @@ DataFrame, Series, Row, Column, Tabs, CSpacer, RSpacer, Title, Image, VSpacer, HSpacer) # noqa (API import) from chainladder.utils.utility_functions import ( # noqa (API import) - load_dataset, parallelogram_olf, read_pickle, read_json) + load_dataset, parallelogram_olf, read_pickle, read_json, concat) from chainladder.utils.cupy import cp - diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 1205da96..72a4407b 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -40,3 +40,8 @@ def test_pipeline_json_io(): for item in pipe.get_params()['steps']} == \ {item[0]: item[1].get_params() for item in pipe2.get_params()['steps']} + +def test_concat(): + tri = cl.load_dataset('clrd').groupby('LOB').sum() + assert cl.concat([tri.loc['wkcomp'], tri.loc['comauto']], axis=0) == \ + tri.loc[['wkcomp', 'comauto']] diff --git a/chainladder/utils/utility_functions.py b/chainladder/utils/utility_functions.py index 96d98444..4d6bf683 100644 --- a/chainladder/utils/utility_functions.py +++ b/chainladder/utils/utility_functions.py @@ -8,6 +8,7 @@ import joblib import json import os +import copy from chainladder.core.triangle import Triangle from chainladder.workflow import Pipeline @@ -128,3 +129,37 @@ def parallelogram_olf(values, date, start_date=None, end_date=None, y.columns = ['Origin', 'OLF'] y['Origin'] = y['Origin'].astype(str) return y.set_index('Origin') + +def concat(objs, axis): + """ Concatenate Triangle objects along a particular axis. + + Parameters + ---------- + objs : list or tuple + A list or tuple of Triangle objects to concat. All non-concat axes must + be identical and all elements of the concat axes must be unique. + axis : string or int + The axis along which to concatenate. + + Returns + ------- + Updated triangle + """ + xp = cp.get_array_module(objs[0].values) + axis = objs[0]._get_axis(axis) + mapper = {0: 'kdims', 1: 'vdims', 2: 'odims', 3: 'ddims'} + for k, v in mapper.items(): + if k != axis: # All non-concat axes must be identical + assert xp.all(xp.array([getattr(obj, mapper[k]) for obj in objs]) == + getattr(objs[0], mapper[k])) + else: # All elements of concat axis must be unique + new_axis = xp.concatenate([getattr(obj, mapper[axis]) for obj in objs]) + if axis == 0: + assert len(pd.DataFrame(new_axis).drop_duplicates()) == len(new_axis) + else: + assert len(new_axis) == len(set(new_axis)) + out = copy.deepcopy(objs[0]) + out.values = xp.concatenate([obj.values for obj in objs], axis=axis) + setattr(out, mapper[axis], new_axis) + out._set_slicers() + return out