Skip to content

Commit

Permalink
ARE-6139: sensitivity analysis enpoint wrapper (#49)
Browse files Browse the repository at this point in the history
* python wrapper for sensitivity analsys and tests drafts

* some updates to the tests

* candidates filtering and supporting test

* made parameterized test, added list of attributes
  • Loading branch information
almabekov authored Mar 29, 2019
1 parent f10ce73 commit 0593d5b
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
11 changes: 11 additions & 0 deletions analyzere/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ def candidate_metrics(self):
resp = request('get', path)
return convert_to_analyzere_object(resp)

def sensitivity_analysis(self, candidates=[]):
# candidates can be only non negative integers
candidates = list(filter(lambda x: isinstance(x, int) and x >= 0, candidates))
if len(candidates) == 0:
path = '{}/sensitivity_analysis'.format(self._get_path(self.id))
else:
path = '{}/sensitivity_analysis?candidates={}'.format(self._get_path(self.id),
','.join(str(c) for c in candidates))
resp = request('get', path)
return convert_to_analyzere_object(resp)


class OptimizationDomain(EmbeddedResource):
pass
Expand Down
46 changes: 46 additions & 0 deletions tests/test_base_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,3 +911,49 @@ def test_initial_portfolio_metrics(self, reqmock):
text='{"num": 1.0}')
f = OptimizationView(id='abc123').initial_metrics()
assert f.num == 1.0

sensitivity_text = """
{"sensitivities": [
{"normalized_interquartile_range": 0.25, "min": 0.0, "normalized_standard_deviation": 0.29,
"initial_share": 0.18, "hist": { "0.05": 5, "0.1": 0, "0.15": 0, "0.2": 0, "0.25": 0, "0.3": 9, "0.35": 0,
"0.4": 0, "0.45": 0, "0.5": 3, "0.55": 0, "0.6": 5, "0.65": 0, "0.7": 3, "0.75": 0, "0.8": 3, "0.85": 2,
"0.9": 4, "0.95": 2, "1.0": 64}, "mean": 0.81, "max": 1.0, "ref_id": "layer2"
},
{ "normalized_interquartile_range": 0.71, "min": 0.0, "normalized_standard_deviation": 0.35,
"initial_share": 0.08, "hist": {"0.05": 36, "0.1": 0, "0.15": 8, "0.2": 1, "0.25": 1, "0.3": 3,
"0.35": 2, "0.4": 3, "0.45": 0, "0.5": 0, "0.55": 8, "0.6": 0, "0.65": 12, "0.7": 0, "0.75": 1,
"0.8": 10, "0.85": 0, "0.9": 7, "0.95": 0, "1.0": 8}, "mean": 0.37, "max": 1.0, "ref_id": "layer1" }
] } """

required_attributes = ['hist', 'min', 'max', 'mean', 'normalized_standard_deviation',
'normalized_interquartile_range', 'ref_id']

@pytest.mark.parametrize("get_string, candidates, expected_len", [
('https://api/optimization_views/abc_id/sensitivity_analysis', [], 2),
('https://api/optimization_views/abc_id/sensitivity_analysis?candidates=0,2,5', [0, 2, 5], 2),
('https://api/optimization_views/abc_id/sensitivity_analysis?candidates=0,2,5', [0, 2, 5, 'a', 1.1, -2], 2)
])
def test_sensitivity_analysis_with_candidates(self, reqmock, candidates, expected_len, get_string):
# list of candidates should be translated to the correct api request
reqmock.get('https://api/optimization_views/abc_id',
status_code=200, text='{"id":"abc_id"}')
ov = OptimizationView.retrieve('abc_id')
reqmock.get(get_string,
status_code=200, text=TestOptimizationResource.sensitivity_text)
r = ov.sensitivity_analysis(candidates=candidates)
assert hasattr(r, 'sensitivities')
assert len(r.sensitivities) == expected_len
for i in range(len(r.sensitivities)):
for attribute in TestOptimizationResource.required_attributes:
assert hasattr(r.sensitivities[i], attribute)

def test_sensitivity_analysis_empty(self, reqmock):
# result can be an empty list
reqmock.get('https://api/optimization_views/abc_id',
status_code=200, text='{"id":"abc_id"}')
ov = OptimizationView.retrieve('abc_id')
reqmock.get('https://api/optimization_views/abc_id/sensitivity_analysis',
status_code=200, text='{"sensitivities": []}')
r = ov.sensitivity_analysis()
assert hasattr(r, 'sensitivities')
assert len(r.sensitivities) == 0

0 comments on commit 0593d5b

Please sign in to comment.