<jupyter_start><jupyter_text>Read in Data<jupyter_code>data_name = 'german'
data_file = data_dir / ('%s_processed.csv' % data_name)
output_dir = results_dir /  data_name

## load and process data
data_df = pd.read_csv(data_file).reset_index(drop=True)
# german_df = german_df.assign(isMale=lambda df: (df['Gender']=='Male').astype(int))#.drop(['PurposeOfLoan', 'Gender', 'OtherLoansAtStore'], axis=1)
y = data_df['GoodCustomer']
X = (data_df.drop('GoodCustomer', axis=1)
     .drop(['PurposeOfLoan', 'Gender', 'OtherLoansAtStore'], axis=1))<jupyter_output><empty_output><jupyter_text>Train classifier<jupyter_code>## Train and generate AUCs
## grid search
clf = LogisticRegression(max_iter=1000, solver='lbfgs')
grid = GridSearchCV(
        clf, param_grid={'C': np.logspace(-4, 3)},
        cv=10,
        scoring='roc_auc',
        return_train_score=True
        )
grid.fit(X, y)
clf = grid.best_estimator_<jupyter_output><empty_output><jupyter_text>Create Actionset<jupyter_code>## set up actionset
gender_weight = data_df.assign(c = 1).groupby('Gender')['c'].transform(lambda s: s * 1. / len(s))
X_gender_balanced = X.sample(n = len(X)*3, replace=True, weights=gender_weight)
action_set = ActionSet(X = X_gender_balanced)
action_set['Age'].mutable = False
action_set['Single'].mutable = False
action_set['JobClassIsSkilled'].mutable = False
action_set['ForeignWorker'].mutable = False
action_set['OwnsHouse'].mutable = False
action_set['RentsHouse'].mutable = False
action_set['CriticalAccountOrLoansElsewhere'].step_direction = -1
action_set['CheckingAccountBalance_geq_0'].step_direction = 1<jupyter_output><empty_output><jupyter_text>Run Audit<jupyter_code># median score
np.median(clf.predict_proba(X)[:, 1])
# cache classifier
coefficients = clf.coef_[0]
intercept = clf.intercept_[0]
action_set.align(coefficients=coefficients)

# audit
p = .72 ## for now we'll set this to the median score, but we could set this to any threshold we want.
auditor = RecourseAuditor(
    action_set,
    coefficients = coefficients,
    intercept = intercept - (np.log(p / (1. - p))),
)
audit_results = auditor.audit(X = X)<jupyter_output><empty_output><jupyter_text>Visualize<jupyter_code>scores = pd.Series(clf.predict_proba(X)[:, 1])
matching_df = pd.concat([
    data_df[['GoodCustomer', 'Gender']],
    audit_results[['cost']],
    scores.to_frame('y_pred')
    ], axis=1).replace(np.inf, np.nan).dropna()


####### Matching 2: Control for Y=+/- 1
matching_df['y_pred_bin'] = pd.cut(
    matching_df['y_pred'],
    bins=np.arange(0, .9, .2)
)

bins = matching_df['y_pred_bin'].unique()

max_cost = matching_df['cost'].max()
plt.rc("font", size=20)
for y_true in [-1, 1]:
    plt.figure(figsize=(4, 4))
    ax = sns.violinplot(
        x='y_pred_bin',  y='cost', hue='Gender',
        data=matching_df.loc[lambda df: df['GoodCustomer'] == y_true].sort_values('Gender'),
        linewidth = 0.5, cut=0, background='white',
        scale = 'width', color="gold",  inner = 'quartile'
    )
    ax.set_xticklabels(["10%", "30%", "50%", "70%"])

    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)

    ax.set_ylim((0, max_cost))
    plt.title('Total Cost By Gender: Y-True: %d' % y_true, fontsize=20)
    plt.ylabel('Cost of Recourse')
    plt.xlabel('Predicted Risk' )

    if y_true == -1:
        plt.legend(fontsize=12., loc='upper right', bbox_to_anchor=(1.1, 1))
    else:
        plt.legend(fontsize=12., loc='lower left')#, bbox_to_anchor=(1.1, 1))
    for l in ax.lines:
        l.set_linewidth(2.)
        l.set_linestyle('-')
        l.set_solid_capstyle('butt')

    plt.show()<jupyter_output><empty_output><jupyter_text>Generate Flipsets<jupyter_code>from IPython.display import HTML
# ### get flipset
gender = "Male"
individuals = (matching_df
    .loc[lambda df: df['Gender'] == gender]
    .loc[lambda df: df['y_pred'] < .3]
    .loc[lambda df: df['y_pred'] > .1]
    .loc[lambda df: df['GoodCustomer'] == 1]
    )
i = individuals.index[1]

fb = Flipset(
    x = X.loc[i].values, 
    action_set = action_set, 
    coefficients = coefficients,
    intercept = intercept - (np.log(p / (1. - p))),
)
fb.populate(enumeration_type = 'distinct_subsets', total_items = 6)
HTML(fb.to_html())
gender = 'Female'
individuals = (matching_df
    .loc[lambda df: df['Gender'] == gender]
    .loc[lambda df: df['y_pred'] < .3]
    .loc[lambda df: df['y_pred'] > .1]
    .loc[lambda df: df['GoodCustomer'] == 1]
    )
i = individuals.index[0]

fb = Flipset(
    x = X.loc[i].values, 
    action_set = action_set, 
    coefficients = coefficients,
    intercept = intercept - (np.log(p / (1. - p))),
)
fb.populate(enumeration_type = 'distinct_subsets', total_items = 6)
HTML(fb.to_html())<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1691269506537326, 'fraction_numerical': 0.01560522986081822, 'mean_word_length': 4.599763872491145, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '80929', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1858, 'n_tokens_neox': 1768, 'n_words': 320, 'path': 'examples/ex_04_audit_demo-disparities-in-recourse.ipynb', 'repo_name': 'alexisjihyeross/actionable-recourse', 'size': '4742'}