Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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))