def draw(self):
        """
        Renders the classification report across each axis.
        """
        # Create display grid
        cr_display = np.zeros((len(self.classes_), len(self._displayed_scores)))

        # For each class row, append columns for precision, recall, f1, and support
        for idx, cls in enumerate(self.classes_):
            for jdx, metric in enumerate(self._displayed_scores):
                cr_display[idx, jdx] = self.scores_[metric][cls]

        # Set up the dimensions of the pcolormesh
        # NOTE: pcolormesh accepts grids that are (N+1,M+1)
        X, Y = (
            np.arange(len(self.classes_) + 1),
            np.arange(len(self._displayed_scores) + 1),
        )
        self.ax.set_ylim(bottom=0, top=cr_display.shape[0])
        self.ax.set_xlim(left=0, right=cr_display.shape[1])

        # Set data labels in the grid, enumerating over class, metric pairs
        # NOTE: X and Y are one element longer than the classification report
        # so skip the last element to label the grid correctly.
        for x in X[:-1]:
            for y in Y[:-1]:

                # Extract the value and the text label
                value = cr_display[x, y]
                svalue = "{:0.3f}".format(value)

                # change the svalue for support (when y == 3) because we want
                # to label it as the actual support value, not the percentage
                if y == 3:
                    if self.support != PERCENT:
                        svalue = self.support_score_[x]

                # Determine the grid and text colors
                base_color = self.cmap(value)
                text_color = find_text_color(base_color)

                # Add the label to the middle of the grid
                cx, cy = x + 0.5, y + 0.5
                self.ax.text(cy, cx, svalue, va="center", ha="center", color=text_color)

        # Draw the heatmap with colors bounded by the min and max of the grid
        # NOTE: I do not understand why this is Y, X instead of X, Y it works
        # in this order but raises an exception with the other order.
        g = self.ax.pcolormesh(
            Y, X, cr_display, vmin=0, vmax=1, cmap=self.cmap, edgecolor="w"
        )

        # Add the color bar
        plt.colorbar(g, ax=self.ax)  # TODO: Could use self.fig now

        # Return the axes being drawn on
        return self.ax
예제 #2
0
    def draw(self):
        """
        Renders the classification report; must be called after score.
        """

        # Perform display related manipulations on the confusion matrix data
        cm_display = self.confusion_matrix_

        # Convert confusion matrix to percent of each row, i.e. the
        # predicted as a percent of true in each class.
        if self.percent is True:
            # Note: div_safe function returns 0 instead of NAN.
            cm_display = div_safe(self.confusion_matrix_,
                                  self.class_counts_.reshape(-1, 1))
            cm_display = np.round(cm_display * 100, decimals=0)

        # Y axis should be sorted top to bottom in pcolormesh
        cm_display = cm_display[::-1, ::]

        # Get the human readable labels
        labels = self._labels()
        if labels is None:
            labels = self.classes_

        # Set up the dimensions of the pcolormesh
        n_classes = len(labels)
        X, Y = np.arange(n_classes + 1), np.arange(n_classes + 1)
        self.ax.set_ylim(bottom=0, top=cm_display.shape[0])
        self.ax.set_xlim(left=0, right=cm_display.shape[1])

        # Fetch the grid labels from the classes in correct order; set ticks.
        xticklabels = labels
        yticklabels = labels[::-1]
        ticks = np.arange(n_classes) + 0.5

        self.ax.set(xticks=ticks, yticks=ticks)
        self.ax.set_xticklabels(xticklabels,
                                rotation="vertical",
                                fontsize=self.fontsize)
        self.ax.set_yticklabels(yticklabels, fontsize=self.fontsize)

        # Set data labels in the grid enumerating over all x,y class pairs.
        # NOTE: X and Y are one element longer than the confusion matrix, so
        # skip the last element in the enumeration to label grids.
        for x in X[:-1]:
            for y in Y[:-1]:

                # Extract the value and the text label
                value = cm_display[x, y]
                svalue = "{:0.0f}".format(value)
                if self.percent:
                    svalue += "%"

                # Determine the grid and text colors
                base_color = self.cmap(value / cm_display.max())
                text_color = find_text_color(base_color)

                # Make zero values more subtle
                if cm_display[x, y] == 0:
                    text_color = CMAP_MUTEDCOLOR

                # Add the label to the middle of the grid
                cx, cy = x + 0.5, y + 0.5
                self.ax.text(
                    cy,
                    cx,
                    svalue,
                    va="center",
                    ha="center",
                    color=text_color,
                    fontsize=self.fontsize,
                )

                # Add a dark line on the grid with the diagonal. Note that the
                # tick labels have already been reversed.
                lc = "k" if xticklabels[x] == yticklabels[y] else "w"
                self._edgecolors.append(lc)

        # Draw the heatmap with colors bounded by vmin,vmax
        vmin = 0.00001
        vmax = 99.999 if self.percent is True else cm_display.max()
        self.ax.pcolormesh(
            X,
            Y,
            cm_display,
            vmin=vmin,
            vmax=vmax,
            edgecolor=self._edgecolors,
            cmap=self.cmap,
            linewidth="0.01",
        )

        # Return the axes being drawn on
        return self.ax
예제 #3
0
    def ClassificationReportViz(self, support=True):
        displayed_scores = [
            key for key in ("precision", "recall", "f1", "support")
        ]

        results = precision_recall_fscore_support(self.y_true, self.y_pred)
        scores = map(lambda s: dict(zip(self.classes, s)), results)
        scores_ = dict(zip(tuple(displayed_scores), scores))

        if not support:
            displayed_scores.remove("support")
            scores_.pop("support")

        # Create display grid
        cr_display = np.zeros((len(self.classes), len(displayed_scores)))

        # For each class row, append columns for precision, recall, f1, and support
        for idx, cls in enumerate(self.classes):
            for jdx, metric in enumerate(displayed_scores):
                cr_display[idx, jdx] = scores_[metric][cls]

        # Set up the dimensions of the pcolormesh
        # NOTE: pcolormesh accepts grids that are (N+1,M+1)
        X, Y = (
            np.arange(len(self.classes) + 1),
            np.arange(len(displayed_scores) + 1),
        )

        fig, ax = plt.subplots(ncols=1, nrows=1)

        ax.set_ylim(bottom=0, top=cr_display.shape[0])
        ax.set_xlim(left=0, right=cr_display.shape[1])

        # Set data labels in the grid, enumerating over class, metric pairs
        # NOTE: X and Y are one element longer than the classification report
        # so skip the last element to label the grid correctly.
        for x in X[:-1]:
            for y in Y[:-1]:

                # Extract the value and the text label
                value = cr_display[x, y]
                svalue = "{:0.3f}".format(value)

                if y == 3:
                    value = cr_display[x, y]
                    svalue = "{:0.0f}".format(value)

                # Determine the grid and text colors
                base_color = self.cmap(value)
                text_color = find_text_color(base_color)

                # Add the label to the middle of the grid
                cx, cy = x + 0.5, y + 0.5
                ax.text(cy,
                        cx,
                        svalue,
                        va="center",
                        ha="center",
                        color=text_color)

        # Draw the heatmap with colors bounded by the min and max of the grid
        # NOTE: I do not understand why this is Y, X instead of X, Y it works
        # in this order but raises an exception with the other order.
        g = ax.pcolormesh(Y,
                          X,
                          cr_display,
                          vmin=0,
                          vmax=1,
                          cmap=self.cmap,
                          edgecolor="w")

        # Add the color bar
        plt.colorbar(g, ax=ax)  # TODO: Could use fig now

        # Set the title of the classifiation report
        ax.set_title("Classification Report for {}".format(self.name))

        # Set the tick marks appropriately
        ax.set_xticks(np.arange(len(displayed_scores)) + 0.5)
        ax.set_yticks(np.arange(len(self.classes)) + 0.5)

        ax.set_xticklabels(displayed_scores, rotation=45)
        ax.set_yticklabels(self.classes)

        fig.tight_layout()
        fig.savefig(self.path_to_save + "/ClassificationReport_" + self.name +
                    ".pdf")
        # Return the axes being drawn on
        return ax
예제 #4
0
    def ConfusionMatrixViz(self, percent=True):
        """
        Renders the classification report; must be called after score.
        """
        labels = [0, 1]
        confusion_matrix_ = confusion_matrix_metric(self.y_true,
                                                    self.y_pred,
                                                    labels=labels)
        class_counts_ = dict(zip(*np.unique(self.y_true, return_counts=True)))

        # Make array of only the classes actually being used.
        # Needed because sklearn confusion_matrix only returns counts for
        # selected classes but percent should be calculated on all classes
        selected_class_counts = []
        for c in labels:
            try:
                selected_class_counts.append(class_counts_[c])
            except KeyError:
                selected_class_counts.append(0)
        class_counts_ = np.array(selected_class_counts)

        # Perform display related manipulations on the confusion matrix data
        cm_display = confusion_matrix_

        # Convert confusion matrix to percent of each row, i.e. the
        # predicted as a percent of true in each class.
        if percent is True:
            # Note: div_safe function returns 0 instead of NAN.
            cm_display = div_safe(confusion_matrix_,
                                  class_counts_.reshape(-1, 1))
            cm_display = np.round(cm_display * 100, decimals=0)

        # Y axis should be sorted top to bottom in pcolormesh
        cm_display = cm_display[::-1, ::]

        # Set up the dimensions of the pcolormesh
        n_classes = len(self.classes)
        X, Y = np.arange(n_classes + 1), np.arange(n_classes + 1)

        fig, ax = plt.subplots(ncols=1, nrows=1)
        ax.set_ylim(bottom=0, top=cm_display.shape[0])
        ax.set_xlim(left=0, right=cm_display.shape[1])

        # Fetch the grid labels from the classes in correct order; set ticks.
        xticklabels = self.classes
        yticklabels = self.classes[::-1]
        ticks = np.arange(n_classes) + 0.5

        ax.set(xticks=ticks, yticks=ticks)
        ax.set_xticklabels(xticklabels,
                           rotation="vertical",
                           fontsize=self.fontsize)
        ax.set_yticklabels(yticklabels, fontsize=self.fontsize)

        # Set data labels in the grid enumerating over all x,y class pairs.
        # NOTE: X and Y are one element longer than the confusion matrix, so
        # skip the last element in the enumeration to label grids.
        for x in X[:-1]:
            for y in Y[:-1]:

                # Extract the value and the text label
                value = cm_display[x, y]
                svalue = "{:0.0f}".format(value)
                if percent:
                    svalue += "%"

                # Determine the grid and text colors
                base_color = self.cmap(value / cm_display.max())
                text_color = find_text_color(base_color)

                # Make zero values more subtle
                if cm_display[x, y] == 0:
                    text_color = "0.75"

                # Add the label to the middle of the grid
                cx, cy = x + 0.5, y + 0.5
                ax.text(
                    cy,
                    cx,
                    svalue,
                    va="center",
                    ha="center",
                    color=text_color,
                    fontsize=self.fontsize,
                )

                # Add a dark line on the grid with the diagonal. Note that the
                # tick labels have already been reversed.
                lc = "k" if xticklabels[x] == yticklabels[y] else "w"
                self._edgecolors.append(lc)

        # Draw the heatmap with colors bounded by vmin,vmax
        vmin = 0.00001
        vmax = 99.999 if percent is True else cm_display.max()
        ax.pcolormesh(
            X,
            Y,
            cm_display,
            vmin=vmin,
            vmax=vmax,
            edgecolor=self._edgecolors,
            cmap=self.cmap,
            linewidth="0.01",
        )
        ax.set_title("Confusion Matrix for {}".format(self.name))
        ax.set_ylabel("True Class")
        ax.set_xlabel("Predicted Class")

        # Call tight layout to maximize readability
        fig.tight_layout()
        fig.savefig(self.path_to_save + "/ConfusionMatrix_" + self.name +
                    ".pdf")
        # Return the axes being drawn on
        return ax