{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Basic usage example of ``imbalanced_ensemble``\n\nThis example shows the basic usage of the ensemble estimators \n(:class:`imbalanced_ensemble.ensemble.under_sampling.SelfPacedEnsembleClassifier`,\n:class:`imbalanced_ensemble.ensemble.under_sampling.RUSBoostClassifier`,\n:class:`imbalanced_ensemble.ensemble.under_sampling.EasyEnsembleClassifier`,\n:class:`imbalanced_ensemble.ensemble.under_sampling.BalancedRandomForestClassifier`,\n:class:`imbalanced_ensemble.ensemble.over_sampling.SMOTEBoostClassifier`,\n:class:`imbalanced_ensemble.ensemble.over_sampling.OverBaggingClassifier`,\n) in :mod:`imbalanced_ensemble.ensemble` module. \n\nWe also show how to use the :class:`imbalanced_ensemble.visualizer.ImbalancedEnsembleVisualizer` \nto visualize and compare different ensemble classifiers.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Authors: Zhining Liu <zhining.liu@outlook.com>\n# License: MIT"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "print(__doc__)\n\nimport imbalanced_ensemble as imbens\n\nRANDOM_STATE = 0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Import imbalanced_ensemble\n\nFirst, we will import necessary packages and implement \nsome utilities for data visualization\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from imbalanced_ensemble.ensemble.under_sampling import SelfPacedEnsembleClassifier\nfrom imbalanced_ensemble.ensemble.under_sampling import RUSBoostClassifier\nfrom imbalanced_ensemble.ensemble.under_sampling import EasyEnsembleClassifier\nfrom imbalanced_ensemble.ensemble.under_sampling import BalancedRandomForestClassifier\n\nfrom imbalanced_ensemble.ensemble.over_sampling import SMOTEBoostClassifier\nfrom imbalanced_ensemble.ensemble.over_sampling import OverBaggingClassifier\n\nimport time\nimport pandas as pd\nfrom collections import Counter\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom sklearn.decomposition import KernelPCA\nfrom sklearn.datasets import make_classification\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, balanced_accuracy_score, f1_score\n\n# implement some utilities for data visualization\n\nvis_params = {\n    'palette': plt.cm.rainbow,\n    'cmap': plt.cm.rainbow,\n    'edgecolor': 'black',\n    'alpha': 0.6,\n}\n\ndef set_ax_border(ax, border_color='black', border_width=2):\n    for _, spine in ax.spines.items():\n        spine.set_color(border_color)\n        spine.set_linewidth(border_width)\n        \n    return ax\n\ndef plot_scatter(X, y, ax=None, weights=None, title='',\n                 projection=None, vis_params=vis_params):\n    if ax is None:\n        ax = plt.axes()\n    X_vis = projection.transform(X) if X.shape[1] > 2 else X\n    title += ' (2D projection by {})'.format(\n        str(projection.__class__).split('.')[-1][:-2]\n    )\n    size = 50 if weights is None else weights\n    sns.scatterplot(x=X_vis[:, 0], y=X_vis[:, 1], \n        hue=y, style=y, s=size, **vis_params, legend='full', ax=ax)\n    \n    ax.set_title(title)\n    ax = set_ax_border(ax, border_color='black', border_width=2)\n    ax.grid(color='black', linestyle='-.', alpha=0.5)\n    \n    return ax\n\ndef plot_class_distribution(y, ax=None, title='', \n                            sort_values=False, plot_average=True):\n    count = pd.DataFrame(list(Counter(y).items()), \n                         columns=['Class', 'Frequency'])\n    if sort_values:\n        count = count.sort_values(by='Frequency', ascending=False)\n    if ax is None:\n        ax = plt.axes()\n    count.plot.bar(x='Class', y='Frequency', title=title, ax=ax)\n    \n    ax.set_title(title)\n    ax = set_ax_border(ax, border_color='black', border_width=2)\n    ax.grid(color='black', linestyle='-.', alpha=0.5, axis='y')\n\n    if plot_average:\n        ax.axhline(y=count['Frequency'].mean(),ls=\"dashdot\",c=\"red\")\n        xlim_min, xlim_max, ylim_min, ylim_max = ax.axis()\n        ax.text(\n            x=xlim_min+(xlim_max-xlim_min)*0.82,\n            y=count['Frequency'].mean()+(ylim_max-ylim_min)*0.03,\n            c=\"red\",s='Average')\n    \n    return ax\n\ndef plot_2Dprojection_and_cardinality(X, y, figsize=(10, 4), vis_params=vis_params,\n                                     projection=None, weights=None, plot_average=True,\n                                     title1='Dataset', title2='Class Distribution'):\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)\n    if projection == None:\n        projection = KernelPCA(n_components=2).fit(X, y)\n    ax1 = plot_scatter(X, y, ax=ax1, weights=weights, title=title1, \n                    projection=projection, vis_params=vis_params)\n    ax2 = plot_class_distribution(y, ax=ax2, title=title2, \n                    sort_values=True, plot_average=plot_average)\n    plt.tight_layout()\n    return fig"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Make a toy 3-class imbalanced classification task\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "X, y = make_classification(n_classes=3, class_sep=2, # 3-class\n    weights=[0.1, 0.3, 0.6], n_informative=3, n_redundant=1, flip_y=0,\n    n_features=20, n_clusters_per_class=2, n_samples=2000, random_state=0)\n\nX_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.5, random_state=42)\n\norigin_distr = dict(Counter(y_train)) # {2: 600, 1: 300, 0: 100}\nprint('Original training dataset shape %s' % origin_distr)\n\n# Visualize the dataset\nprojection = KernelPCA(n_components=2).fit(X, y)\nfig = plot_2Dprojection_and_cardinality(X, y, projection=projection)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Train some imbalanced_ensemble classifiers\n(with `train_verbose` enabled)\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Set training parameters\ninit_kwargs = {\n    'n_estimators': 50,\n    'random_state': RANDOM_STATE,\n}\nfit_kwargs = {\n    'X': X_train,\n    'y': y_train,\n    'eval_datasets': {'valid': (X_valid, y_valid)},\n    'eval_metrics': {\n        'acc': (accuracy_score, {}),\n        'balanced_acc': (balanced_accuracy_score, {}),\n        'weighted_f1': (f1_score, {'average':'weighted'}),\n    },\n    'train_verbose': True,\n}\n\n# Train ensemble estimators\nensembles = {}\n\nensembles['spe'] = spe = SelfPacedEnsembleClassifier(**init_kwargs)\nprint ('Training {} ...'.format(spe.__name__))\nstart_time = time.time()\nspe.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    spe.__name__, time.time() - start_time,\n))\n\nensembles['rusboost'] = rusboost = RUSBoostClassifier(**init_kwargs)\nprint ('Training {} ...'.format(rusboost.__name__))\nstart_time = time.time()\nrusboost.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    rusboost.__name__, time.time() - start_time,\n))\n\nensembles['easyens'] = easyens = EasyEnsembleClassifier(**init_kwargs)\nprint ('Training {} ...'.format(easyens.__name__))\nstart_time = time.time()\neasyens.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    easyens.__name__, time.time() - start_time,\n))\n\nensembles['balanced_rf'] = balanced_rf = BalancedRandomForestClassifier(**init_kwargs)\nprint ('Training {} ...'.format(balanced_rf.__name__))\nstart_time = time.time()\nbalanced_rf.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    balanced_rf.__name__, time.time() - start_time,\n))\n\nensembles['smoteboost'] = smoteboost = SMOTEBoostClassifier(**init_kwargs)\nprint ('Training {} ...'.format(smoteboost.__name__))\nstart_time = time.time()\nsmoteboost.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    smoteboost.__name__, time.time() - start_time,\n))\n\nensembles['overbagging'] = overbagging = OverBaggingClassifier(**init_kwargs)\nprint ('Training {} ...'.format(overbagging.__name__))\nstart_time = time.time()\noverbagging.fit(**fit_kwargs)\nprint ('Running time of {}.fit(): {:.4f}s\\n'.format(\n    overbagging.__name__, time.time() - start_time,\n))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Visualize the results with ImbalancedEnsembleVisualizer\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from imbalanced_ensemble.visualizer import ImbalancedEnsembleVisualizer\n\n# Fit visualizer\nvisualizer = ImbalancedEnsembleVisualizer().fit(\n    ensembles = ensembles,\n    granularity = 10,\n)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Plot performance curves w.r.t. number of base estimators\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axes = visualizer.performance_lineplot(\n    n_samples_as_x_axis=False,\n    alpha=0.7,\n)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Plot performance curves w.r.t. number of training samples (split subfigures by datasets)\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axes = visualizer.performance_lineplot(\n    split_by=['dataset'],\n    n_samples_as_x_axis=True,\n    alpha=0.7,\n)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Plot confusion matrices for selected methods/datasets\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axes = visualizer.confusion_matrix_heatmap(\n    on_ensembles=['spe', 'smoteboost'],\n    on_datasets=['valid'],\n    sub_figsize=(4, 3.3),\n)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Plot confusion matrices for all methods/datasets\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "fig, axes = visualizer.confusion_matrix_heatmap(\n    sub_figsize=(4, 3.3),\n)\nplt.show()\n\n# %%"
      ]
    }
  ],
  "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.8.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}