{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zyACqF5CQKUU"
   },
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/master/examples/sklearn/demo_new_features.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting Started"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "3P12Gr-RQW2e",
    "outputId": "4a6126f9-198f-4b88-c9cf-f44a932ecaf0"
   },
   "outputs": [],
   "source": [
    "#Install AIF360\n",
    "!pip install 'aif360'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "KFgI8U9ZQX7g",
    "outputId": "b47381c9-8f71-4e59-ee4d-e63730211573"
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import tensorflow.compat.v1 as tf\n",
    "tf.disable_eager_execution()\n",
    "tf.logging.set_verbosity(tf.logging.ERROR)\n",
    "\n",
    "from sklearn.compose import make_column_transformer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import accuracy_score\n",
    "from sklearn.model_selection import GridSearchCV, train_test_split\n",
    "from sklearn.preprocessing import OneHotEncoder\n",
    "\n",
    "from aif360.sklearn.preprocessing import Reweighing, ReweighingMeta\n",
    "from aif360.sklearn.inprocessing import AdversarialDebiasing\n",
    "from aif360.sklearn.postprocessing import CalibratedEqualizedOdds, PostProcessingMeta\n",
    "from aif360.sklearn.datasets import fetch_adult\n",
    "from aif360.sklearn.metrics import disparate_impact_ratio, average_odds_error, generalized_fpr\n",
    "from aif360.sklearn.metrics import generalized_fnr, difference"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zHi_Esd7QKUW"
   },
   "source": [
    "## Loading data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JBSgJWDJQKUX"
   },
   "source": [
    "Datasets are formatted as separate `X` (# samples x # features) and `y` (# samples x # labels) DataFrames. The index of each DataFrame contains protected attribute values per sample. Datasets may also load a `sample_weight` object to be used with certain algorithms/metrics. All of this makes it so that aif360 is compatible with scikit-learn objects.\n",
    "\n",
    "For example, we can easily load the Adult dataset from UCI with the following line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 238
    },
    "id": "CVuqa6BpQKUX",
    "outputId": "cc44625b-359a-46cd-fc15-cc15e90412b3"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>age</th>\n",
       "      <th>workclass</th>\n",
       "      <th>education</th>\n",
       "      <th>education-num</th>\n",
       "      <th>marital-status</th>\n",
       "      <th>occupation</th>\n",
       "      <th>relationship</th>\n",
       "      <th>race</th>\n",
       "      <th>sex</th>\n",
       "      <th>capital-gain</th>\n",
       "      <th>capital-loss</th>\n",
       "      <th>hours-per-week</th>\n",
       "      <th>native-country</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>race</th>\n",
       "      <th>sex</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Non-white</th>\n",
       "      <th>Male</th>\n",
       "      <td>25.0</td>\n",
       "      <td>Private</td>\n",
       "      <td>11th</td>\n",
       "      <td>7.0</td>\n",
       "      <td>Never-married</td>\n",
       "      <td>Machine-op-inspct</td>\n",
       "      <td>Own-child</td>\n",
       "      <td>Black</td>\n",
       "      <td>Male</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>United-States</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th rowspan=\"2\" valign=\"top\">White</th>\n",
       "      <th>Male</th>\n",
       "      <td>38.0</td>\n",
       "      <td>Private</td>\n",
       "      <td>HS-grad</td>\n",
       "      <td>9.0</td>\n",
       "      <td>Married-civ-spouse</td>\n",
       "      <td>Farming-fishing</td>\n",
       "      <td>Husband</td>\n",
       "      <td>White</td>\n",
       "      <td>Male</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>United-States</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Male</th>\n",
       "      <td>28.0</td>\n",
       "      <td>Local-gov</td>\n",
       "      <td>Assoc-acdm</td>\n",
       "      <td>12.0</td>\n",
       "      <td>Married-civ-spouse</td>\n",
       "      <td>Protective-serv</td>\n",
       "      <td>Husband</td>\n",
       "      <td>White</td>\n",
       "      <td>Male</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>United-States</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Non-white</th>\n",
       "      <th>Male</th>\n",
       "      <td>44.0</td>\n",
       "      <td>Private</td>\n",
       "      <td>Some-college</td>\n",
       "      <td>10.0</td>\n",
       "      <td>Married-civ-spouse</td>\n",
       "      <td>Machine-op-inspct</td>\n",
       "      <td>Husband</td>\n",
       "      <td>Black</td>\n",
       "      <td>Male</td>\n",
       "      <td>7688.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>United-States</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>White</th>\n",
       "      <th>Male</th>\n",
       "      <td>34.0</td>\n",
       "      <td>Private</td>\n",
       "      <td>10th</td>\n",
       "      <td>6.0</td>\n",
       "      <td>Never-married</td>\n",
       "      <td>Other-service</td>\n",
       "      <td>Not-in-family</td>\n",
       "      <td>White</td>\n",
       "      <td>Male</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>30.0</td>\n",
       "      <td>United-States</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                 age  workclass     education  education-num  \\\n",
       "race      sex                                                  \n",
       "Non-white Male  25.0    Private          11th            7.0   \n",
       "White     Male  38.0    Private       HS-grad            9.0   \n",
       "          Male  28.0  Local-gov    Assoc-acdm           12.0   \n",
       "Non-white Male  44.0    Private  Some-college           10.0   \n",
       "White     Male  34.0    Private          10th            6.0   \n",
       "\n",
       "                    marital-status         occupation   relationship   race  \\\n",
       "race      sex                                                                 \n",
       "Non-white Male       Never-married  Machine-op-inspct      Own-child  Black   \n",
       "White     Male  Married-civ-spouse    Farming-fishing        Husband  White   \n",
       "          Male  Married-civ-spouse    Protective-serv        Husband  White   \n",
       "Non-white Male  Married-civ-spouse  Machine-op-inspct        Husband  Black   \n",
       "White     Male       Never-married      Other-service  Not-in-family  White   \n",
       "\n",
       "                 sex  capital-gain  capital-loss  hours-per-week  \\\n",
       "race      sex                                                      \n",
       "Non-white Male  Male           0.0           0.0            40.0   \n",
       "White     Male  Male           0.0           0.0            50.0   \n",
       "          Male  Male           0.0           0.0            40.0   \n",
       "Non-white Male  Male        7688.0           0.0            40.0   \n",
       "White     Male  Male           0.0           0.0            30.0   \n",
       "\n",
       "               native-country  \n",
       "race      sex                  \n",
       "Non-white Male  United-States  \n",
       "White     Male  United-States  \n",
       "          Male  United-States  \n",
       "Non-white Male  United-States  \n",
       "White     Male  United-States  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X, y, sample_weight = fetch_adult()\n",
    "X.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YoUxNPTHQKUY"
   },
   "source": [
    "We can then map the protected attributes to integers,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "KoIzyUpIQKUY"
   },
   "outputs": [],
   "source": [
    "X.index = pd.MultiIndex.from_arrays(X.index.codes, names=X.index.names)\n",
    "y.index = pd.MultiIndex.from_arrays(y.index.codes, names=y.index.names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "FpMD-pRaQKUY"
   },
   "source": [
    "and the target classes to 0/1,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "id": "4FuupoA3QKUY"
   },
   "outputs": [],
   "source": [
    "y = pd.Series(y.factorize(sort=True)[0], index=y.index)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3T-NvfWyQKUZ"
   },
   "source": [
    "split the dataset,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "Vc4qGCNGQKUZ"
   },
   "outputs": [],
   "source": [
    "(X_train, X_test,\n",
    " y_train, y_test) = train_test_split(X, y, train_size=0.7, random_state=1234567)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1YBoWRhSQKUZ"
   },
   "source": [
    "and finally, one-hot encode the categorical features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 366
    },
    "id": "D1RI5-mUQKUZ",
    "outputId": "633dc1a0-c526-4d4f-9af0-21550a873740"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>workclass_Federal-gov</th>\n",
       "      <th>workclass_Local-gov</th>\n",
       "      <th>workclass_Private</th>\n",
       "      <th>workclass_Self-emp-inc</th>\n",
       "      <th>workclass_Self-emp-not-inc</th>\n",
       "      <th>workclass_State-gov</th>\n",
       "      <th>workclass_Without-pay</th>\n",
       "      <th>education_10th</th>\n",
       "      <th>education_11th</th>\n",
       "      <th>education_12th</th>\n",
       "      <th>...</th>\n",
       "      <th>native-country_Thailand</th>\n",
       "      <th>native-country_Trinadad&amp;Tobago</th>\n",
       "      <th>native-country_United-States</th>\n",
       "      <th>native-country_Vietnam</th>\n",
       "      <th>native-country_Yugoslavia</th>\n",
       "      <th>age</th>\n",
       "      <th>education-num</th>\n",
       "      <th>capital-gain</th>\n",
       "      <th>capital-loss</th>\n",
       "      <th>hours-per-week</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>race</th>\n",
       "      <th>sex</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th rowspan=\"5\" valign=\"top\">1</th>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>58.0</td>\n",
       "      <td>11.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>42.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>51.0</td>\n",
       "      <td>12.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>30.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>26.0</td>\n",
       "      <td>14.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1887.0</td>\n",
       "      <td>40.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>44.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>33.0</td>\n",
       "      <td>6.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 103 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "          workclass_Federal-gov  workclass_Local-gov  workclass_Private  \\\n",
       "race sex                                                                  \n",
       "1    1                      0.0                  0.0                0.0   \n",
       "     0                      0.0                  0.0                0.0   \n",
       "     1                      0.0                  0.0                1.0   \n",
       "     1                      0.0                  0.0                1.0   \n",
       "     1                      0.0                  0.0                1.0   \n",
       "\n",
       "          workclass_Self-emp-inc  workclass_Self-emp-not-inc  \\\n",
       "race sex                                                       \n",
       "1    1                       0.0                         1.0   \n",
       "     0                       0.0                         1.0   \n",
       "     1                       0.0                         0.0   \n",
       "     1                       0.0                         0.0   \n",
       "     1                       0.0                         0.0   \n",
       "\n",
       "          workclass_State-gov  workclass_Without-pay  education_10th  \\\n",
       "race sex                                                               \n",
       "1    1                    0.0                    0.0             0.0   \n",
       "     0                    0.0                    0.0             0.0   \n",
       "     1                    0.0                    0.0             0.0   \n",
       "     1                    0.0                    0.0             0.0   \n",
       "     1                    0.0                    0.0             1.0   \n",
       "\n",
       "          education_11th  education_12th  ...  native-country_Thailand  \\\n",
       "race sex                                  ...                            \n",
       "1    1               0.0             0.0  ...                      0.0   \n",
       "     0               0.0             0.0  ...                      0.0   \n",
       "     1               0.0             0.0  ...                      0.0   \n",
       "     1               0.0             0.0  ...                      0.0   \n",
       "     1               0.0             0.0  ...                      0.0   \n",
       "\n",
       "          native-country_Trinadad&Tobago  native-country_United-States  \\\n",
       "race sex                                                                 \n",
       "1    1                               0.0                           1.0   \n",
       "     0                               0.0                           0.0   \n",
       "     1                               0.0                           1.0   \n",
       "     1                               0.0                           0.0   \n",
       "     1                               0.0                           1.0   \n",
       "\n",
       "          native-country_Vietnam  native-country_Yugoslavia   age  \\\n",
       "race sex                                                            \n",
       "1    1                       0.0                        0.0  58.0   \n",
       "     0                       0.0                        0.0  51.0   \n",
       "     1                       0.0                        0.0  26.0   \n",
       "     1                       0.0                        0.0  44.0   \n",
       "     1                       0.0                        0.0  33.0   \n",
       "\n",
       "          education-num  capital-gain  capital-loss  hours-per-week  \n",
       "race sex                                                             \n",
       "1    1             11.0           0.0           0.0            42.0  \n",
       "     0             12.0           0.0           0.0            30.0  \n",
       "     1             14.0           0.0        1887.0            40.0  \n",
       "     1              3.0           0.0           0.0            40.0  \n",
       "     1              6.0           0.0           0.0            40.0  \n",
       "\n",
       "[5 rows x 103 columns]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ohe = make_column_transformer(\n",
    "        (OneHotEncoder(sparse=False), X_train.dtypes == 'category'),\n",
    "        remainder='passthrough', verbose_feature_names_out=False)\n",
    "X_train  = pd.DataFrame(ohe.fit_transform(X_train), columns=ohe.get_feature_names_out(), index=X_train.index)\n",
    "X_test = pd.DataFrame(ohe.transform(X_test), columns=ohe.get_feature_names_out(), index=X_test.index)\n",
    "\n",
    "X_train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TsmP5ozGQKUa"
   },
   "source": [
    "Note: the column names are lost in this transformation. The same encoding can be done with Pandas, but this cannot be combined with other preprocessing in a Pipeline."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 366
    },
    "id": "_pBLAXtIQKUa",
    "outputId": "b18a38cf-fa47-4728-c66b-0c258b625e21"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th>age</th>\n",
       "      <th>education-num</th>\n",
       "      <th>capital-gain</th>\n",
       "      <th>capital-loss</th>\n",
       "      <th>hours-per-week</th>\n",
       "      <th>workclass_Private</th>\n",
       "      <th>workclass_Self-emp-not-inc</th>\n",
       "      <th>workclass_Self-emp-inc</th>\n",
       "      <th>workclass_Federal-gov</th>\n",
       "      <th>workclass_Local-gov</th>\n",
       "      <th>...</th>\n",
       "      <th>native-country_Guatemala</th>\n",
       "      <th>native-country_Nicaragua</th>\n",
       "      <th>native-country_Scotland</th>\n",
       "      <th>native-country_Thailand</th>\n",
       "      <th>native-country_Yugoslavia</th>\n",
       "      <th>native-country_El-Salvador</th>\n",
       "      <th>native-country_Trinadad&amp;Tobago</th>\n",
       "      <th>native-country_Peru</th>\n",
       "      <th>native-country_Hong</th>\n",
       "      <th>native-country_Holand-Netherlands</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>race</th>\n",
       "      <th>sex</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <th>1</th>\n",
       "      <td>25.0</td>\n",
       "      <td>7.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th rowspan=\"2\" valign=\"top\">1</th>\n",
       "      <th>1</th>\n",
       "      <td>38.0</td>\n",
       "      <td>9.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>50.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>28.0</td>\n",
       "      <td>12.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <th>1</th>\n",
       "      <td>44.0</td>\n",
       "      <td>10.0</td>\n",
       "      <td>7688.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>40.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <th>1</th>\n",
       "      <td>34.0</td>\n",
       "      <td>6.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>30.0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 103 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "           age  education-num  capital-gain  capital-loss  hours-per-week  \\\n",
       "race sex                                                                    \n",
       "0    1    25.0            7.0           0.0           0.0            40.0   \n",
       "1    1    38.0            9.0           0.0           0.0            50.0   \n",
       "     1    28.0           12.0           0.0           0.0            40.0   \n",
       "0    1    44.0           10.0        7688.0           0.0            40.0   \n",
       "1    1    34.0            6.0           0.0           0.0            30.0   \n",
       "\n",
       "          workclass_Private  workclass_Self-emp-not-inc  \\\n",
       "race sex                                                  \n",
       "0    1                    1                           0   \n",
       "1    1                    1                           0   \n",
       "     1                    0                           0   \n",
       "0    1                    1                           0   \n",
       "1    1                    1                           0   \n",
       "\n",
       "          workclass_Self-emp-inc  workclass_Federal-gov  workclass_Local-gov  \\\n",
       "race sex                                                                       \n",
       "0    1                         0                      0                    0   \n",
       "1    1                         0                      0                    0   \n",
       "     1                         0                      0                    1   \n",
       "0    1                         0                      0                    0   \n",
       "1    1                         0                      0                    0   \n",
       "\n",
       "          ...  native-country_Guatemala  native-country_Nicaragua  \\\n",
       "race sex  ...                                                       \n",
       "0    1    ...                         0                         0   \n",
       "1    1    ...                         0                         0   \n",
       "     1    ...                         0                         0   \n",
       "0    1    ...                         0                         0   \n",
       "1    1    ...                         0                         0   \n",
       "\n",
       "          native-country_Scotland  native-country_Thailand  \\\n",
       "race sex                                                     \n",
       "0    1                          0                        0   \n",
       "1    1                          0                        0   \n",
       "     1                          0                        0   \n",
       "0    1                          0                        0   \n",
       "1    1                          0                        0   \n",
       "\n",
       "          native-country_Yugoslavia  native-country_El-Salvador  \\\n",
       "race sex                                                          \n",
       "0    1                            0                           0   \n",
       "1    1                            0                           0   \n",
       "     1                            0                           0   \n",
       "0    1                            0                           0   \n",
       "1    1                            0                           0   \n",
       "\n",
       "          native-country_Trinadad&Tobago  native-country_Peru  \\\n",
       "race sex                                                        \n",
       "0    1                                 0                    0   \n",
       "1    1                                 0                    0   \n",
       "     1                                 0                    0   \n",
       "0    1                                 0                    0   \n",
       "1    1                                 0                    0   \n",
       "\n",
       "          native-country_Hong  native-country_Holand-Netherlands  \n",
       "race sex                                                          \n",
       "0    1                      0                                  0  \n",
       "1    1                      0                                  0  \n",
       "     1                      0                                  0  \n",
       "0    1                      0                                  0  \n",
       "1    1                      0                                  0  \n",
       "\n",
       "[5 rows x 103 columns]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.get_dummies(X).head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8bMlpomVQKUa"
   },
   "source": [
    "The protected attribute information is also replicated in the labels:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "HSpthk1lQKUa",
    "outputId": "b98fb738-2dd1-44b7-fe64-2dbbcb59a6a2"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "race  sex\n",
       "1     1      0\n",
       "      0      1\n",
       "      1      1\n",
       "      1      0\n",
       "      1      0\n",
       "dtype: int64"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "13UfQJx_QKUb"
   },
   "source": [
    "## Running metrics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iMTOzFVLQKUb"
   },
   "source": [
    "With the data in this format, we can easily train a scikit-learn model and get predictions for the test data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "tHeMqub4QKUb",
    "outputId": "7b06fd6c-bbc9-45d4-b6f2-daf3455a44fa"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8455074813886637"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_pred = LogisticRegression(solver='liblinear').fit(X_train, y_train).predict(X_test)\n",
    "accuracy_score(y_test, y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "aVu8VzgpQKUb"
   },
   "source": [
    "Now, we can analyze our predictions and quickly calucate the disparate impact for females vs. males:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "8mVD0ZWSQKUb",
    "outputId": "204a3f64-8306-469f-c39a-2fbf90f17039"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.26889803976599136"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "disparate_impact_ratio(y_test, y_pred, prot_attr='sex')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "cNonH6t1QKUc"
   },
   "source": [
    "And similarly, we can assess how close the predictions are to equality of odds.\n",
    "\n",
    "`average_odds_error()` computes the (unweighted) average of the absolute values of the true positive rate (TPR) difference and false positive rate (FPR) difference, i.e.:\n",
    "\n",
    "$$ \\tfrac{1}{2}\\left(|FPR_{D = \\text{unprivileged}} - FPR_{D = \\text{privileged}}| + |TPR_{D = \\text{unprivileged}} - TPR_{D = \\text{privileged}}|\\right) $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "C5_ZlM4dQKUc",
    "outputId": "2fd17013-5f9f-4131-8826-d3a50b7ef713"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.09875694175767563"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "average_odds_error(y_test, y_pred, priv_group=(1, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "H0VZVtSrQKUc"
   },
   "source": [
    "In that case, we chose to look at the intersection of all protected attributes (race and sex) and designate a single combination (white males) as privileged.\n",
    "\n",
    "If we wish to do something more complex, we can pass a custom array of protected attributes, like so (note: this choice of protected groups is just for demonstration):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "LmcK2STxQKUc",
    "outputId": "eccc550b-b7cb-49c7-dca7-5ef1f2d621c2"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.3844295196608744"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "race = y_test.index.get_level_values('race').to_numpy()\n",
    "sex = y_test.index.get_level_values('sex').to_numpy()\n",
    "prot_attr = np.where(race ^ sex, 0, 1)\n",
    "disparate_impact_ratio(y_test, y_pred, prot_attr=prot_attr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vZRn0EJiQKUd"
   },
   "source": [
    "## Debiasing algorithms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "FJhOugvqQKUd"
   },
   "source": [
    "`ReweighingMeta` is a workaround until changing sample weights can be handled properly in `Pipeline`/`GridSearchCV`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "KyGPWqeVQKUd",
    "outputId": "83e7a7b8-a6a5-4c27-fd73-fb300bb00b58"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.8402004864745338\n",
      "{'estimator__C': 1}\n"
     ]
    }
   ],
   "source": [
    "rew = ReweighingMeta(estimator=LogisticRegression(solver='liblinear'),\n",
    "                     reweigher=Reweighing('sex'))\n",
    "\n",
    "params = {'estimator__C': [1, 10]}\n",
    "\n",
    "clf = GridSearchCV(rew, params, scoring='accuracy', cv=5)\n",
    "clf.fit(X_train, y_train)\n",
    "print(clf.score(X_test, y_test))\n",
    "print(clf.best_params_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WGz3Hn8UQKUd",
    "outputId": "7f2e11c0-dd77-4a0b-b813-02961a3f756a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5874230323221501"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "disparate_impact_ratio(y_test, clf.predict(X_test), prot_attr='sex')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "svwzVk2DQKUe"
   },
   "source": [
    "Rather than trying to weight accuracy and fairness, we can try a fair in-processing algorithm:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "waeF--xEQKUe",
    "outputId": "e68f4dbc-b526-49f2-e859-10d3e1f7d59a"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8345249502469226"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "adv_deb = AdversarialDebiasing(prot_attr='sex', random_state=1234567)\n",
    "adv_deb.fit(X_train, y_train)\n",
    "adv_deb.score(X_test, y_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "PEXZO3CsQKUe",
    "outputId": "31b00ed9-8ccd-4cb6-b55a-a45bbca77368"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.1200164835077702"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "average_odds_error(y_test, adv_deb.predict(X_test), prot_attr='sex')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ihLM59GVQKUe"
   },
   "source": [
    "Note that `AdversarialDebiasing` creates a TensorFlow session which we should close when we're finished to free up resources:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "id": "pm75UZQEQKUe"
   },
   "outputs": [],
   "source": [
    "adv_deb.sess_.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "MwUp9d4GQKUf"
   },
   "source": [
    "Finally, let's try a post-processor, `CalibratedEqualizedOdds`.\n",
    "\n",
    "Since the post-processor needs to be trained on data unseen by the original estimator, we will use the `PostProcessingMeta` class which splits the data and trains the estimator and post-processor with their own split."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "dCcMevWgQKUf",
    "outputId": "213d4a62-3898-4fb8-a61e-c56395d53660"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8200044224957618"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cal_eq_odds = CalibratedEqualizedOdds('sex', cost_constraint='fnr', random_state=1234567)\n",
    "log_reg = LogisticRegression(solver='liblinear')\n",
    "postproc = PostProcessingMeta(estimator=log_reg, postprocessor=cal_eq_odds, random_state=1234567)\n",
    "\n",
    "postproc.fit(X_train, y_train)\n",
    "accuracy_score(y_test, postproc.predict(X_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 283
    },
    "id": "oSElX6mEQKUf",
    "outputId": "7c6189a7-b615-4ce1-bf6a-ffdac35b0d49"
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 30960 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "y_pred = postproc.predict_proba(X_test)[:, 1]\n",
    "y_lr = postproc.estimator_.predict_proba(X_test)[:, 1]\n",
    "br = postproc.postprocessor_.base_rates_\n",
    "i = X_test.index.get_level_values('sex') == 1\n",
    "\n",
    "plt.plot([0, br[0]], [0, 1-br[0]], '-b', label='All calibrated classifiers (Females)')\n",
    "plt.plot([0, br[1]], [0, 1-br[1]], '-r', label='All calibrated classifiers (Males)')\n",
    "\n",
    "plt.scatter(generalized_fpr(y_test[~i], y_lr[~i]),\n",
    "            generalized_fnr(y_test[~i], y_lr[~i]),\n",
    "            300, c='b', marker='.', label='Original classifier (Females)')\n",
    "plt.scatter(generalized_fpr(y_test[i], y_lr[i]),\n",
    "            generalized_fnr(y_test[i], y_lr[i]),\n",
    "            300, c='r', marker='.', label='Original classifier (Males)')\n",
    "                                                                        \n",
    "plt.scatter(generalized_fpr(y_test[~i], y_pred[~i]),\n",
    "            generalized_fnr(y_test[~i], y_pred[~i]),\n",
    "            100, c='b', marker='d', label='Post-processed classifier (Females)')\n",
    "plt.scatter(generalized_fpr(y_test[i], y_pred[i]),\n",
    "            generalized_fnr(y_test[i], y_pred[i]),\n",
    "            100, c='r', marker='d', label='Post-processed classifier (Males)')\n",
    "\n",
    "plt.plot([0, 1], [generalized_fnr(y_test, y_pred)]*2, '--', c='0.5')\n",
    "\n",
    "plt.axis('square')\n",
    "plt.xlim([0.0, 0.4])\n",
    "plt.ylim([0.3, 0.7])\n",
    "plt.xlabel('generalized fpr');\n",
    "plt.ylabel('generalized fnr');\n",
    "plt.legend(bbox_to_anchor=(1.04,1), loc='upper left');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "na2YWDICQKUg"
   },
   "source": [
    "We can see the generalized false negative rate is approximately equalized and the classifiers remain close to the calibration lines.\n",
    "\n",
    "We can quanitify the discrepancy between protected groups using the `difference` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "smrTomVyQKUg",
    "outputId": "6add1353-fae6-474a-b9e8-5d1be8cd8a39"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0006901337553552045"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "difference(generalized_fnr, y_test, y_pred, prot_attr='sex')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "vscode": {
   "interpreter": {
    "hash": "d0c5ced7753e77a483fec8ff7063075635521cce6e0bd54998c8f174742209dd"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
