Source code for transparentai.models.classification.classification_plots

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

from ..classification import compute_metrics
from ..evaluation import evaluation

from transparentai import plots


[docs]def plot_confusion_matrix(confusion_matrix): """ Show confusion matrix. Parameters ---------- confusion_matrix: array confusion_matrix metrics result """ n_classes = len(confusion_matrix) sns.heatmap(confusion_matrix, cmap='Blues', square=True, fmt='d', annot=True) plt.xlabel('Predicted') plt.ylabel('Real') plt.title('Confusion Matrix')
[docs]def plot_roc_curve(roc_curve, roc_auc): """ Show a roc curve plot with roc_auc score on the legend. Parameters ---------- roc_curve: array roc_curve metrics result for each class roc_auc: array roc_auc metrics result for each class """ fpr, tpr = dict(), dict() n_classes = len(roc_curve) for v in range(n_classes): fpr[v] = roc_curve[v][0] tpr[v] = roc_curve[v][1] lw = 2 colors = sns.color_palette("colorblind", n_classes) for v, color in zip(range(n_classes), colors): n = 1 if n_classes == 1 else v plt.plot(fpr[v], tpr[v], color=color, lw=lw, label='ROC curve of class {0} (area = {1:0.2f})' ''.format(n, roc_auc[v])) if n_classes <= 2: break plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver Operating Characteristic curve') plt.legend(loc="lower right")
SCORE_PLOT_FUNCTION = { 'confusion_matrix': plot_confusion_matrix, 'roc_auc': plot_roc_curve }
[docs]def plot_table_score_clf(perf): """Insert a table of scores on a matplotlib graphic for a classifier Parameters ---------- perf: dict Dictionnary with computed score """ perf_clf = {} for k, v in perf.items(): if k in SCORE_PLOT_FUNCTION: continue perf_clf[k] = v return plots.plot_table_score(perf_clf)
[docs]def plot_score_function(perf, perf_prob, metric): """Plots score with a specific function. E.g. confusion_matrix or roc_auc Parameters ---------- perf: dict Dictionnary with computed score perf_prob: dict Dictionnary with computed score (using probabilities) metric: str name of the metric Raises ------ ValueError: metric does not have a plot function """ if metric not in SCORE_PLOT_FUNCTION: raise ValueError('metric does not have a plot function') fn = SCORE_PLOT_FUNCTION[metric] if metric == 'roc_auc': fn(perf_prob['roc_curve'], perf_prob['roc_auc']) elif metric in perf: fn(perf[metric]) else: fn(perf_prob[metric])
[docs]def compute_prob_performance(y_true, y_prob, metrics): """Computes performance that require probabilities Parameters ---------- y_true: array like True labels y_pred: array like Predicted labels metrics: list List of metrics to compute Returns ------- dict: Dictionnary of metrics computed that requires probabilities. If no metrics need those then it returns None Raises ------ TypeError: metrics must be a list """ if type(metrics) != list: raise TypeError('metrics must be a list') if type(y_prob) in [list, pd.Series, pd.DataFrame]: y_prob = np.array(y_prob) for m in metrics: if not evaluation.score_function_need_prob(m): metrics = [m1 for m1 in metrics if m1 != m] if len(metrics) == 0: return None if len(y_prob.shape) > 1: n_classes = y_prob.shape[1] if n_classes == 1: n_classes = 2 else: n_classes = 2 perf_prob = {} for m in metrics: perf_prob[m] = list() for c in range(n_classes): # If binary classifier then default class is 1 if n_classes == 2: c = 1 pred = y_prob else: pred = y_prob[:, c] y_true_c = np.array(y_true == c).astype(int) score = compute_metrics(y_true_c, pred, [m])[m] perf_prob[m].append(score) return perf_prob
[docs]def preprocess_scores(y_pred): """Preprocess y_pred for plot_performance function. if y_pred is probabilities then y_pred become predicted class, y_prob is the probabilities else, y_prob is None Parameters ---------- y_pred: array like (1D or 2D) if 1D array Predicted labels, if 2D array probabilities (returns of a predict_proba function) Returns ------- np.ndarray array with predicted labels np.ndarray array with probabilities if available else None int: number of classes """ if type(y_pred) in [list, pd.Series, pd.DataFrame]: y_pred = np.array(y_pred) y_prob = y_pred if len(y_pred.shape) > 1: n_classes = y_pred.shape[1] y_pred = np.argmax(y_pred, axis=1) else: max, min, uniq = np.max(y_pred), np.min(y_pred), len(np.unique(y_pred)) # If List of predicted class if (max == int(max)) & (min == int(min)) & (uniq <= max+1): n_classes = max # Else : list of probabilities else: n_classes = 2 return y_pred, y_prob, n_classes
[docs]def plot_performance(y_true, y_pred, y_true_valid=None, y_pred_valid=None, metrics=None, **kwargs): """Plots the performance of a classifier. You can use the metrics of your choice with the metrics argument Can compare train and validation set. Parameters ---------- y_true: array like True labels y_pred: array like (1D or 2D) if 1D array Predicted labels, if 2D array probabilities (returns of a predict_proba function) y_true_valid: array like (default None) True labels y_pred_valid: array like (1D or 2D) (default None) if 1D array Predicted labels, if 2D array probabilities (returns of a predict_proba function) metrics: list (default None) List of metrics to plots Raises ------ TypeError: if metrics is set it must be a list """ y_pred, y_prob, n_classes = preprocess_scores(y_pred) validation = (y_true_valid is not None) & (y_pred_valid is not None) if validation: y_pred_valid, y_prob_valid, _ = preprocess_scores(y_pred_valid) if metrics is None: metrics = ['accuracy', 'confusion_matrix', 'roc_auc', 'roc_curve'] if n_classes <= 2: metrics += ['f1', 'recall', 'precision'] else: metrics += ['f1_micro', 'recall_micro', 'precision_micro'] elif type(metrics) != list: raise TypeError('metrics must be a list') elif ('roc_auc' in metrics) & ('roc_curve' not in metrics): metrics.append('roc_curve') elif ('roc_auc' not in metrics) & ('roc_curve' in metrics): metrics.append('roc_auc') metrics_plot = [m for m in metrics if m in SCORE_PLOT_FUNCTION] # 1. Compute scores perf_prob = compute_prob_performance(y_true, y_prob, metrics) if validation: perf_prob_valid = compute_prob_performance( y_true_valid, y_prob_valid, metrics) if perf_prob is not None: metrics = [m for m in metrics if m not in perf_prob] if len(metrics) == 0: perf = None else: perf = compute_metrics(y_true, y_pred, metrics) if validation: perf_valid = compute_metrics(y_true_valid, y_pred_valid, metrics) # 2. If some metrics can be plot plot them if len(metrics_plot) > 0: n_fn = len(metrics_plot) n_rows = n_fn if validation else n_fn + n_fn % 2 row_size = 6 # Init figure fig = plt.figure(figsize=(15, row_size*n_rows+1)) widths = [1, 1] heights = [2] + [row_size]*n_rows gs = fig.add_gridspec(ncols=2, nrows=n_rows+1, wspace=0.7, width_ratios=widths, height_ratios=heights) r, c = 1, 0 # Header with scores if not validation: ax = fig.add_subplot(gs[0, :]) plot_table_score_clf(perf) else: ax = fig.add_subplot(gs[0, 0]) plot_table_score_clf(perf) ax = fig.add_subplot(gs[0, 1]) plot_table_score_clf(perf_valid) for m in metrics_plot: ax = fig.add_subplot(gs[r:r+1, c]) plot_score_function(perf, perf_prob, m) c += 1 if validation: ax = fig.add_subplot(gs[r:r+1, c]) plot_score_function(perf_valid, perf_prob_valid, m) c += 1 if c == 2: r += 1 c = 0 title = 'Model performance plot' if validation: title += ' train set (left) and test set (right)' fig.suptitle(title, fontsize=18) plt.show() return plots.plot_or_figure(fig, **kwargs)