Source code for plots.scores.perfdiag

# -*- coding: utf-8 -*-
"""
Created on 24 April 2024

@author: radanovics

based on code from Ange Haddjeri thesis.
"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cm
from snowtools.plots.abstracts.figures import Mplfigure


def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
    new_cmap = colors.LinearSegmentedColormap.from_list(
        'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
        cmap(np.linspace(minval, maxval, n)))
    return new_cmap


[docs] class PerfDiag(Mplfigure): """ Performance diagram Plot 1-FAR on x-axis, POD on y-axis and iso-lines for frequency bias and CSI Example: .. code-block:: python import matplotlib.pyplot as plt from snowtools.plots.scores.perfdiag import PerfDiag diag = PerfDiag() diag.plot_scores([0.2, 0.5, 0.6, 0.9], [0.1, 0.5, 0.7, 0.4]) diag.add_legend() diag.addlogo() plt.show() diag.close() .. figure:: /images/perfdiag.png :align: center """ default_colors = plt.get_cmap('Paired') #: default colors for the different experiments
[docs] def __init__(self, figsize=(9, 8), csi_cmap='Purples', title="Performance diagram"): """ :param figsize: figure size :param csi_cmap: colormap for CSI shading. Will be truncated :param title: Figure title """ self.fig = plt.figure(figsize=figsize) self.plot = plt.subplot(111) self.xlabel = "Success Ratio (1-FAR)" self.ylabel = "Probability of Detection (POD)" self.ticks = np.arange(0, 1.1, 0.1) self.dpi = 300 self.base_cmap = plt.get_cmap(csi_cmap) self.csi_cmap = truncate_colormap(self.base_cmap, 00.5, 1) self.csi_cmap2 = truncate_colormap(self.base_cmap, 0, 0.5) self.set_title(title, fontsize=14) self.csi_label = "Critical Success Index (CSI)" self.legend_params = dict(loc='upper left', fontsize=12, framealpha=0.9, frameon=True) self.diagram_background() self.add_labels()
@property def xlabel(self): return self._xlabel @xlabel.setter def xlabel(self, value): self._xlabel = value @property def ylabel(self): return self._ylabel @ylabel.setter def ylabel(self, value): self._ylabel = value @property def csi_label(self): return self._csi_label @csi_label.setter def csi_label(self, value): self._csi_label = value
[docs] def diagram_background(self): """ create diagram background with CSI iso-lines and shading and frequency bias iso-lines """ grid_ticks = np.arange(0., 1.01, 0.01) sr_g, pod_g = np.meshgrid(grid_ticks, grid_ticks) bias = np.empty((len(grid_ticks), len(grid_ticks))) bias.fill(np.nan) bias[:, 1:] = pod_g[:, 1:] / sr_g[:, 1:] csi = np.zeros((len(grid_ticks), len(grid_ticks))) # csi.fill(np.nan) csi[1:, 1:] = 1.0 / (1.0 / sr_g[1:, 1:] + 1.0 / pod_g[1:, 1:] - 1.0) csi_contourf = self.plot.contourf(sr_g, pod_g, csi, np.arange(0.1, 1.1, 0.05), extend="max", cmap=self.csi_cmap2) csi_contour = self.plot.contour(sr_g, pod_g, csi, np.arange(0.1, 1.1, 0.05), extend="max", cmap=self.csi_cmap) self.cbar = plt.colorbar(csi_contourf) #, ax=self.plot) self.cbar.set_label(self.csi_label, fontsize=14) b_contour = self.plot.contour(sr_g, pod_g, bias, [0.3, 0.5, 0.8, 1, 1.2, 1.5, 2, 3, 5, 10], colors="k", linestyles="dashed")
[docs] def add_datapoint(self, pod, far, marker='*', color='#6ACC64', label=None, linestyle='None', markersize=10): """ :param pod: Probability of detection :param far: False Alarm Ratio :param marker: matplotlib marker type :param color: matplotlib compatible color specification :param label: experiment label for the legend :param linestyle: matplotlib linestyle :param markersize: marker size :return: """ self.plot.plot(1-far, pod, marker=marker, color=color, label=label, linestyle=linestyle, markersize=markersize)
[docs] def plot_scores(self, pod, far, list_colors=None, list_markers=None, list_labels=None, list_markersizes=None): """ :param pod: list or 1d array of Probability of detection values :param far: list or 1d array of False Alarm Ratio :param list_colors: colors for the different points. List of length 1 (all points will be plotted with the same color) or same length as pod and far. If None, default colours `self.default_colors` will be used. :type list_colors: list :param list_markers: Markers for the different points. List of length 1 (all points will be plotted with the same marker) or same length as pod and far. If None, all points will be plotted with the '*' marker :type list_markers: list :param list_labels: List of the same length as pod and far with labels to put on the legend. If None, the labels will be "experiment 1", "experiment 2 etc. :type list_labels: list :param list_markersizes: Marker sizes for the different points. List of length 1 (all points will be plotted with the same marker size) or same length as pod and far. If None, all points will be plotted with a marker size of 10. :type list_markersizes: list :return: """ try: assert len(pod) == len(far) except AssertionError: raise AssertionError("POD and FAR lists have to be same length") if list_colors is not None: if len(list_colors) == 1: colors = np.repeat(list_colors[0], len(pod)) else: assert len(list_colors) == len(pod) colors = list_colors else: colors = self.default_colors.colors[:len(pod)] if list_markers is not None: if len(list_markers) == 1: markers = np.repeat(list_markers[0], len(pod)) else: assert len(list_markers) == len(pod) markers = list_markers else: markers = np.repeat('*', len(pod)) if list_labels is not None: if len(list_labels) == 1: labels = np.repeat(list_labels[0], len(pod)) else: assert len(list_labels) == len(pod) labels = list_labels else: labels = ['experiment {0}'.format(i) for i in range(1, len(pod)+1)] if list_markersizes is not None: if len(list_markersizes) == 1: markersizes = np.repeat(list_markersizes[0], len(pod)) else: assert len(list_markersizes) == len(pod) markersizes = list_markersizes else: markersizes = np.repeat(10, len(pod)) for ii, (ipod, ifar, icolor, imarker, ilabel, isize) in enumerate(zip(pod, far, colors, markers, labels, markersizes)): self.add_datapoint(ipod, ifar, color=icolor, marker=imarker, label=ilabel, markersize=isize)
[docs] def add_labels(self): """ add axis labels and tick markers. Annotate the background lines with "Frequency Bias" and "CSI score". :return: """ self.plot.set_xlabel(self.xlabel) self.plot.set_ylabel(self.ylabel) self.plot.set_xticks(self.ticks) self.plot.set_yticks(self.ticks) self.plot.text(0.78, 0.37, "Frequency Bias", fontdict=dict(fontsize=14, rotation=23)) self.plot.text(0.5, 0.08, "CSI score", fontdict=dict(fontsize=14, rotation=0), color='#6B51A3')
[docs] def add_legend(self): """ add a legend. """ self.plot.legend(**self.legend_params)
# # import matplotlib.patches as mpatches # # green_patch = mpatches.Patch(color='#6ACC64', label='Safran precipitation') # orange_patch = mpatches.Patch(color='#EE854A', label='Safran HR precipitation') # red_patch = mpatches.Patch(color='#D65F5F', label='Antilope precipitation') # blue_patch = mpatches.Patch(color='#4878D0', label='Arome precipitation') # # csii=Line2D([0], [0], color='mediumpurple', lw=3, label='CSI score') # legend1 = plt.legend(handles=[green_patch, orange_patch, red_patch, blue_patch], loc='lower right') # # from matplotlib.lines import Line2D # # legend_elements = [Line2D([0], [0], marker='*', color='w', # label='Simulation with snow transport\nNeighborhood radius of 0\n(equivalent to pixel-pixel)', # markerfacecolor='dimgray', markersize=10), # Line2D([0], [0], marker='p', color='w', # label='Simulation without snow transport\nNeighborhood radius of 0\n(equivalent to pixel-pixel)', # markerfacecolor='dimgray', markersize=10), # Line2D([0], [0], marker='P', color='w', # label='Simulation with snow transport\nNeighborhood radius of 1', markerfacecolor='dimgray', # markersize=10), # Line2D([0], [0], marker='s', color='w', # label='Simulation without snow transport\nNeighborhood radius of 1', # markerfacecolor='dimgray', markersize=10), # ] # legend2 = plt.legend(handles=legend_elements, loc='upper left') # plt.gca().add_artist(legend1) # plt.gca().add_artist(legend2) # plt.clabel(csi_contour, inline=True, fontsize=9) # plt.clabel(b_contour, fmt="%1.1f", ) # manual=[(0.2, 0.9), (0.4, 0.9), (0.6, 0.9), (0.7, 0.7)]) # plt.savefig("CHAP4/perfdiagram.pdf", bbox_inches="tight") class FuzzyScoreDiagram(Mplfigure): def __init__(self, figsize=(9, 8), cmap='winter', title="Fuzzy Score Diagram"): self.fig = plt.figure(figsize=figsize) self.plot = plt.subplot(111) self.xlabel = "Thresholds" self.ylabel = "Neighborhood size" self.cmap = cmap self.set_title(title, fontsize=14) def draw(self, data, xlabels, ylabels): # (xl, yl) = np.meshgrid(xlabels, ylabels) # print(xl) mesh = self.plot.pcolormesh(data, cmap=self.cmap) #xt = self.plot.get_xticks() #print(xlabels) self.plot.set_xticks(np.arange(0, len(xlabels), 1)) self.plot.set_xticklabels(xlabels) self.plot.set_yticks(np.arange(0.5, len(ylabels), 1)) self.plot.set_yticklabels(ylabels) self.plot.set_xlabel(self.xlabel) self.plot.set_ylabel(self.ylabel) self.cbar = plt.colorbar(mesh)