Skip to content

Commit

Permalink
Merge branch 'main' into gaugup/Replace_serialize_json_safe
Browse files Browse the repository at this point in the history
Signed-off-by: Gaurav Gupta <gaugup@microsoft.com>
  • Loading branch information
gaugup committed Jul 7, 2022
2 parents 978202f + 9e0693b commit a32c9a8
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 346 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ def node_to_dict(df, tree, nodeid, categories, json,
metric_name = metric_to_display_name[metric]
is_error_metric = metric in error_metrics
if SPLIT_FEATURE in tree:
node_name = feature_names[tree[SPLIT_FEATURE]]
node_name = str(feature_names[tree[SPLIT_FEATURE]])
else:
node_name = None
json.append(get_json_node(arg, condition, error, nodeid, method,
Expand Down
7 changes: 3 additions & 4 deletions erroranalysis/erroranalysis/report/error_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import json
import uuid

from raiutils.data_processing import serialize_json_safe

_ErrorReportVersion1 = '1.0'
_ErrorReportVersion2 = '2.0'
_ErrorReportVersion3 = '3.0'
Expand Down Expand Up @@ -35,10 +37,7 @@ def json_converter(obj):
if isinstance(obj, ErrorReport):
rdict = obj.__dict__
return rdict
try:
return obj.to_json()
except AttributeError:
return obj.__dict__
return serialize_json_safe(obj)


def as_error_report(error_dict):
Expand Down
2 changes: 1 addition & 1 deletion erroranalysis/erroranalysis/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
name = 'erroranalysis'
_major = '0'
_minor = '3'
_patch = '3'
_patch = '4'
version = '{}.{}.{}'.format(_major, _minor, _patch)
1 change: 1 addition & 0 deletions erroranalysis/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pandas>=0.25.1
scipy>=1.4.1
scikit-learn>=0.22.1
lightgbm>=2.0.11
raiutils>=0.1.0
12 changes: 12 additions & 0 deletions erroranalysis/tests/test_error_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ def test_error_report_iris(self, alter_feature_names):
categorical_features,
expect_user_warnings=alter_feature_names)

def test_error_report_iris_numpy_int64_features(self):
X_train, X_test, y_train, y_test, _, _ = create_iris_data()
# Test with numpy feature indexes instead of string feature names
feature_names = range(0, X_train.shape[1])
feature_names = [np.int64(i) for i in feature_names]
models = create_models_classification(X_train, y_train)

for model in models:
categorical_features = []
run_error_analyzer(model, X_test, y_test, feature_names,
categorical_features)

def test_error_report_cancer(self):
X_train, X_test, y_train, y_test, feature_names, _ = \
create_cancer_data()
Expand Down
29 changes: 14 additions & 15 deletions raiwidgets/raiwidgets/error_analysis_dashboard_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
from erroranalysis._internal.error_analyzer import (ModelAnalyzer,
PredictionsAnalyzer)
from erroranalysis._internal.utils import is_spark
from raiutils.data_processing import serialize_json_safe
from raiutils.data_processing import convert_to_list, serialize_json_safe
from raiutils.models import is_classifier
from responsibleai._input_processing import _convert_to_list
from responsibleai._interfaces import ErrorAnalysisData

from .constants import ModelTask
Expand Down Expand Up @@ -183,14 +182,14 @@ def setup_pyspark(self, model, dataset, true_y, classes,
predicted_y = self.predicted_y_to_list(predicted_y)
true_y = pd_sample[true_y]
pd_sample = pd_sample[features]
list_dataset = _convert_to_list(pd_sample)
list_dataset = convert_to_list(pd_sample)
self.setup_visualization_input(classes, predicted_y,
list_dataset, true_y, features)

def setup_visualization_input(self, classes, predicted_y,
list_dataset, true_y, features):
if classes is not None:
classes = _convert_to_list(classes)
classes = convert_to_list(classes)
self.dashboard_input[
ExplanationDashboardInterface.CLASS_NAMES
] = classes
Expand Down Expand Up @@ -222,7 +221,7 @@ def setup_visualization_input(self, classes, predicted_y,
] = serialize_json_safe(list_dataset)

if true_y is not None and len(true_y) == row_length:
list_true_y = _convert_to_list(true_y)
list_true_y = convert_to_list(true_y)
# If classes specified, convert true_y to numeric representation
if classes is not None and list_true_y[0] in class_to_index:
for i in range(len(list_true_y)):
Expand All @@ -232,7 +231,7 @@ def setup_visualization_input(self, classes, predicted_y,
] = list_true_y

if features is not None:
features = _convert_to_list(features)
features = convert_to_list(features)
if feature_length is not None and len(features) != feature_length:
raise ValueError("Feature vector length mismatch:"
" feature names length differs"
Expand Down Expand Up @@ -277,7 +276,7 @@ def setup_local(self, explanation, model, dataset, true_y, classes,
self._dataframeColumns = dataset.columns
self._dfdtypes = dataset.dtypes
try:
list_dataset = _convert_to_list(dataset)
list_dataset = convert_to_list(dataset)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand Down Expand Up @@ -306,7 +305,7 @@ def setup_local(self, explanation, model, dataset, true_y, classes,
" for given dataset type,"
" inner error: {}".format(ex_str))
try:
probability_y = _convert_to_list(probability_y)
probability_y = convert_to_list(probability_y)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand Down Expand Up @@ -407,7 +406,7 @@ def compute_predicted_y(self, model, dataset):

def predicted_y_to_list(self, predicted_y):
try:
predicted_y = _convert_to_list(predicted_y)
predicted_y = convert_to_list(predicted_y)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand Down Expand Up @@ -447,13 +446,13 @@ def input_explanation_data(self, list_dataset, classes):

if local_explanation is not None:
try:
local_explanation["scores"] = _convert_to_list(
local_explanation["scores"] = convert_to_list(
local_explanation["scores"])
if np.shape(local_explanation["scores"])[-1] > 1000:
raise ValueError("Exceeds maximum number of features for "
"visualization (1000). Please regenerate"
" the explanation using fewer features.")
local_explanation["intercept"] = _convert_to_list(
local_explanation["intercept"] = convert_to_list(
local_explanation["intercept"])
# We can ignore perf explanation data.
# Note if it is added back at any point,
Expand Down Expand Up @@ -490,10 +489,10 @@ def input_explanation_data(self, list_dataset, classes):
"local explanations dimension")
if local_explanation is None and global_explanation is not None:
try:
global_explanation["scores"] = _convert_to_list(
global_explanation["scores"] = convert_to_list(
global_explanation["scores"])
if 'intercept' in global_explanation:
global_explanation["intercept"] = _convert_to_list(
global_explanation["intercept"] = convert_to_list(
global_explanation["intercept"])
self.dashboard_input[
ExplanationDashboardInterface.GLOBAL_EXPLANATION
Expand Down Expand Up @@ -583,10 +582,10 @@ def on_predict(self, data):
data = data.astype(dict(self._dfdtypes))
if (self._is_classifier):
model_pred_proba = self._model.predict_proba(data)
prediction = _convert_to_list(model_pred_proba)
prediction = convert_to_list(model_pred_proba)
else:
model_predict = self._model.predict(data)
prediction = _convert_to_list(model_predict)
prediction = convert_to_list(model_predict)
return {
WidgetRequestResponseConstants.DATA: prediction
}
Expand Down
31 changes: 15 additions & 16 deletions raiwidgets/raiwidgets/explanation_dashboard_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import numpy as np
import pandas as pd

from raiutils.data_processing import serialize_json_safe
from raiutils.data_processing import convert_to_list, serialize_json_safe
from raiutils.models import is_classifier
from responsibleai._input_processing import _convert_to_list

from .constants import ErrorMessages
from .error_handling import _format_exception
Expand Down Expand Up @@ -94,7 +93,7 @@ class and for the regression case a method of predict()
self._dataframeColumns = dataset.columns
self._dfdtypes = dataset.dtypes
try:
list_dataset = _convert_to_list(dataset, EXP_VIZ_ERR_MSG)
list_dataset = convert_to_list(dataset, EXP_VIZ_ERR_MSG)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand All @@ -109,7 +108,7 @@ class and for the regression case a method of predict()
ex_str)
raise ValueError(msg)
try:
predicted_y = _convert_to_list(predicted_y, EXP_VIZ_ERR_MSG)
predicted_y = convert_to_list(predicted_y, EXP_VIZ_ERR_MSG)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand Down Expand Up @@ -147,13 +146,13 @@ class and for the regression case a method of predict()
if true_y is not None and len(true_y) == row_length:
self.dashboard_input[
ExplanationDashboardInterface.TRUE_Y
] = _convert_to_list(true_y, EXP_VIZ_ERR_MSG)
] = convert_to_list(true_y, EXP_VIZ_ERR_MSG)

if local_explanation is not None:
try:
local_explanation["scores"] = _convert_to_list(
local_explanation["scores"] = convert_to_list(
local_explanation["scores"], EXP_VIZ_ERR_MSG)
local_explanation["intercept"] = _convert_to_list(
local_explanation["intercept"] = convert_to_list(
local_explanation["intercept"], EXP_VIZ_ERR_MSG)
# We can ignore perf explanation data.
# Note if it is added back at any point,
Expand Down Expand Up @@ -185,10 +184,10 @@ class and for the regression case a method of predict()
" length differs from dataset")
if local_explanation is None and global_explanation is not None:
try:
global_explanation["scores"] = _convert_to_list(
global_explanation["scores"] = convert_to_list(
global_explanation["scores"], EXP_VIZ_ERR_MSG)
if 'intercept' in global_explanation:
global_explanation["intercept"] = _convert_to_list(
global_explanation["intercept"] = convert_to_list(
global_explanation["intercept"], EXP_VIZ_ERR_MSG)
self.dashboard_input[
ExplanationDashboardInterface.GLOBAL_EXPLANATION
Expand All @@ -213,7 +212,7 @@ class and for the regression case a method of predict()
and explanation.features is not None:
features = explanation.features
if features is not None:
features = _convert_to_list(features, EXP_VIZ_ERR_MSG)
features = convert_to_list(features, EXP_VIZ_ERR_MSG)
if feature_length is not None and len(features) != feature_length:
raise ValueError("Feature vector length mismatch:"
" feature names length differs"
Expand All @@ -227,7 +226,7 @@ class and for the regression case a method of predict()
and explanation.classes is not None:
classes = explanation.classes
if classes is not None:
classes = _convert_to_list(classes, EXP_VIZ_ERR_MSG)
classes = convert_to_list(classes, EXP_VIZ_ERR_MSG)
if local_dim is not None and len(classes) != local_dim[0]:
raise ValueError("Class vector length mismatch:"
"class names length differs from"
Expand All @@ -244,8 +243,8 @@ class and for the regression case a method of predict()
" for given dataset type,"
" inner error: {}".format(ex_str))
try:
probability_y = _convert_to_list(probability_y,
EXP_VIZ_ERR_MSG)
probability_y = convert_to_list(probability_y,
EXP_VIZ_ERR_MSG)
except Exception as ex:
ex_str = _format_exception(ex)
raise ValueError(
Expand All @@ -261,11 +260,11 @@ def on_predict(self, data):
data = pd.DataFrame(data, columns=self._dataframeColumns)
data = data.astype(dict(self._dfdtypes))
if (self._is_classifier):
prediction = _convert_to_list(
prediction = convert_to_list(
self._model.predict_proba(data), EXP_VIZ_ERR_MSG)
else:
prediction = _convert_to_list(self._model.predict(data),
EXP_VIZ_ERR_MSG)
prediction = convert_to_list(self._model.predict(data),
EXP_VIZ_ERR_MSG)
return {
WidgetRequestResponseConstants.DATA: prediction
}
Expand Down
18 changes: 9 additions & 9 deletions raiwidgets/raiwidgets/fairness_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import numpy as np
from flask import jsonify, request

from responsibleai._input_processing import (_convert_to_list,
_convert_to_string_list_dict)
from raiutils.data_processing import (convert_to_list,
convert_to_string_list_dict)

from .dashboard import Dashboard
from .fairness_metric_calculation import FairnessMetricModule
Expand Down Expand Up @@ -57,15 +57,15 @@ def __init__(
if sensitive_features is None or y_true is None or y_pred is None:
raise ValueError("Required parameters not provided")

model_dict = _convert_to_string_list_dict("Model {0}",
y_pred,
y_true)
sf_dict = _convert_to_string_list_dict("Sensitive Feature {0}",
sensitive_features,
y_true)
model_dict = convert_to_string_list_dict("Model {0}",
y_pred,
y_true)
sf_dict = convert_to_string_list_dict("Sensitive Feature {0}",
sensitive_features,
y_true)

# Make sure that things are as the TS layer expects
self._y_true = _convert_to_list(y_true)
self._y_true = convert_to_list(y_true)
self._y_pred = list(model_dict.values())
# Note transpose in the following
dataset = (np.array(list(sf_dict.values())).T).tolist()
Expand Down
6 changes: 3 additions & 3 deletions raiwidgets/raiwidgets/responsibleai_dashboard_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import pandas as pd

from erroranalysis._internal.constants import ModelTask, display_name_to_metric
from raiutils.data_processing import convert_to_list
from raiutils.models import is_classifier
from responsibleai import RAIInsights
from responsibleai._input_processing import _convert_to_list
from responsibleai._internal.constants import ManagerNames
from responsibleai.exceptions import UserConfigValidationException

Expand Down Expand Up @@ -101,10 +101,10 @@ def on_predict(self, data):
data = pd.DataFrame(
data, columns=self.dashboard_input.dataset.feature_names)
if (self._is_classifier):
prediction = _convert_to_list(
prediction = convert_to_list(
self._analysis.model.predict_proba(data), EXP_VIZ_ERR_MSG)
else:
prediction = _convert_to_list(
prediction = convert_to_list(
self._analysis.model.predict(data), EXP_VIZ_ERR_MSG)
return {
WidgetRequestResponseConstants.data: prediction
Expand Down
2 changes: 1 addition & 1 deletion raiwidgets/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ rai-core-flask==0.3.0
itsdangerous==2.0.1
scikit-learn>=0.22.1
lightgbm>=2.0.11
erroranalysis>=0.3.2
erroranalysis>=0.3.3
fairlearn>=0.7.0
raiutils>=0.1.0
2 changes: 1 addition & 1 deletion responsibleai/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dice-ml>=0.8,<0.9
econml~=0.13.0
jsonschema
erroranalysis>=0.3.2
erroranalysis>=0.3.3
interpret-community>=0.26.0
lightgbm>=2.0.11
numpy>=1.17.2
Expand Down
Loading

0 comments on commit a32c9a8

Please sign in to comment.