def test_textualise_counterfactuals_errors(): """ Tests the ``textualise_counterfactuals`` function for errors. This function thests the :func:`fatf.transparency.predictions. counterfactuals.textualise_counterfactuals` function. """ incorrect_shape_instance = ('The instance has to be a 1-dimensional numpy ' 'array.') incorrect_shape_counterfactuals = ('The counterfactuals array should be a ' '2-dimensional numpy array.') incorrect_shape_counterfactuals_distances = ( 'The counterfactuals_distances array should be a 1-dimensional array.') incorrect_shape_counterfactuals_predictions = ( 'The counterfactuals_predictions array should be a 1-dimensional ' 'array.') # value_error_instance = ('The instance has to be of a base type (strings ' 'and/or numbers).') value_error_counterfactuals = ('The counterfactuals array has to be of a ' 'base type (strings and/or numbers).') value_error_counterfactuals_distances_type = ( 'The counterfactuals_distances array should be purely numerical.') value_error_counterfactuals_distances_shape = ( 'The counterfactuals_distances array should be of the same length as ' 'the number of rows in the counterfactuals array.') value_error_counterfactuals_predictions_type = ( 'The counterfactuals_predictions array should be of a base type ' '(numbers and/or strings).') value_error_counterfactuals_predictions_shape = ( 'The counterfactuals_predictions array should be of the same length ' 'as the number of rows in the counterfactuals array.') value_error_counterfactuals_distances_instance = ( 'The type of the instance_class is different than the type of the ' 'counterfactuals_predictions array.') # type_error_instance_class = ('The instance_class has to be either an ' 'integer or a string.') type_error_intsance_counterfactuals = ('The type of the instance and the ' 'counterfactuals arrays do not ' 'agree.') # index_error_instance = ('The counterfactuals and instance column indices ' 'do not agree. (The two arrays have different ' 'number of columns.)') struct_array = np.array([('a', 1), ('b', 2)], dtype=[('a', 'U1'), ('b', 'i')]) classic_array = np.array([[0, 1], [5.5, 2]]) classic_array_type = np.array([[0, None], [5.5, 2]]) classic_array_str = np.array([[0, 'None'], [5.5, 2]]) # Instance with pytest.raises(IncorrectShapeError) as exin: ftpc.textualise_counterfactuals(struct_array, struct_array) assert str(exin.value) == incorrect_shape_instance with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals(classic_array_type[0], struct_array) assert str(exin.value) == value_error_instance # Counterfactuals with pytest.raises(IncorrectShapeError) as exin: ftpc.textualise_counterfactuals(struct_array[0], classic_array[0]) assert str(exin.value) == incorrect_shape_counterfactuals with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals(classic_array[0], classic_array_type) assert str(exin.value) == value_error_counterfactuals with pytest.raises(TypeError) as exin: ftpc.textualise_counterfactuals(struct_array[0], classic_array) assert str(exin.value) == type_error_intsance_counterfactuals # Instance class with pytest.raises(TypeError) as exin: ftpc.textualise_counterfactuals(classic_array[0], classic_array, 5.5) assert str(exin.value) == type_error_instance_class # Counterfactuals distances with pytest.raises(IncorrectShapeError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_distances=classic_array) assert str(exin.value) == incorrect_shape_counterfactuals_distances with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_distances=classic_array_str[0]) assert str(exin.value) == value_error_counterfactuals_distances_type with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_distances=classic_array[1, [0]]) assert str(exin.value) == value_error_counterfactuals_distances_shape # Counterfactuals predictions with pytest.raises(IncorrectShapeError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_predictions=classic_array) assert str(exin.value) == incorrect_shape_counterfactuals_predictions with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_predictions=classic_array_type[0]) assert str(exin.value) == value_error_counterfactuals_predictions_type with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, counterfactuals_predictions=classic_array[1, [0]]) assert str(exin.value) == value_error_counterfactuals_predictions_shape with pytest.raises(ValueError) as exin: ftpc.textualise_counterfactuals( classic_array[0], classic_array, instance_class='1', counterfactuals_predictions=classic_array[1]) assert str(exin.value) == value_error_counterfactuals_distances_instance # Invalid indices with pytest.raises(IndexError) as exin: ftpc.textualise_counterfactuals(classic_array[0], classic_array[:, [0]]) assert str(exin.value) == index_error_instance
dp_1_cfs, dp_1_cfs_distances, dp_1_cfs_predictions = dp_1_cf_tuple dp_1_cfs_predictions_names = np.array( [iris_class_names[i] for i in dp_1_cfs_predictions]) print('\nCounterfactuals for the data point:') pprint(dp_1_cfs) print('\nDistances between the counterfactuals and the data point:') pprint(dp_1_cfs_distances) print('\nClasses (indices and class names) of the counterfactuals:') pprint(dp_1_cfs_predictions) pprint(dp_1_cfs_predictions_names) # Textualise the counterfactuals dp_1_cfs_text = fatf_cf.textualise_counterfactuals( dp_1_X, dp_1_cfs, instance_class=dp_1_y, counterfactuals_distances=dp_1_cfs_distances, counterfactuals_predictions=dp_1_cfs_predictions) print(dp_1_cfs_text) ############################################################################### # Explain another data point. # Select a data point to be explained dp_2_index = 99 dp_2_X = iris_X[dp_2_index, :] dp_2_y = iris_y[dp_2_index] describe_data_point(dp_2_index) # Get a Counterfactual Explanation tuple for this data point dp_2_cf_tuple = cf_explainer.explain_instance(dp_2_X)
def test_textualise_counterfactuals(): """ Tests the ``textualise_counterfactuals`` function. This function thests the :func:`fatf.transparency.predictions. counterfactuals.textualise_counterfactuals` function. """ instance_class_str = 'good' instance_class_int = 1 counterfactuals_distances = np.array([4, 1]) counterfactuals_predictions_str = np.array(['bad', 'mediocre']) counterfactuals_predictions_int = np.array([0, 2]) classic_instance = np.array([1, 2, 3]) classic_counterfactuals = np.array([[1, 2.5, 3.0], [0, 2, 5]]) struct_instance = np.array([(1, 'bb', 3)], dtype=[('a', 'i'), ('b', 'U2'), ('c', 'i')])[0] struct_counterfactuals = np.array( [(1, 'a', 3.0), (2, 'b', 3)], dtype=[('a', 'i'), ('b', 'U1'), ('c', 'i')] ) # yapf: disable cls_cf_0 = ('Instance:\n[1 2 3]\n\nFeature names: [0, 1, 2]\n\n' 'Counterfactual instance:\n feature *1*: *2* -> *2.5*\n\n' 'Counterfactual instance:\n feature *0*: *1* -> *0.0*\n' ' feature *2*: *3* -> *5.0*') cls_cf_1 = ('Instance (of class *good*):\n[1 2 3]\n\n' 'Feature names: [0, 1, 2]\n\n' 'Counterfactual instance (of class *mediocre*):\n' 'Distance: 1\n' ' feature *0*: *1* -> *0.0*\n' ' feature *2*: *3* -> *5.0*\n\n' 'Counterfactual instance (of class *bad*):\n' 'Distance: 4\n' ' feature *1*: *2* -> *2.5*') stc_cf_0 = ('Instance (of class *1*):\n(1, \'bb\', 3)\n\n' 'Feature names: (\'a\', \'b\', \'c\')\n\n' 'Counterfactual instance (of class *0*):\n' ' feature *b*: *bb* -> *a*\n\n' 'Counterfactual instance (of class *2*):\n' ' feature *a*: *1* -> *2*\n feature *b*: *bb* -> *b*') # A minimal working example dsc = ftpc.textualise_counterfactuals(classic_instance, classic_counterfactuals) assert dsc == cls_cf_0 # With the original class and counterfactual classes dsc = ftpc.textualise_counterfactuals( struct_instance, struct_counterfactuals, instance_class=instance_class_int, counterfactuals_predictions=counterfactuals_predictions_int) assert dsc == stc_cf_0 # With the original class and counterfactual classes (strings) and dists dsc = ftpc.textualise_counterfactuals( classic_instance, classic_counterfactuals, instance_class=instance_class_str, counterfactuals_predictions=counterfactuals_predictions_str, counterfactuals_distances=counterfactuals_distances) assert dsc == cls_cf_1
assert protected_features, 'The protected features list cannot be empty.' person = ' is' if len(protected_features) == 1 else 's are' print('The following fautre{} considered protected:'.format(person)) for feature_name in protected_features: print(' * "{}".'.format(feature_name)) # Print the instance print('\nEvaluating counterfactual fairness of a data point (index {}) of ' 'class *{}* with the following features:'.format(data_point_index, data_point_y)) for feature_name in data_point.dtype.names: print(' * The feature *{}* has value: {}.'.format( feature_name, data_point[feature_name])) # Compute counterfactually unfair examples cfs, cfs_distances, cfs_classes = fatf_pfm.counterfactual_fairness( instance=data_point, protected_feature_indices=protected_features, model=clf, default_numerical_step_size=1, dataset=hr_X) # Textualise possible counterfactually unfair data points cfs_text = fatf_cf.textualise_counterfactuals( data_point, cfs, instance_class=data_point_y, counterfactuals_distances=cfs_distances, counterfactuals_predictions=cfs_classes) print('\n{}'.format(cfs_text))