Skip to content
Snippets Groups Projects
Commit 4bf01161 authored by Bert Palm's avatar Bert Palm 🎇
Browse files

rework, more explicit

parent 7002faef
No related branches found
No related tags found
No related merge requests found
No preview for this file type
......@@ -44,6 +44,30 @@ class DictOfSeries(OrderedDict):
if missing:
raise KeyError(f"{missing} not in index")
def __check_value(self, val):
if isinstance(val, (DictOfSeries, pd.Series)):
return
if np.isscalar(val):
return
raise ValueError(f"assignments with a values of type {type(val)} are not supported")
def __get_by_label(self, key):
# return a pd.Series
return super().__getitem__(key)
def __get_by_list(self, keys):
new = DictOfSeries()
for k in keys:
new[k] = super().__getitem__(k)
return new
def __get_by_slice(self, keys, slicer):
new = DictOfSeries()
for k in keys:
# pd.Series[slice] always return a pd.Series
new[k] = super().__getitem__(k)[slicer]
return new
def __getitem__(self, key):
"""
`dios['a']` return a pd.Series
......@@ -53,30 +77,20 @@ class DictOfSeries(OrderedDict):
If 'iterable' contains any(!) label that does not exist
a KeyError is raised
"""
# return a pd.Series if we got a single label
if isinstance(key, str):
return super().__getitem__(key)
slicer = slice(None)
self.__check_keys([key])
return self.__get_by_label(key)
if isinstance(key, _listlike):
keys = key
elif isinstance(key, slice):
keys = self.keys()
slicer = keys
else:
raise KeyError(f"{key}")
self.__check_keys(key)
return self.__get_by_list(key)
self.__check_keys(key)
if isinstance(key, slice):
return self.__get_by_slice(self.keys(), key)
new = DictOfSeries()
for k in keys:
# pd.Series[slice] always return a pd.Series
new[k] = super().__getitem__(k)[slicer]
return new
raise KeyError(f"{key}")
def __addnew(self, key, value):
def __assign_new(self, key, value):
v = value
if isinstance(v, DictOfSeries):
v = value.squeeze()
......@@ -93,77 +107,121 @@ class DictOfSeries(OrderedDict):
else:
raise ValueError(f"Only pd.Series and DictOfSeries (of length 1) can be assigned new")
def __setitems(self, keys, slicer, val, itersrc=False):
# we take for granted, that all keys already exist
v = val
for k in keys:
if itersrc:
v = val[k]
super().__getitem__(k).loc[slicer] = v
def __assign_by_slice(self, keys, slicer, val):
if isinstance(val, DictOfSeries):
self.__dios_to_dios(keys, slicer, val)
return
for k in keys:
self.__assign_with_slice(k, slicer, val)
def __assign_by_list(self, keys, val):
if isinstance(val, DictOfSeries):
self.__dios_to_dios(keys, None, val)
return
for k in keys:
self.__assign_by_label(k, val)
return
def __assign_with_slice(self, key, sl, val):
# scalars and everything that has the same size like
# sliced self is allowed.
if np.isscalar(val):
super().__getitem__(key)[sl] = val
return
# ensure same size
item = super().__getitem__(key)
if len(item[sl]) == len(val):
item[sl] = val
return
raise ValueError('not the same length') # todo more info here
def __assign_by_label(self, key, val):
# only Series and scalar are allowed
if isinstance(val, pd.Series):
super().__setitem__(key, val)
elif np.isscalar(val):
super().__getitem__(key)[:] = val
else:
raise ValueError("Only scalars, strings and pd.Series can "
"be assigned to DictionaryOfSeries")
return
def __setitem__(self, key, value):
"""
dios['a'] = pd.Series() -> set a existing or add a new item
dios['a'] = any -> pass any to pd.Series(any) then add or set
dios[iterable]
"""
slicer = slice(None)
# determine action by keys
if isinstance(key, str):
if key not in self.keys():
self.__addnew(key, value)
return
keys = [key]
self.__assign_new(key, value)
else:
self.__check_value(value)
self.__assign_by_label(key, value)
elif isinstance(key, _listlike):
keys = key
elif isinstance(key, slice):
keys = self.keys()
slicer = key
else:
raise KeyError(f"{key}")
self.__check_keys(key)
self.__check_value(value)
self.__assign_by_list(key, value)
self.__check_keys(keys)
elif isinstance(key, slice):
self.__check_value(value)
self.__assign_by_slice(self.keys(), key, value)
# determine dimension by value
if isinstance(value, DictOfSeries):
self.__dios_to_dios(keys, slicer, value)
else:
self.__setitems(keys, slicer, value)
return
def __setitems(self, keys, slicer, val, itersrc=False):
# we take for granted, that all keys already exist
v = val
for k in keys:
if itersrc:
v = val[k]
super().__getitem__(k).loc[slicer] = v
raise KeyError(f"{key}")
def __dios_to_dios(self, keys, slicer, val):
def __dios_to_dios(self, keys, slicer, other):
method = dios_options['dios_to_dios_method']
if method == 0:
# assign where possible, otherwise ignore
keys = (k for k in keys if k in val.keys())
self.__setitems(keys, slicer, val, itersrc=True)
keys = (k for k in keys if k in other.keys())
for k in keys:
if slicer is None:
self.__assign_by_label()
else:
self.__assign_with_slice(k, slicer, other[k])
return
# todo: here i stopped
elif method == 1:
# dest.keys ∩ src.keys >= 1
keys = [k for k in keys if k in val.keys()]
keys = [k for k in keys if k in other.keys()]
if keys:
self.__setitems(keys, slicer, val, itersrc=True)
self.__setitems(keys, slicer, other, itersrc=True)
return
else:
err = "src-DioS and dest-DioS need to share at least one key, "
elif method == 2:
# dest.keys >= src.keys
dest_missing = [k for k in val.keys() if k not in keys]
dest_missing = [k for k in other.keys() if k not in keys]
if not dest_missing:
self.__setitems(keys, slicer, val, itersrc=True)
self.__setitems(keys, slicer, other, itersrc=True)
return
else:
err = f"{dest_missing} are missing in the destiny-dios, "
elif method == 3:
# dest.keys == src.keys
diff = set(keys).symmetric_difference(set(val.keys()))
diff = set(keys).symmetric_difference(set(other.keys()))
if not diff:
self.__setitems(keys, slicer, val, itersrc=True)
self.__setitems(keys, slicer, other, itersrc=True)
return
err = f"{diff} is not in both of src- and dest-dios, "
else:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment