erroranalysis 0.4.2__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {erroranalysis-0.4.2/erroranalysis.egg-info → erroranalysis-0.4.4}/PKG-INFO +3 -2
  2. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/README.md +1 -1
  3. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/constants.py +13 -0
  4. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/surrogate_error_tree.py +4 -5
  5. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/analyzer/error_analyzer.py +36 -17
  6. erroranalysis-0.4.4/erroranalysis/error_correlation_methods/__init__.py +9 -0
  7. erroranalysis-0.4.4/erroranalysis/error_correlation_methods/ebm.py +39 -0
  8. erroranalysis-0.4.4/erroranalysis/error_correlation_methods/gbm.py +39 -0
  9. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/version.py +1 -1
  10. {erroranalysis-0.4.2 → erroranalysis-0.4.4/erroranalysis.egg-info}/PKG-INFO +3 -2
  11. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis.egg-info/SOURCES.txt +3 -0
  12. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis.egg-info/requires.txt +1 -1
  13. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/requirements.txt +1 -1
  14. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/setup.py +1 -0
  15. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_cohort_filter.py +42 -41
  16. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_error_report.py +19 -9
  17. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_importances.py +53 -21
  18. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_matrix_filter.py +27 -26
  19. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_surrogate_error_tree.py +13 -1
  20. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/LICENSE +0 -0
  21. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/MANIFEST.in +0 -0
  22. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/__init__.py +0 -0
  23. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/__init__.py +0 -0
  24. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/cohort_filter.py +0 -0
  25. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/error_analyzer/__init__.py +0 -0
  26. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/error_report/__init__.py +0 -0
  27. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/matrix_filter.py +0 -0
  28. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/metrics.py +0 -0
  29. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/process_categoricals.py +0 -0
  30. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/utils.py +0 -0
  31. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/_internal/version_checker.py +0 -0
  32. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/analyzer/__init__.py +0 -0
  33. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/report/__init__.py +0 -0
  34. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis/report/error_report.py +0 -0
  35. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis.egg-info/dependency_links.txt +0 -0
  36. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/erroranalysis.egg-info/top_level.txt +0 -0
  37. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/setup.cfg +0 -0
  38. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_metrics.py +0 -0
  39. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_pyspark_surrogate_error_tree.py +0 -0
  40. {erroranalysis-0.4.2 → erroranalysis-0.4.4}/tests/test_root_stats.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: erroranalysis
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Core error analysis APIs
5
5
  Home-page: https://github.com/microsoft/responsible-ai-widgets
6
6
  Author: Roman Lutz, Ilya Matiach, Ke Xu
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3.6
9
9
  Classifier: Programming Language :: Python :: 3.7
10
10
  Classifier: Programming Language :: Python :: 3.8
11
11
  Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: License :: OSI Approved :: MIT License
13
14
  Classifier: Operating System :: OS Independent
14
15
  Classifier: Development Status :: 3 - Alpha
@@ -18,7 +19,7 @@ License-File: LICENSE
18
19
 
19
20
  # Error Analysis SDK for Python
20
21
 
21
- ### This package has been tested with Python 3.6, 3.7, 3.8 and 3.9
22
+ ### This package has been tested with Python 3.6, 3.7, 3.8, 3.9 and 3.10
22
23
 
23
24
  The error analysis sdk enables users to get a deeper understanding of machine learning model errors. When evaluating a machine learning model, aggregate accuracy is not sufficient and single-score evaluation may hide important conditions of inaccuracies. Use Error Analysis to identify cohorts with higher error rates and diagnose the root causes behind these errors.
24
25
 
@@ -1,6 +1,6 @@
1
1
  # Error Analysis SDK for Python
2
2
 
3
- ### This package has been tested with Python 3.6, 3.7, 3.8 and 3.9
3
+ ### This package has been tested with Python 3.6, 3.7, 3.8, 3.9 and 3.10
4
4
 
5
5
  The error analysis sdk enables users to get a deeper understanding of machine learning model errors. When evaluating a machine learning model, aggregate accuracy is not sufficient and single-score evaluation may hide important conditions of inaccuracies. Use Error Analysis to identify cohorts with higher error rates and diagnose the root causes behind these errors.
6
6
 
@@ -96,6 +96,9 @@ class Metrics(str, Enum):
96
96
  FALSE_POSITIVE_RATE = 'false_positive_rate'
97
97
  FALSE_NEGATIVE_RATE = 'false_negative_rate'
98
98
  SELECTION_RATE = 'selection_rate'
99
+ MEAN_AVERAGE_PRECISION = 'mean_average_precision'
100
+ AVERAGE_PRECISION = 'average_precision'
101
+ AVERAGE_RECALL = 'average_recall'
99
102
 
100
103
 
101
104
  class MetricKeys(str, Enum):
@@ -121,6 +124,16 @@ class TreeNode(str, Enum):
121
124
  METRIC_VALUE = MetricKeys.METRIC_VALUE.value
122
125
 
123
126
 
127
+ class ErrorCorrelationMethods(str, Enum):
128
+ """Provide the supported error correlation methods.
129
+
130
+ The supported methods are 'mutual_info', 'ebm' and 'gbm_shap'.
131
+ """
132
+ MUTUAL_INFO = 'mutual_info'
133
+ EBM = 'ebm'
134
+ GBM_SHAP = 'gbm_shap'
135
+
136
+
124
137
  metric_to_display_name = {
125
138
  Metrics.ACCURACY_SCORE: 'Accuracy score',
126
139
  Metrics.MEAN_PREDICTION: 'Mean prediction',
@@ -292,7 +292,6 @@ def get_surrogate_booster_local(filtered_df, analyzer, is_model_analyzer,
292
292
  scored dataset.
293
293
  :rtype: (Booster, pandas.DataFrame, (list[str], list[int]))
294
294
  """
295
- row_index = filtered_df[ROW_INDEX]
296
295
  true_y = filtered_df[TRUE_Y]
297
296
  dropped_cols = [TRUE_Y, ROW_INDEX]
298
297
  if not is_model_analyzer:
@@ -303,7 +302,7 @@ def get_surrogate_booster_local(filtered_df, analyzer, is_model_analyzer,
303
302
  if is_pandas:
304
303
  true_y = true_y.to_numpy()
305
304
  else:
306
- input_data = input_data.to_numpy()
305
+ input_data = input_data.to_numpy(copy=True)
307
306
  if is_model_analyzer:
308
307
  pred_y = analyzer.model.predict(input_data)
309
308
  if analyzer.model_task == ModelTask.CLASSIFICATION:
@@ -317,7 +316,7 @@ def get_surrogate_booster_local(filtered_df, analyzer, is_model_analyzer,
317
316
  if not isinstance(true_y, np.ndarray):
318
317
  true_y = np.array(true_y)
319
318
  if is_pandas:
320
- input_data = input_data.to_numpy()
319
+ input_data = input_data.to_numpy(copy=True)
321
320
 
322
321
  if analyzer.categorical_features:
323
322
  # Inplace replacement of columns
@@ -330,7 +329,7 @@ def get_surrogate_booster_local(filtered_df, analyzer, is_model_analyzer,
330
329
  else:
331
330
  string_indexed_data = analyzer.string_indexed_data
332
331
  for idx, c_i in enumerate(analyzer.categorical_indexes):
333
- input_data[:, c_i] = string_indexed_data[row_index, idx]
332
+ input_data[:, c_i] = string_indexed_data[:, idx]
334
333
  dataset_sub_features = input_data[:, indexes]
335
334
 
336
335
  categorical_info = get_categorical_info(analyzer,
@@ -605,7 +604,7 @@ def filter_to_used_features(df, tree):
605
604
  """
606
605
  features = tree[CACHED_SUBTREE_FEATURES]
607
606
  features = features.union({PRED_Y, TRUE_Y, DIFF})
608
- return df[features]
607
+ return df[list(features)]
609
608
 
610
609
 
611
610
  def cache_subtree_features(tree, feature_names, parent=None):
@@ -10,7 +10,8 @@ import pandas as pd
10
10
  from sklearn.feature_selection import (mutual_info_classif,
11
11
  mutual_info_regression)
12
12
 
13
- from erroranalysis._internal.constants import (MatrixParams, Metrics,
13
+ from erroranalysis._internal.constants import (ErrorCorrelationMethods,
14
+ MatrixParams, Metrics,
14
15
  ModelTask, RootKeys,
15
16
  metric_to_display_name)
16
17
  from erroranalysis._internal.matrix_filter import \
@@ -25,11 +26,14 @@ from erroranalysis._internal.surrogate_error_tree import \
25
26
  compute_error_tree_on_dataset as _compute_error_tree_on_dataset
26
27
  from erroranalysis._internal.utils import generate_random_unique_indexes
27
28
  from erroranalysis._internal.version_checker import check_pandas_version
29
+ from erroranalysis.error_correlation_methods import (
30
+ compute_ebm_global_importance, compute_gbm_global_importance)
28
31
  from erroranalysis.report import ErrorReport
29
32
 
30
33
  BIN_THRESHOLD = MatrixParams.BIN_THRESHOLD
31
34
  IMPORTANCES_THRESHOLD = 50000
32
35
  ROOT_COVERAGE = 100
36
+ MUTUAL_INFO = ErrorCorrelationMethods.MUTUAL_INFO.value
33
37
 
34
38
 
35
39
  class BaseAnalyzer(ABC):
@@ -372,7 +376,8 @@ class BaseAnalyzer(ABC):
372
376
  num_leaves=None,
373
377
  min_child_samples=None,
374
378
  compute_importances=False,
375
- compute_root_stats=False):
379
+ compute_root_stats=False,
380
+ error_correlation_method=MUTUAL_INFO):
376
381
  """Creates the error analysis ErrorReport.
377
382
 
378
383
  The ErrorReport contains the importances, heatmap and tree view json.
@@ -409,7 +414,7 @@ class BaseAnalyzer(ABC):
409
414
  None)
410
415
  importances = None
411
416
  if compute_importances:
412
- importances = self.compute_importances()
417
+ importances = self.compute_importances(error_correlation_method)
413
418
  root_stats = None
414
419
  if compute_root_stats:
415
420
  root_stats = self.compute_root_stats()
@@ -419,7 +424,7 @@ class BaseAnalyzer(ABC):
419
424
  importances=importances,
420
425
  root_stats=root_stats)
421
426
 
422
- def compute_importances(self):
427
+ def compute_importances(self, error_correlation_method=MUTUAL_INFO):
423
428
  """Compute the importances or correlation between features and error.
424
429
 
425
430
  Computes the feature importances or the correlation between
@@ -428,6 +433,8 @@ class BaseAnalyzer(ABC):
428
433
  uses the scikit-learn methods mutual_info_classif for classification
429
434
  and mutual_info_regression for regression tasks.
430
435
 
436
+ :param error_correlation_method: Method to compute error correlation.
437
+ :type error_correlation_method: str
431
438
  :return: The computed importances or correlation between the
432
439
  features and error.
433
440
  :rtype: list[float]
@@ -435,7 +442,7 @@ class BaseAnalyzer(ABC):
435
442
  input_data = self.dataset
436
443
  diff = self.get_diff()
437
444
  if isinstance(self.dataset, pd.DataFrame):
438
- input_data = input_data.to_numpy()
445
+ input_data = input_data.to_numpy(copy=True)
439
446
  if self.categorical_features:
440
447
  # Inplace replacement of columns
441
448
  indexes = self.categorical_indexes
@@ -451,24 +458,29 @@ class BaseAnalyzer(ABC):
451
458
  input_data = input_data[indexes]
452
459
  diff = diff[indexes]
453
460
  try:
454
- importances = self._compute_mutual_info(input_data, diff)
461
+ importances = self._compute_error_correlation(
462
+ input_data, diff, error_correlation_method)
455
463
  except ValueError:
456
464
  # Impute input_data if it contains NaNs, infinity or a value too
457
465
  # large for dtype('float64')
458
466
  input_data = np.nan_to_num(input_data)
459
- importances = self._compute_mutual_info(input_data, diff)
467
+ importances = self._compute_error_correlation(
468
+ input_data, diff, error_correlation_method)
460
469
  return importances
461
470
 
462
- def _compute_mutual_info(self, input_data, diff):
463
- """Compute the mutual information between the features and error.
471
+ def _compute_error_correlation(self, input_data, diff,
472
+ error_correlation_method):
473
+ """Compute the correlation between the features and error.
464
474
 
465
- :param input_data: The input data to compute the mutual information
475
+ :param input_data: The input data to compute the error correlation
466
476
  on.
467
477
  :type input_data: numpy.ndarray
468
478
  :param diff: The difference between the label and prediction
469
479
  columns.
470
480
  :type diff: numpy.ndarray
471
- :return: The computed mutual information between the features and
481
+ :param error_correlation_method: Method to compute error correlation.
482
+ :type error_correlation_method: str
483
+ :return: The computed error correlation between the features and
472
484
  error.
473
485
  :rtype: list[float]
474
486
  """
@@ -476,13 +488,20 @@ class BaseAnalyzer(ABC):
476
488
  if input_data.shape[0] == 1:
477
489
  input_data = np.concatenate((input_data, input_data))
478
490
  diff = np.concatenate((diff, diff))
479
- n_neighbors = min(3, input_data.shape[0] - 1)
480
- if self._model_task == ModelTask.CLASSIFICATION:
481
- return mutual_info_classif(
482
- input_data, diff, n_neighbors=n_neighbors).tolist()
491
+ if error_correlation_method == ErrorCorrelationMethods.MUTUAL_INFO:
492
+ n_neighbors = min(3, input_data.shape[0] - 1)
493
+ if self._model_task == ModelTask.CLASSIFICATION:
494
+ return mutual_info_classif(
495
+ input_data, diff, n_neighbors=n_neighbors).tolist()
496
+ else:
497
+ return mutual_info_regression(
498
+ input_data, diff, n_neighbors=n_neighbors).tolist()
499
+ elif error_correlation_method == ErrorCorrelationMethods.EBM:
500
+ return compute_ebm_global_importance(
501
+ input_data, diff, self._model_task)
483
502
  else:
484
- return mutual_info_regression(
485
- input_data, diff, n_neighbors=n_neighbors).tolist()
503
+ return compute_gbm_global_importance(
504
+ input_data, diff, self._model_task, self._categorical_indexes)
486
505
 
487
506
  def compute_root_stats(self):
488
507
  """Compute the root all data statistics.
@@ -0,0 +1,9 @@
1
+ # Copyright (c) Microsoft Corporation
2
+ # Licensed under the MIT License.
3
+
4
+ """Module for defining error correlation methods."""
5
+
6
+ from .ebm import compute_ebm_global_importance
7
+ from .gbm import compute_gbm_global_importance
8
+
9
+ __all__ = ["compute_ebm_global_importance", "compute_gbm_global_importance"]
@@ -0,0 +1,39 @@
1
+ # Copyright (c) Microsoft Corporation
2
+ # Licensed under the MIT License.
3
+
4
+ """Defines the error correlation computation using an EBM."""
5
+
6
+ try:
7
+ from interpret.glassbox import (ExplainableBoostingClassifier,
8
+ ExplainableBoostingRegressor)
9
+ ebm_installed = True
10
+ except ImportError:
11
+ ebm_installed = False
12
+
13
+ from erroranalysis._internal.constants import ModelTask
14
+
15
+
16
+ def compute_ebm_global_importance(input_data, diff, model_task):
17
+ """Compute global importance score for EBM between the features and error.
18
+ :param input_data: The input data to compute the EBM global importance
19
+ score on.
20
+ :type input_data: numpy.ndarray
21
+ :param diff: The difference between the label and prediction
22
+ columns.
23
+ :type diff: numpy.ndarray
24
+ :param model_task: The model task.
25
+ :type model_task: str
26
+ :return: The computed EBM global importance score between the features and
27
+ error.
28
+ :rtype: list[float]
29
+ """
30
+ if not ebm_installed:
31
+ raise ImportError('EBM is not installed. Please install it by running '
32
+ '"pip install interpret"')
33
+ if model_task == ModelTask.CLASSIFICATION:
34
+ model = ExplainableBoostingClassifier(interactions=0)
35
+ else:
36
+ model = ExplainableBoostingRegressor(interactions=0)
37
+ model.fit(input_data, diff)
38
+ ebm_importances = model.term_importances()
39
+ return ebm_importances.tolist()
@@ -0,0 +1,39 @@
1
+ # Copyright (c) Microsoft Corporation
2
+ # Licensed under the MIT License.
3
+
4
+ """Defines the error correlation computation using LightGBM with TreeShap."""
5
+
6
+ import numpy as np
7
+ import shap
8
+ from lightgbm import LGBMClassifier, LGBMRegressor
9
+
10
+ from erroranalysis._internal.constants import ModelTask
11
+
12
+
13
+ def compute_gbm_global_importance(input_data, diff, model_task,
14
+ categorical_indexes):
15
+ """Compute global importance score for EBM between the features and error.
16
+ :param input_data: The input data to compute the EBM global importance
17
+ score on.
18
+ :type input_data: numpy.ndarray
19
+ :param diff: The difference between the label and prediction
20
+ columns.
21
+ :type diff: numpy.ndarray
22
+ :param model_task: The model task.
23
+ :type model_task: str
24
+ :return: The computed EBM global importance score between the features and
25
+ error.
26
+ :rtype: list[float]
27
+ """
28
+ is_classification = model_task == ModelTask.CLASSIFICATION
29
+ if is_classification:
30
+ model = LGBMClassifier()
31
+ else:
32
+ model = LGBMRegressor()
33
+ model.fit(input_data, diff, categorical_feature=categorical_indexes)
34
+ explainer = shap.TreeExplainer(model)
35
+ shap_values = explainer.shap_values(input_data)
36
+ shap_mean_abs = np.abs(shap_values).mean(axis=0)
37
+ if is_classification:
38
+ shap_mean_abs = shap_mean_abs.mean(axis=0)
39
+ return shap_mean_abs.tolist()
@@ -4,5 +4,5 @@
4
4
  name = 'erroranalysis'
5
5
  _major = '0'
6
6
  _minor = '4'
7
- _patch = '2'
7
+ _patch = '4'
8
8
  version = '{}.{}.{}'.format(_major, _minor, _patch)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: erroranalysis
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Core error analysis APIs
5
5
  Home-page: https://github.com/microsoft/responsible-ai-widgets
6
6
  Author: Roman Lutz, Ilya Matiach, Ke Xu
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3.6
9
9
  Classifier: Programming Language :: Python :: 3.7
10
10
  Classifier: Programming Language :: Python :: 3.8
11
11
  Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: License :: OSI Approved :: MIT License
13
14
  Classifier: Operating System :: OS Independent
14
15
  Classifier: Development Status :: 3 - Alpha
@@ -18,7 +19,7 @@ License-File: LICENSE
18
19
 
19
20
  # Error Analysis SDK for Python
20
21
 
21
- ### This package has been tested with Python 3.6, 3.7, 3.8 and 3.9
22
+ ### This package has been tested with Python 3.6, 3.7, 3.8, 3.9 and 3.10
22
23
 
23
24
  The error analysis sdk enables users to get a deeper understanding of machine learning model errors. When evaluating a machine learning model, aggregate accuracy is not sufficient and single-score evaluation may hide important conditions of inaccuracies. Use Error Analysis to identify cohorts with higher error rates and diagnose the root causes behind these errors.
24
25
 
@@ -23,6 +23,9 @@ erroranalysis/_internal/error_analyzer/__init__.py
23
23
  erroranalysis/_internal/error_report/__init__.py
24
24
  erroranalysis/analyzer/__init__.py
25
25
  erroranalysis/analyzer/error_analyzer.py
26
+ erroranalysis/error_correlation_methods/__init__.py
27
+ erroranalysis/error_correlation_methods/ebm.py
28
+ erroranalysis/error_correlation_methods/gbm.py
26
29
  erroranalysis/report/__init__.py
27
30
  erroranalysis/report/error_report.py
28
31
  tests/test_cohort_filter.py
@@ -1,5 +1,5 @@
1
1
  numpy>=1.17.2
2
- pandas>=0.25.1
2
+ pandas<2.0.0,>=0.25.1
3
3
  scipy>=1.4.1
4
4
  scikit-learn>=0.22.1
5
5
  lightgbm>=2.0.11
@@ -1,5 +1,5 @@
1
1
  numpy>=1.17.2
2
- pandas>=0.25.1
2
+ pandas>=0.25.1,<2.0.0
3
3
  scipy>=1.4.1
4
4
  scikit-learn>=0.22.1
5
5
  lightgbm>=2.0.11
@@ -32,6 +32,7 @@ setuptools.setup(
32
32
  "Programming Language :: Python :: 3.7",
33
33
  "Programming Language :: Python :: 3.8",
34
34
  "Programming Language :: Python :: 3.9",
35
+ "Programming Language :: Python :: 3.10",
35
36
  "License :: OSI Approved :: MIT License",
36
37
  "Operating System :: OS Independent",
37
38
  "Development Status :: 3 - Alpha"
@@ -6,8 +6,8 @@ import pandas as pd
6
6
  import pytest
7
7
 
8
8
  from erroranalysis._internal.cohort_filter import filter_from_cohort
9
- from erroranalysis._internal.constants import (PRED_Y, ROW_INDEX, TRUE_Y,
10
- ModelTask)
9
+ from erroranalysis._internal.constants import (ARG, COLUMN, METHOD, PRED_Y,
10
+ ROW_INDEX, TRUE_Y, ModelTask)
11
11
  from erroranalysis._internal.error_analyzer import ModelAnalyzer
12
12
  from rai_test_utils.datasets.tabular import (create_diabetes_data,
13
13
  create_iris_data,
@@ -15,6 +15,7 @@ from rai_test_utils.datasets.tabular import (create_diabetes_data,
15
15
  from rai_test_utils.models.sklearn import (
16
16
  create_sklearn_random_forest_regressor, create_sklearn_svm_classifier,
17
17
  create_titanic_pipeline)
18
+ from raiutils.cohort import CohortFilterMethods
18
19
 
19
20
  TOL = 1e-10
20
21
  SEPAL_WIDTH = 'sepal width'
@@ -28,9 +29,9 @@ class TestCohortFilter(object):
28
29
  def test_cohort_filter_equal(self):
29
30
  X_train, X_test, y_train, y_test, feature_names, _ = \
30
31
  create_iris_pandas()
31
- filters = [{'arg': [2.8],
32
- 'column': SEPAL_WIDTH,
33
- 'method': 'equal'}]
32
+ filters = [{ARG: [2.8],
33
+ COLUMN: SEPAL_WIDTH,
34
+ METHOD: CohortFilterMethods.METHOD_EQUAL}]
34
35
  validation_data = create_validation_data(X_test, y_test)
35
36
  validation_data = validation_data.loc[X_test[SEPAL_WIDTH] == 2.8]
36
37
  model_task = ModelTask.CLASSIFICATION
@@ -53,9 +54,9 @@ class TestCohortFilter(object):
53
54
  "in Predicted Y cohort filtering")
54
55
  X_train, X_test, y_train, y_test, feature_names, classes = \
55
56
  create_iris_pandas(use_str_labels)
56
- filters = [{'arg': [2],
57
- 'column': target_type,
58
- 'method': 'includes'}]
57
+ filters = [{ARG: [2],
58
+ COLUMN: target_type,
59
+ METHOD: CohortFilterMethods.METHOD_INCLUDES}]
59
60
  validation_data = create_validation_data(X_test, y_test)
60
61
  if use_str_labels:
61
62
  validation_filter = y_test == classes[2]
@@ -79,9 +80,9 @@ class TestCohortFilter(object):
79
80
  def test_cohort_filter_less(self):
80
81
  X_train, X_test, y_train, y_test, feature_names, _ = \
81
82
  create_iris_pandas()
82
- filters = [{'arg': [2.8],
83
- 'column': SEPAL_WIDTH,
84
- 'method': 'less'}]
83
+ filters = [{ARG: [2.8],
84
+ COLUMN: SEPAL_WIDTH,
85
+ METHOD: CohortFilterMethods.METHOD_LESS}]
85
86
  validation_data = create_validation_data(X_test, y_test)
86
87
  validation_data = validation_data.loc[X_test[SEPAL_WIDTH] < 2.8]
87
88
  model_task = ModelTask.CLASSIFICATION
@@ -100,9 +101,9 @@ class TestCohortFilter(object):
100
101
  X_train, X_test, y_train, y_test, feature_names, _ = \
101
102
  create_iris_pandas()
102
103
 
103
- filters = [{'arg': [2.8],
104
- 'column': SEPAL_WIDTH,
105
- 'method': 'less and equal'}]
104
+ filters = [{ARG: [2.8],
105
+ COLUMN: SEPAL_WIDTH,
106
+ METHOD: CohortFilterMethods.METHOD_LESS_AND_EQUAL}]
106
107
  validation_data = create_validation_data(X_test, y_test)
107
108
  validation_data = validation_data.loc[X_test[SEPAL_WIDTH] <= 2.8]
108
109
  model_task = ModelTask.CLASSIFICATION
@@ -120,9 +121,9 @@ class TestCohortFilter(object):
120
121
  def test_cohort_filter_greater(self):
121
122
  X_train, X_test, y_train, y_test, feature_names, _ = \
122
123
  create_iris_pandas()
123
- filters = [{'arg': [2.8],
124
- 'column': SEPAL_WIDTH,
125
- 'method': 'greater'}]
124
+ filters = [{ARG: [2.8],
125
+ COLUMN: SEPAL_WIDTH,
126
+ METHOD: CohortFilterMethods.METHOD_GREATER}]
126
127
  validation_data = create_validation_data(X_test, y_test)
127
128
  validation_data = validation_data.loc[X_test[SEPAL_WIDTH] > 2.8]
128
129
  model_task = ModelTask.CLASSIFICATION
@@ -140,9 +141,9 @@ class TestCohortFilter(object):
140
141
  def test_cohort_filter_greater_and_equal(self):
141
142
  X_train, X_test, y_train, y_test, feature_names, _ = \
142
143
  create_iris_pandas()
143
- filters = [{'arg': [2.8],
144
- 'column': SEPAL_WIDTH,
145
- 'method': 'greater and equal'}]
144
+ filters = [{ARG: [2.8],
145
+ COLUMN: SEPAL_WIDTH,
146
+ METHOD: CohortFilterMethods.METHOD_GREATER_AND_EQUAL}]
146
147
  validation_data = create_validation_data(X_test, y_test)
147
148
  validation_data = validation_data.loc[X_test[SEPAL_WIDTH] >= 2.8]
148
149
  model_task = ModelTask.CLASSIFICATION
@@ -160,9 +161,9 @@ class TestCohortFilter(object):
160
161
  def test_cohort_filter_in_the_range_of(self):
161
162
  X_train, X_test, y_train, y_test, feature_names, _ = \
162
163
  create_iris_pandas()
163
- filters = [{'arg': [2.8, 3.4],
164
- 'column': SEPAL_WIDTH,
165
- 'method': 'in the range of'}]
164
+ filters = [{ARG: [2.8, 3.4],
165
+ COLUMN: SEPAL_WIDTH,
166
+ METHOD: CohortFilterMethods.METHOD_RANGE}]
166
167
  validation_data = create_validation_data(X_test, y_test)
167
168
  validation_data = validation_data.loc[
168
169
  (X_test[SEPAL_WIDTH] <= 3.4) & (X_test[SEPAL_WIDTH] >= 2.8)]
@@ -190,9 +191,9 @@ class TestCohortFilter(object):
190
191
 
191
192
  # the index 1, corresponds to incorrect prediction
192
193
  # the index 0 correspond to correct prediction
193
- filters = [{'arg': arg,
194
- 'column': CLASSIFICATION_OUTCOME,
195
- 'method': 'includes'}]
194
+ filters = [{ARG: arg,
195
+ COLUMN: CLASSIFICATION_OUTCOME,
196
+ METHOD: CohortFilterMethods.METHOD_INCLUDES}]
196
197
  pred_y = model.predict(X_test)
197
198
  validation_data = create_validation_data(X_test, y_test, pred_y)
198
199
  if correct_prediction:
@@ -221,9 +222,9 @@ class TestCohortFilter(object):
221
222
  clf = create_titanic_pipeline(X_train, y_train)
222
223
  categorical_features = categorical
223
224
  # the indexes 0, 2 correspond to S, C
224
- filters = [{'arg': [0, 2],
225
- 'column': EMBARKED,
226
- 'method': 'includes'}]
225
+ filters = [{ARG: [0, 2],
226
+ COLUMN: EMBARKED,
227
+ METHOD: CohortFilterMethods.METHOD_INCLUDES}]
227
228
  validation_data = create_validation_data(X_test, y_test)
228
229
  filter_embarked = X_test[EMBARKED].isin(['S', 'C'])
229
230
  validation_data = validation_data.loc[filter_embarked]
@@ -244,9 +245,9 @@ class TestCohortFilter(object):
244
245
  clf = create_titanic_pipeline(X_train, y_train)
245
246
  categorical_features = categorical
246
247
  # the indexes other than 0, 2 correspond to Q
247
- filters = [{'arg': [0, 2],
248
- 'column': EMBARKED,
249
- 'method': 'excludes'}]
248
+ filters = [{ARG: [0, 2],
249
+ COLUMN: EMBARKED,
250
+ METHOD: CohortFilterMethods.METHOD_EXCLUDES}]
250
251
  validation_data = create_validation_data(X_test, y_test)
251
252
  filter_embarked = X_test[EMBARKED].isin(['Q'])
252
253
  validation_data = validation_data.loc[filter_embarked]
@@ -269,9 +270,9 @@ class TestCohortFilter(object):
269
270
  categorical_features = categorical
270
271
  # the indexes 1, 2 correspond to false positives and false negatives
271
272
  # the indexes 0, 3 correspond to true positives and true negatives
272
- filters = [{'arg': arg,
273
- 'column': CLASSIFICATION_OUTCOME,
274
- 'method': 'includes'}]
273
+ filters = [{ARG: arg,
274
+ COLUMN: CLASSIFICATION_OUTCOME,
275
+ METHOD: CohortFilterMethods.METHOD_INCLUDES}]
275
276
  pred_y = clf.predict(X_test)
276
277
  validation_data = create_validation_data(X_test, y_test, pred_y)
277
278
  if not outcome:
@@ -296,9 +297,9 @@ class TestCohortFilter(object):
296
297
  X_train, X_test, y_train, y_test, feature_names, _ = \
297
298
  create_iris_pandas()
298
299
  # filter on index, which can be done from the RAI dashboard
299
- filters = [{'arg': [40],
300
- 'column': ROW_INDEX,
301
- 'method': 'less and equal'}]
300
+ filters = [{ARG: [40],
301
+ COLUMN: ROW_INDEX,
302
+ METHOD: CohortFilterMethods.METHOD_LESS_AND_EQUAL}]
302
303
  validation_data = create_validation_data(X_test, y_test)
303
304
  validation_data = validation_data.loc[validation_data[ROW_INDEX] <= 40]
304
305
  model_task = ModelTask.CLASSIFICATION
@@ -321,9 +322,9 @@ class TestCohortFilter(object):
321
322
 
322
323
  # filter on regression error, which can be done from the
323
324
  # RAI dashboard
324
- filters = [{'arg': [40],
325
- 'column': REGRESSION_ERROR,
326
- 'method': 'less and equal'}]
325
+ filters = [{ARG: [40],
326
+ COLUMN: REGRESSION_ERROR,
327
+ METHOD: CohortFilterMethods.METHOD_LESS_AND_EQUAL}]
327
328
 
328
329
  model = create_sklearn_random_forest_regressor(X_train, y_train)
329
330
  pred_y = model.predict(X_test)
@@ -1,8 +1,6 @@
1
1
  # Copyright (c) Microsoft Corporation
2
2
  # Licensed under the MIT License.
3
3
 
4
- import uuid
5
-
6
4
  import numpy as np
7
5
  import pandas as pd
8
6
  import pytest
@@ -11,9 +9,13 @@ from erroranalysis._internal.error_analyzer import ModelAnalyzer
11
9
  from erroranalysis._internal.error_report import ErrorReport
12
10
  from rai_test_utils.datasets.tabular import (create_cancer_data,
13
11
  create_housing_data,
14
- create_iris_data)
12
+ create_iris_data,
13
+ create_simple_titanic_data)
15
14
  from rai_test_utils.models.model_utils import (create_models_classification,
16
15
  create_models_regression)
16
+ from rai_test_utils.models.sklearn import \
17
+ create_complex_classification_pipeline
18
+ from rai_test_utils.utilities import is_valid_uuid
17
19
 
18
20
 
19
21
  class TestErrorReport(object):
@@ -79,13 +81,21 @@ class TestErrorReport(object):
79
81
  categorical_features,
80
82
  filter_features=filter_features)
81
83
 
84
+ def test_error_report_titanic(self):
85
+ X_train, X_test, y_train, y_test, numeric, categorical = \
86
+ create_simple_titanic_data()
87
+
88
+ # Drop all numeric features
89
+ X_train = X_train.drop(['pclass', 'age', 'fare'], axis=1)
90
+ X_test = X_test.drop(['pclass', 'age', 'fare'], axis=1)
91
+
92
+ clf = create_complex_classification_pipeline(
93
+ X_train, y_train, [], ['embarked', 'sex'])
82
94
 
83
- def is_valid_uuid(id):
84
- try:
85
- uuid.UUID(str(id))
86
- return True
87
- except ValueError:
88
- return False
95
+ # Pass the remaining categorical features
96
+ run_error_analyzer(clf, X_test, y_test, ['embarked', 'sex'],
97
+ ['embarked', 'sex'],
98
+ filter_features=[])
89
99
 
90
100
 
91
101
  def run_error_analyzer(model, X_test, y_test, feature_names,
@@ -7,7 +7,8 @@ import numpy as np
7
7
  import pytest
8
8
  from common_utils import replicate_dataset
9
9
 
10
- from erroranalysis._internal.constants import ModelTask
10
+ from erroranalysis._internal.constants import (ErrorCorrelationMethods,
11
+ ModelTask)
11
12
  from erroranalysis._internal.error_analyzer import ModelAnalyzer
12
13
  from rai_test_utils.datasets.tabular import (
13
14
  create_binary_classification_dataset, create_cancer_data,
@@ -22,11 +23,17 @@ from rai_test_utils.models.sklearn import (
22
23
  TOL = 1e-10
23
24
  NUM_SAMPLE_ROWS = 100
24
25
  DEFAULT_SAMPLE_COLS = 20
26
+ MUTUAL_INFO = ErrorCorrelationMethods.MUTUAL_INFO
27
+ EBM = ErrorCorrelationMethods.EBM
28
+ GBM_SHAP = ErrorCorrelationMethods.GBM_SHAP
29
+ GBM_ERROR_SAMPLES_TOL = 6
25
30
 
26
31
 
27
32
  class TestImportances(object):
28
33
 
29
- def test_importances_iris(self):
34
+ @pytest.mark.parametrize('error_correlation_method',
35
+ [MUTUAL_INFO, EBM, GBM_SHAP])
36
+ def test_importances_iris(self, error_correlation_method):
30
37
  X_train, X_test, y_train, y_test, feature_names, _ = create_iris_data()
31
38
 
32
39
  models = create_models_classification(X_train, y_train)
@@ -34,9 +41,11 @@ class TestImportances(object):
34
41
  for model in models:
35
42
  categorical_features = []
36
43
  run_error_analyzer(model, X_test, y_test, feature_names,
37
- categorical_features)
44
+ categorical_features, error_correlation_method)
38
45
 
39
- def test_importances_cancer(self):
46
+ @pytest.mark.parametrize('error_correlation_method',
47
+ [MUTUAL_INFO, EBM, GBM_SHAP])
48
+ def test_importances_cancer(self, error_correlation_method):
40
49
  X_train, X_test, y_train, y_test, feature_names, _ = \
41
50
  create_cancer_data()
42
51
 
@@ -45,9 +54,12 @@ class TestImportances(object):
45
54
  for model in models:
46
55
  categorical_features = []
47
56
  run_error_analyzer(model, X_test, y_test, feature_names,
48
- categorical_features)
57
+ categorical_features, error_correlation_method)
49
58
 
50
- def test_importances_binary_classification(self):
59
+ @pytest.mark.parametrize('error_correlation_method',
60
+ [MUTUAL_INFO, EBM, GBM_SHAP])
61
+ def test_importances_binary_classification(self,
62
+ error_correlation_method):
51
63
  X_train, y_train, X_test, y_test, _ = \
52
64
  create_binary_classification_dataset()
53
65
  feature_names = list(X_train.columns)
@@ -56,18 +68,22 @@ class TestImportances(object):
56
68
  for model in models:
57
69
  categorical_features = []
58
70
  run_error_analyzer(model, X_test, y_test, feature_names,
59
- categorical_features)
71
+ categorical_features, error_correlation_method)
60
72
 
61
- def test_importances_titanic(self):
73
+ @pytest.mark.parametrize('error_correlation_method',
74
+ [MUTUAL_INFO, EBM, GBM_SHAP])
75
+ def test_importances_titanic(self, error_correlation_method):
62
76
  X_train, X_test, y_train, y_test, numeric, categorical = \
63
77
  create_simple_titanic_data()
64
78
  feature_names = categorical + numeric
65
79
  clf = create_titanic_pipeline(X_train, y_train)
66
80
  categorical_features = categorical
67
81
  run_error_analyzer(clf, X_test, y_test, feature_names,
68
- categorical_features)
82
+ categorical_features, error_correlation_method)
69
83
 
70
- def test_importances_housing(self):
84
+ @pytest.mark.parametrize('error_correlation_method',
85
+ [MUTUAL_INFO, EBM, GBM_SHAP])
86
+ def test_importances_housing(self, error_correlation_method):
71
87
  X_train, X_test, y_train, y_test, feature_names = \
72
88
  create_housing_data()
73
89
  models = create_models_regression(X_train, y_train)
@@ -75,9 +91,11 @@ class TestImportances(object):
75
91
  for model in models:
76
92
  categorical_features = []
77
93
  run_error_analyzer(model, X_test, y_test, feature_names,
78
- categorical_features)
94
+ categorical_features, error_correlation_method)
79
95
 
80
- def test_large_data_importances(self):
96
+ @pytest.mark.parametrize('error_correlation_method',
97
+ [MUTUAL_INFO, EBM, GBM_SHAP])
98
+ def test_large_data_importances(self, error_correlation_method):
81
99
  # mutual information can be very costly for large number of rows
82
100
  # hence, assert we downsample to compute importances for large data
83
101
  X_train, y_train, X_test, y_test, _ = \
@@ -91,16 +109,22 @@ class TestImportances(object):
91
109
  model_analyzer = ModelAnalyzer(model, X_test, y_test,
92
110
  feature_names,
93
111
  categorical_features)
94
- model_analyzer.compute_importances()
112
+ model_analyzer.compute_importances(error_correlation_method)
95
113
  t1 = time.time()
96
114
  execution_time = t1 - t0
97
115
  print(execution_time)
98
116
  # assert we don't take too long and downsample the dataset
99
117
  # note execution time is in seconds
100
- assert execution_time < 20
118
+ # note GBM_SHAP is slower than the other methods
119
+ if error_correlation_method == GBM_SHAP:
120
+ assert execution_time < 35
121
+ else:
122
+ assert execution_time < 25
101
123
 
102
124
  @pytest.mark.parametrize('num_rows', [1, 2, 3, 4])
103
- def test_small_data_importances(self, num_rows):
125
+ @pytest.mark.parametrize('error_correlation_method',
126
+ [MUTUAL_INFO, EBM, GBM_SHAP])
127
+ def test_small_data_importances(self, num_rows, error_correlation_method):
104
128
  # validate we can run on very few rows
105
129
  X_train, y_train, X_test, y_test, _ = \
106
130
  create_binary_classification_dataset(NUM_SAMPLE_ROWS)
@@ -112,10 +136,12 @@ class TestImportances(object):
112
136
  model_analyzer = ModelAnalyzer(model, X_test, y_test,
113
137
  feature_names,
114
138
  categorical_features)
115
- scores = model_analyzer.compute_importances()
139
+ scores = model_analyzer.compute_importances(error_correlation_method)
116
140
  assert len(scores) == DEFAULT_SAMPLE_COLS
117
141
 
118
- def test_importances_missings(self):
142
+ @pytest.mark.parametrize('error_correlation_method',
143
+ [MUTUAL_INFO, EBM, GBM_SHAP])
144
+ def test_importances_missings(self, error_correlation_method):
119
145
  X_train, X_test, y_train, y_test, feature_names, _ = create_iris_data()
120
146
 
121
147
  # add missing values to X_test
@@ -127,23 +153,29 @@ class TestImportances(object):
127
153
 
128
154
  categorical_features = []
129
155
  run_error_analyzer(model, X_test, y_test, feature_names,
130
- categorical_features)
156
+ categorical_features, error_correlation_method)
131
157
 
132
158
 
133
159
  def run_error_analyzer(model, X_test, y_test, feature_names,
134
- categorical_features):
160
+ categorical_features,
161
+ error_correlation_method):
135
162
  model_analyzer = ModelAnalyzer(model, X_test, y_test,
136
163
  feature_names,
137
164
  categorical_features)
138
- scores = model_analyzer.compute_importances()
165
+ scores = model_analyzer.compute_importances(error_correlation_method)
139
166
  if model_analyzer.model_task == ModelTask.CLASSIFICATION:
140
167
  diff = model.predict(model_analyzer.dataset) != model_analyzer.true_y
141
168
  else:
142
169
  diff = model.predict(model_analyzer.dataset) - model_analyzer.true_y
143
170
  assert isinstance(scores, list)
144
171
  assert len(scores) == len(feature_names)
172
+ # GBM methods seems to predict all zeros
173
+ # (and sometimes otherwise) even if very few instances non-zero
174
+ is_gbm = error_correlation_method == GBM_SHAP
175
+ sum_diff = sum(diff)
176
+ noisy_gbm = sum_diff < GBM_ERROR_SAMPLES_TOL and sum_diff > 0 and is_gbm
145
177
  # If model predicted perfectly, assert all scores are zeros
146
178
  if not any(diff):
147
179
  assert all(abs(score - 0) < TOL for score in scores)
148
- else:
180
+ elif not noisy_gbm:
149
181
  assert any(score != 0 for score in scores)
@@ -6,8 +6,9 @@ import pandas as pd
6
6
  import pytest
7
7
 
8
8
  from erroranalysis._internal.cohort_filter import filter_from_cohort
9
- from erroranalysis._internal.constants import (ROW_INDEX, TRUE_Y, MatrixParams,
10
- Metrics, ModelTask, f1_metrics,
9
+ from erroranalysis._internal.constants import (ARG, COLUMN, METHOD, ROW_INDEX,
10
+ TRUE_Y, MatrixParams, Metrics,
11
+ ModelTask, f1_metrics,
11
12
  metric_to_display_name,
12
13
  precision_metrics,
13
14
  recall_metrics)
@@ -50,9 +51,9 @@ class TestMatrixFilter(object):
50
51
  # cohort was chosen in tree view
51
52
  X_train, X_test, y_train, y_test, feature_names, _ = create_iris_data()
52
53
 
53
- filters = [{'arg': [2.85],
54
- 'column': feature_names[1],
55
- 'method': 'less and equal'}]
54
+ filters = [{ARG: [2.85],
55
+ COLUMN: feature_names[1],
56
+ METHOD: 'less and equal'}]
56
57
 
57
58
  model_task = ModelTask.CLASSIFICATION
58
59
  run_error_analyzer_on_models(X_train, y_train, X_test,
@@ -67,9 +68,9 @@ class TestMatrixFilter(object):
67
68
  X_train = pd.DataFrame(X_train, columns=feature_names)
68
69
  X_test = pd.DataFrame(X_test, columns=feature_names)
69
70
 
70
- filters = [{'arg': [2.85],
71
- 'column': feature_names[1],
72
- 'method': 'less and equal'}]
71
+ filters = [{ARG: [2.85],
72
+ COLUMN: feature_names[1],
73
+ METHOD: 'less and equal'}]
73
74
 
74
75
  model_task = ModelTask.CLASSIFICATION
75
76
  run_error_analyzer_on_models(X_train, y_train, X_test,
@@ -222,24 +223,24 @@ class TestMatrixFilter(object):
222
223
 
223
224
  composite_filters = [{'compositeFilters':
224
225
  [{'compositeFilters':
225
- [{'arg': [11.364, 13.182],
226
- 'column': 'mean radius',
227
- 'method': 'in the range of'}],
226
+ [{ARG: [11.364, 13.182],
227
+ COLUMN: 'mean radius',
228
+ METHOD: 'in the range of'}],
228
229
  'operation': 'and'},
229
230
  {'compositeFilters':
230
- [{'arg': [13.182, 15],
231
- 'column': 'mean radius',
232
- 'method': 'in the range of'}],
231
+ [{ARG: [13.182, 15],
232
+ COLUMN: 'mean radius',
233
+ METHOD: 'in the range of'}],
233
234
  'operation': 'and'},
234
235
  {'compositeFilters':
235
- [{'arg': [15, 16.817],
236
- 'column': 'mean radius',
237
- 'method': 'in the range of'}],
236
+ [{ARG: [15, 16.817],
237
+ COLUMN: 'mean radius',
238
+ METHOD: 'in the range of'}],
238
239
  'operation': 'and'},
239
240
  {'compositeFilters':
240
- [{'arg': [16.817, 18.635],
241
- 'column': 'mean radius',
242
- 'method': 'in the range of'}],
241
+ [{ARG: [16.817, 18.635],
242
+ COLUMN: 'mean radius',
243
+ METHOD: 'in the range of'}],
243
244
  'operation': 'and'}],
244
245
  'operation': 'or'}]
245
246
 
@@ -293,12 +294,12 @@ class TestMatrixFilter(object):
293
294
  def test_matrix_filter_housing_filters(self):
294
295
  X_train, X_test, y_train, y_test, feature_names = create_housing_data()
295
296
 
296
- filters = [{'arg': [600],
297
- 'column': 'Population',
298
- 'method': 'less and equal'},
299
- {'arg': [6],
300
- 'column': 'AveRooms',
301
- 'method': 'greater'}]
297
+ filters = [{ARG: [600],
298
+ COLUMN: 'Population',
299
+ METHOD: 'less and equal'},
300
+ {ARG: [6],
301
+ COLUMN: 'AveRooms',
302
+ METHOD: 'greater'}]
302
303
 
303
304
  model_task = ModelTask.REGRESSION
304
305
  run_error_analyzer_on_models(X_train, y_train, X_test,
@@ -66,12 +66,24 @@ class TestSurrogateErrorTree(object):
66
66
  def test_surrogate_error_tree_int_categorical(self, analyzer_type):
67
67
  X_train, X_test, y_train, y_test, categorical_features = \
68
68
  create_adult_census_data()
69
-
70
69
  model = create_kneighbors_classifier(X_train, y_train)
71
70
 
72
71
  run_error_analyzer(model, X_test, y_test, list(X_train.columns),
73
72
  analyzer_type, categorical_features)
74
73
 
74
+ @pytest.mark.parametrize('analyzer_type', [AnalyzerType.MODEL,
75
+ AnalyzerType.PREDICTIONS])
76
+ def test_surrogate_error_tree_categorical_filtered(self, analyzer_type):
77
+ X_train, X_test, y_train, y_test, categorical_features = \
78
+ create_adult_census_data()
79
+ model = create_kneighbors_classifier(X_train, y_train)
80
+ filters = [{ARG: [40],
81
+ COLUMN: 'Age',
82
+ METHOD: 'less and equal'}]
83
+ run_error_analyzer(model, X_test, y_test, list(X_train.columns),
84
+ analyzer_type, categorical_features,
85
+ filters=filters)
86
+
75
87
  def test_large_data_surrogate_error_tree(self):
76
88
  # validate tree trains quickly for large data
77
89
  X_train, y_train, X_test, y_test, _ = \
File without changes
File without changes
File without changes