Skip to content
Snippets Groups Projects
Commit cfa1cacb authored by dh00601's avatar dh00601
Browse files

working on dict tests

parent 9709eb68
No related branches found
No related tags found
No related merge requests found
......@@ -15,7 +15,7 @@
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="31.5" y="14">coverage</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">51%</text>
<text x="80" y="14">51%</text>
<text x="80" y="15" fill="#010101" fill-opacity=".3">53%</text>
<text x="80" y="14">53%</text>
</g>
</svg>
......@@ -24,7 +24,17 @@ from binarycpython.tests.test_dicts import (
test_merge_dicts,
test_setopts,
test_AutoVivicationDict,
test_inspect_dict
test_inspect_dict,
test_custom_sort_dict,
test_filter_dict,
test_filter_dict_through_values,
test_prepare_dict,
test_normalize_dict,
test_multiply_values_dict,
test_count_keys_recursive,
test_keys_to_floats,
test_recursive_change_key_to_float,
test_recursive_change_key_to_string
)
from binarycpython.tests.test_ensemble import (
test_binaryc_json_serializer,
......
"""
Unittests for dicts module
TODO: keys_to_floats
TODO: recursive_change_key_to_float
TODO: recursive_change_key_to_string
TODO: _nested_set
TODO: _nested_get
TODO: _recursive_normalize_floats
TODO: multiply_float_values
TODO: subtract_dicts
TODO: count_keys_recursive
TODO: update_dicts
TODO: multiply_values_dict
"""
import os
......@@ -31,7 +24,13 @@ from binarycpython.utils.dicts import (
filter_dict,
filter_dict_through_values,
prepare_dict,
custom_sort_dict
custom_sort_dict,
multiply_values_dict,
keys_to_floats,
count_keys_recursive,
recursive_change_key_to_float,
recursive_change_key_to_string,
multiply_float_values
)
TMP_DIR = temp_dir("tests", "test_dicts")
......@@ -406,8 +405,144 @@ class test_normalize_dict(unittest.TestCase):
self.assertEqual(sum(list(res_1.values())), 1.0)
class test_multiply_values_dict(unittest.TestCase):
"""
Unittests for function multiply_values_dict
"""
def test_multiply_values_dict(self):
with Capturing() as output:
self._test_multiply_values_dict()
def _test_multiply_values_dict(self):
"""
Test multiply_values_dict
"""
input_1 = {'a': 1, 'b': {'c': 10}}
desired_output_1 = {'a': 2, 'b': {'c': 20}}
output_1 = multiply_values_dict(input_1, 2)
#
self.assertEqual(output_1, desired_output_1)
class test_count_keys_recursive(unittest.TestCase):
"""
Unittests for function count_keys_recursive
"""
def test_count_keys_recursive(self):
with Capturing() as output:
self._test_count_keys_recursive()
def _test_count_keys_recursive(self):
"""
Test count_keys_recursive
"""
#
input_1 = {'a': 2, 'b': {'c': 20, 'd': {'aa': 1, 'bb': 2}}}
output_1 = count_keys_recursive(input_1)
#
self.assertEqual(output_1, 6)
class test_keys_to_floats(unittest.TestCase):
"""
Unittests for function keys_to_floats
"""
def test_keys_to_floats(self):
with Capturing() as output:
self._test_keys_to_floats()
def _test_keys_to_floats(self):
"""
Test keys_to_floats
"""
input_1 = {'a': 1, '1': 2, '1.0': 3, 'b': {4: 10, '5': 1}}
output_1 = keys_to_floats(input_1)
desired_output_1 = {'a': 1, 1.0: 3, 'b': {4.0: 10, 5.0: 1}}
self.assertEqual(output_1, desired_output_1)
class test_recursive_change_key_to_float(unittest.TestCase):
"""
Unittests for function recursive_change_key_to_float
"""
def test_recursive_change_key_to_float(self):
with Capturing() as output:
self._test_recursive_change_key_to_float()
def _test_recursive_change_key_to_float(self):
"""
Test recursive_change_key_to_float
"""
input_1 = {'a': 1, '1': 2, '1.0': 3, 'b': {4: 10, '5': 1}}
output_1 = recursive_change_key_to_float(input_1)
desired_output_1 = OrderedDict([('a', 1), (1.0, 3), ('b', OrderedDict([(4.0, 10), (5.0, 1)]))])
self.assertEqual(output_1, desired_output_1)
class test_recursive_change_key_to_string(unittest.TestCase):
"""
Unittests for function recursive_change_key_to_string
"""
def test_recursive_change_key_to_string(self):
with Capturing() as output:
self._test_recursive_change_key_to_string()
def _test_recursive_change_key_to_string(self):
"""
Test recursive_change_key_to_string
"""
input_1 = {'a': 1, '1': 2, '1.0': 3, 'b': {4: 10, '5': 1, 6: 10}}
output_1 = recursive_change_key_to_string(input_1, "{:.2E}")
desired_output_1 = OrderedDict([('a', 1),
('1.00E+00', 3),
('b',
OrderedDict([('4.00E+00', 10),
('5.00E+00', 1),
('6.00E+00', 10)]))])
self.assertEqual(output_1, desired_output_1)
class test_multiply_float_values(unittest.TestCase):
"""
Unittests for function multiply_float_values
"""
def test_multiply_float_values(self):
with Capturing() as output:
self._test_multiply_float_values()
def _test_multiply_float_values(self):
"""
Test multiply_float_values
"""
# Test with all valid input
input_1 = {1: 2.2, '2': {'a': 2, 'b': 10, 'c': 0.5}}
multiply_float_values(input_1, 2)
desired_output_1 = {1: 4.4, '2': {'a': 2, 'b': 10, 'c': 1.0}}
#
self.assertEqual(input_1, desired_output_1)
# Test with unrecognised input:
input_2 = {1: 2.2, '2': {'a': 2, 'b': 10, 'c': 0.5, 'd': dummy('david')}}
_ = multiply_float_values(input_2, 2)
if __name__ == "__main__":
unittest.main()
\ No newline at end of file
unittest.main()
# class TestDistributions(unittest.TestCase):
# """
# Unittest class
# # https://stackoverflow.com/questions/17353213/init-for-unittest-testcase
# """
# def __init__(self, *args, **kwargs):
# """
# init
# """
# super(TestDistributions, self).__init__(*args, **kwargs)
......@@ -7,26 +7,29 @@ import collections
import astropy.units as u
import numpy as np
def keys_to_floats(json_data):
from typing import Union
NUMERIC = Union[int, float, complex, np.number]
def keys_to_floats(input_dict: dict) -> dict:
"""
Function to convert all the keys of the dictionary to float to float
we need to convert keys to floats:
this is ~ a factor 10 faster than David's recursive_change_key_to_float routine, probably because this version only does the float conversion, nothing else.
"""
# assumes nested dicts ...
# new_data = {}
this is ~ a factor 10 faster than David's ``recursive_change_key_to_float`` routine, probably because this version only does the float conversion, nothing else.
Args:
input_dict: dict of which we want to turn all the keys to float types if possible
# but this copies the variable type, but has some
# pointless copying
# new_data = copy.copy(json_data)
# new_data.clear()
Returns:
new_dict: dict of which the keys have been turned to float types where possible
"""
# this adopts the type correctly *and* is fast
new_data = type(json_data)()
new_dict = type(input_dict)()
for k, v in json_data.items():
for k, v in input_dict.items():
# convert key to a float, if we can
# otherwise leave as is
try:
......@@ -37,7 +40,7 @@ def keys_to_floats(json_data):
# act on value(s)
if isinstance(v, list):
# list data
new_data[newkey] = [
new_dict[newkey] = [
keys_to_floats(item)
if isinstance(item, collections.abc.Mapping)
else item
......@@ -45,20 +48,27 @@ def keys_to_floats(json_data):
]
elif isinstance(v, collections.abc.Mapping):
# dict, ordereddict, etc. data
new_data[newkey] = keys_to_floats(v)
new_dict[newkey] = keys_to_floats(v)
else:
# assume all other data are scalars
new_data[newkey] = v
new_dict[newkey] = v
return new_data
return new_dict
def recursive_change_key_to_float(input_dict):
def recursive_change_key_to_float(input_dict: dict) -> dict:
"""
Function to recursively change the key to float
This only works if the dict contains just sub-dicts or numbers/strings.
Does not work with lists as values
Args:
input_dict: dict of which we want to turn all the keys to float types if possible
Returns:
new_dict: dict of which the keys have been turned to float types where possible
"""
new_dict = collections.OrderedDict()
......@@ -80,26 +90,36 @@ def recursive_change_key_to_float(input_dict):
return new_dict
def recursive_change_key_to_string(input_dict, custom_format="{:g}"):
def recursive_change_key_to_string(input_dict: dict, custom_format: str = "{:g}"):
"""
Function to recursively change the key back to a string but this time in a format that we decide
Function to recursively change the key back to a string but this time in a format that we decide. We'll try to turn a string key into a float key before formatting the key
Args:
input_dict: dict of which we want to turn all the keys to string types (with a custom format)
custom_format: custom format used when turning the key to strings
Returns:
new_dict: dict of which the keys have been turned to string types where possible
"""
new_dict = collections.OrderedDict()
for key in input_dict:
# Try to turn into a float
try:
string_key = float(key)
except ValueError:
string_key = key
# Turn into string with new format
if not isinstance(string_key, str):
string_key = custom_format.format(string_key)
# If dictionary type, call function again
if isinstance(input_dict[key], (dict, collections.OrderedDict)):
if isinstance(key, (int, float)):
string_key = custom_format.format(key)
new_dict[string_key] = recursive_change_key_to_string(input_dict[key])
else:
new_dict[key] = recursive_change_key_to_string(input_dict[key])
new_dict[string_key] = recursive_change_key_to_string(input_dict[key], custom_format)
else:
if isinstance(key, (int, float)):
string_key = custom_format.format(key)
new_dict[string_key] = input_dict[key]
else:
new_dict[key] = input_dict[key]
new_dict[string_key] = input_dict[key]
return new_dict
......@@ -130,22 +150,22 @@ def _nested_get(dic, keys):
return dic[keys[-1]]
def _recursive_normalize_floats(path, d, const, parent=None, ignore=None):
def _recursive_normalize_floats(path, input_dict, factor, parent=None, ignore=None):
"""
function to walk through the dictionary, multiplying only float values by a const
Function to walk through the dictionary, multiplying only float values by a factor
"""
if not parent:
parent = d
parent = input_dict
for k, v in d.items():
for k, v in input_dict.items():
if ignore and k in ignore:
continue
if isinstance(v, float):
path.append(k)
# must be a float, multiply by the constant
_nested_set(parent, path, v * const)
# must be a float, multiply by the factor
_nested_set(parent, path, v * factor)
path.pop()
elif isinstance(v, (str, int)):
path.append(k)
......@@ -160,7 +180,7 @@ def _recursive_normalize_floats(path, d, const, parent=None, ignore=None):
elif isinstance(v, collections.abc.Mapping):
path.append(k)
# nested dict
_recursive_normalize_floats(path, v, const, parent=parent)
_recursive_normalize_floats(path, v, factor, parent=parent)
path.pop()
else:
print(
......@@ -170,17 +190,17 @@ def _recursive_normalize_floats(path, d, const, parent=None, ignore=None):
)
def multiply_float_values(d, const, ignore=None):
def multiply_float_values(input_dict, factor, ignore=None):
"""
A function to recursively multiply values of a (nested) dictionary that are floats by a constant. Nested dictionaries call this function recursively.
Args:
d: the dictionary
const: the constant that multiplies float values
input_dict: the dictionary
factor: the constant that multiplies float values
"""
path = []
_recursive_normalize_floats(path, d, const, parent=d, ignore=ignore)
_recursive_normalize_floats(path, input_dict, factor, parent=input_dict, ignore=ignore)
def subtract_dicts(dict_1: dict, dict_2: dict) -> dict:
......@@ -208,7 +228,7 @@ def subtract_dicts(dict_1: dict, dict_2: dict) -> dict:
dict_2: second dictionary
Returns:
Subtracted dictionary, i.e. ``dict_1 - dict_2``
Subtracted dictionary, i.e. ``dict_1 - dict_2``
"""
# Set up new dict
......@@ -391,9 +411,15 @@ def inspect_dict(
return structure_dict
def count_keys_recursive(input_dict):
def count_keys_recursive(input_dict: dict) -> int:
"""
Function to count the total number of keys in a dictionary
Function to recursively count the total number of keys in a dictionary.
Args:
input_dict: dictionary that we want to know the total amount of keys from.
Returns:
local_count: total amount of keys within the input_dict.
"""
local_count = 0
......@@ -401,6 +427,7 @@ def count_keys_recursive(input_dict):
local_count += 1
if isinstance(input_dict[key], (dict, collections.OrderedDict)):
local_count += count_keys_recursive(input_dict[key])
return local_count
......@@ -643,11 +670,20 @@ def update_dicts(dict_1: dict, dict_2: dict) -> dict:
return new_dict
def multiply_values_dict(input_dict, factor):
def multiply_values_dict(input_dict: dict, factor: NUMERIC):
"""
Function that goes over dictionary recursively and multiplies the value if possible by a factor
If the key equals "general_info", the multiplication gets skipped
If the key equals "general_info", the multiplication gets skipped.
This function changes the values in-place, so the original dict is modified
Args:
input_dict: dictionary of which we want to multiply the values by <factor>
factor: factor that we want to multiply the values with
Returns:
multiplied_dict: dict containing the multiplied keys. This is the same object as we passed as input.
"""
for key in input_dict:
......@@ -661,7 +697,7 @@ def multiply_values_dict(input_dict, factor):
return input_dict
def custom_sort_dict(input_dict):
def custom_sort_dict(input_dict: dict) -> dict:
"""
Returns a dictionary that is ordered, but can handle numbers better than normal OrderedDict
......
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