Skip to content

Commit fe7fa6a

Browse files
committed
convert nan to null
1 parent 3fa92e8 commit fe7fa6a

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11

22
Unreleased
33
----------
4-
-
4+
**Bugfixes**
5+
- Convert NaN values to null (None) when calling `microanalytic_score.execute_module_step`.
56

67
v1.2.2 (2019-8-21)
78
------------------

src/sasctl/_services/microanalytic_score.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import re
1010
from collections import OrderedDict
11+
from math import isnan
1112

1213
import six
1314

@@ -127,9 +128,17 @@ def execute_module_step(self, module, step, return_dict=True, **kwargs):
127128
elif type_name == 'int64':
128129
kwargs[k] = int(kwargs[k])
129130

130-
131131
body = {'inputs': [{'name': k, 'value': v}
132132
for k, v in six.iteritems(kwargs)]}
133+
134+
# Convert NaN to None (null) before calling MAS
135+
for input in body['inputs']:
136+
try:
137+
if isnan(input['value']):
138+
input['value'] = None
139+
except TypeError:
140+
pass
141+
133142
r = self.post('/modules/{}/steps/{}'.format(module, step), json=body)
134143

135144
# Convert list of name/value pair dictionaries to single dict

tests/unit/test_pymas.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,42 @@ def domath(a, b):
504504
result = package.code()
505505
assert target.lstrip('\n') == result
506506
assert '\t' not in result
507+
508+
509+
def test_bugfix_27():
510+
"""NaN values should be converted to null before being sent to MAS
511+
512+
https://github.com/sassoftware/python-sasctl/issues/27
513+
"""
514+
515+
import io
516+
from sasctl.core import RestObj
517+
from sasctl.services import microanalytic_score as mas
518+
pd = pytest.importorskip('pandas')
519+
520+
df = pd.read_csv(io.StringIO('\n'.join([
521+
'BAD,LOAN,MORTDUE,VALUE,REASON,JOB,YOJ,DEROG,DELINQ,CLAGE,NINQ,CLNO,DEBTINC',
522+
'0,1.0,1100.0,25860.0,39025.0,HomeImp,Other,10.5,0.0,0.0,94.36666667,1.0,9.0,',
523+
'1,1.0,1300.0,70053.0,68400.0,HomeImp,Other,7.0,0.0,2.0,121.8333333,0.0,14.0,'
524+
])))
525+
526+
with mock.patch('sasctl._services.microanalytic_score.MicroAnalyticScore.get_module') as get_module:
527+
get_module.return_value = RestObj({
528+
'name': 'Mock Module',
529+
'id': 'mockmodule'
530+
})
531+
with mock.patch('sasctl._services.microanalytic_score.MicroAnalyticScore.post') as post:
532+
x = df.iloc[0, :]
533+
mas.execute_module_step('module', 'step', **x)
534+
535+
# Ensure we're passing NaN to execute_module_step
536+
assert pd.isna(x['DEBTINC'])
537+
538+
# Make sure the value has been converted to None before being serialized to JSON.
539+
# This ensures that the JSON value will be null.
540+
json = post.call_args[1]['json']
541+
inputs = json['inputs']
542+
debtinc = [i for i in inputs if i['name'] == 'DEBTINC'].pop()
543+
assert debtinc['value'] is None
544+
545+

0 commit comments

Comments
 (0)