ROC/DET plotter following the ISO/IEC 19795-1:2006(E) standard
Parameters allow users to change the configuration of an plotter when scheduling an experiment
Name | Description | Type | Default |
---|---|---|---|
xlabel | The label of the X-axis (horizontal) | string | False Positives (False Match Rate), in % |
ylabel | The label of the Y-axis (vertical) | string | True Positives (1 - False Non-Match Rate), in % |
xlim-left | float64 | 0.0 | |
xlim-right | float64 | 100.0 | |
ylim-bottom | float64 | 0.0 | |
ylim-top | float64 | 100.0 | |
title | The title for this plot | string | ISO/IEC 19795-1:2006(E) ROC |
title-fontsize | Controls the title font size | uint16 | 10 |
xaxis_multiplier | The multiplication factor for the X-axis (horizontal) | float64 | 100.0 |
yaxis_multiplier | The multiplication factor for the Y-axis (vertical) | float64 | 100.0 |
axis-fontsize | Controls the axis font size (labels and values) | uint16 | 10 |
legend | Short description of the data, to be added to the plot | string | |
legend-fontsize | Controls the font size of the legend | uint16 | 12 |
legend-loc | The location of the legend | string | best |
grid | If we should draw grid lines or not for the plot | bool | True |
xaxis_log | If X-axis (horizontal) should be in log-scale | bool | False |
yaxis_log | If Y-axis (vertical) should be in log-scale | bool | False |
dpi | Dots-per-inch in raster image formats | uint16 | 60 |
width | Width of the resulting image in pixels | uint16 | 400 |
height | Height of the resulting image in pixels | uint16 | 300 |
content_type | The type of image returned | string | image/png |
line_attributes | Scatter/Line attributes passed directly to Matplotlib | string | |
det | If set, plot a DET curve instead of a ROC | bool | False |
xxxxxxxxxx
###############################################################################
# #
# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# This file is part of the beat.examples module of the BEAT platform. #
# #
# Commercial License Usage #
# Licensees holding valid commercial BEAT licenses may use this file in #
# accordance with the terms contained in a written agreement between you #
# and Idiap. For further information contact tto@idiap.ch #
# #
# Alternatively, this file may be used under the terms of the GNU Affero #
# Public License version 3 as published by the Free Software and appearing #
# in the file LICENSE.AGPL included in the packaging of this file. #
# The BEAT platform is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
# or FITNESS FOR A PARTICULAR PURPOSE. #
# #
# You should have received a copy of the GNU Affero Public License along #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/. #
# #
###############################################################################
# Makes sure we won't require an X11 connection
import matplotlib
matplotlib.use('Agg')
import numpy
import itertools
class Plotter(baselib.Plotter):
def setup(self, parameters):
super(Plotter, self).setup(parameters)
self.line_attributes = parameters.get('line_attributes', '').split('&')
self.loc = parameters.get('legend-loc', '')
self.title_fontsize = parameters.get('title-fontsize', '')
self.xlim_left = parameters.get('xlim-left', '')
self.xlim_right = parameters.get('xlim-right', '')
self.ylim_bottom = parameters.get('ylim-bottom', '')
self.ylim_top = parameters.get('ylim-top', '')
self.axis_fontsize = parameters.get('axis-fontsize', '')
self.legend_fontsize = parameters.get('legend-fontsize', '')
self.line_attributes = [k for k in self.line_attributes if k]
self.det = parameters.get('det', False)
if self.det:
self.ylabel = "False Negatives (False Non-Match Rate), in %"
# These are the "Tableau 20" colors as RGB.
self.tableau20 = [(31,119,180), (152,223,138), (140,86,75), (199,199,199),
(174,199,232), (214,39,40), (196,156,148), (188,189,34),
(255,127,14), (255,152,150), (227,119,194), (219,219,141),
(255,187,120), (148,103,189), (247,182,210), (23,190,207),
(44,160,44), (197,176,213), (127,127,127), (158,218,229)]
# Scale the RGB values to the [0, 1] range, which is the format matplotlib accepts.
for i in range(len(self.tableau20)):
r, g, b = self.tableau20[i]
self.tableau20[i] = (r / 255., g / 255., b / 255.)
return True
def process(self, inputs):
fig, ax = super(Plotter, self).prepare_canvas()
super(Plotter, self).apply_parameters(ax)
ax.set_title(self.title, fontdict={'fontsize':self.title_fontsize})
ax.set_xlabel(self.xlabel, fontdict={'fontsize':self.axis_fontsize})
ax.set_ylabel(self.ylabel, fontdict={'fontsize':self.axis_fontsize})
ax.set_xlim((self.xlim_left,self.xlim_right))
ax.set_ylim((self.ylim_bottom,self.ylim_top))
label = ""
kwargs = {}
single_experiment = len(inputs) == 1
number_of_positives = []
number_of_negatives = []
n_lines = 0
for xp_label, xp_data in inputs:
for scatter in xp_data.data:
args = []
# the data
x = scatter.false_positives
if self.det:
y = scatter.false_negatives
else:
y = 1.0 - scatter.false_negatives
# accumulate counts (useful in log plots)
number_of_positives.append(scatter.number_of_positives)
number_of_negatives.append(scatter.number_of_negatives)
args.append(x * self.xaxis_multiplier)
args.append(y * self.yaxis_multiplier)
# the label
if self.label: #gets the label from user overwritten input
label = self.label[n_lines%len(self.label)]
else: #make-up label
if single_experiment:
label = scatter.label
else:
text = [k for k in (xp_label, scatter.label) if k]
label = '-'.join(text)
# the line attributes
if self.line_attributes:
args.append(self.line_attributes[n_lines%len(self.line_attributes)])
else:
kwargs['color'] = self.tableau20[n_lines % len(self.tableau20)]
kwargs['label'] = label
n_lines += 1
ax.plot(*args, **kwargs)
if n_lines > 1:
ax.legend(loc=self.loc, fontsize=self.legend_fontsize, fancybox=True, framealpha=0.5)
# reset log axis
if self.xaxis_log:
N = min([k for k in number_of_positives if k > 0]) or 100
ax.set_xscale('symlog', linthreshx=1.0/N)
if self.yaxis_log:
N = min([k for k in number_of_negatives if k > 0]) or 100
ax.set_yscale('symlog', linthreshy=1.0/N)
return super(Plotter, self).encode_figure(fig)
This is a plotter for ISO/IEC 19795-1:2006(E) standard ROC and DET curves. It is based on the description on the standard, section 10.6 "Graphical presentation of results", pages 35 and 36.