Commit 36b268e9 authored by Tiago de Freitas Pereira's avatar Tiago de Freitas Pereira

Merge branch 'fix-histogram' into 'master'

Fix issues with matplotlib.hist.

See merge request !71
parents ec617147 5c03e531
Pipeline #21192 passed with stages
in 12 minutes and 18 seconds
......@@ -239,23 +239,31 @@ def points_curve_option(**kwargs):
def n_bins_option(**kwargs):
'''Get the number of bins in the histograms'''
possible_strings = ['auto', 'fd', 'doane',
'scott', 'rice', 'sturges', 'sqrt']
def custom_n_bins_option(func):
def callback(ctx, param, value):
if value is None:
value = 'auto'
value = ['doane']
else:
tmp = value.split(',')
try:
value = [int(i) if i != 'auto' else i for i in tmp]
value = [int(i) if i not in possible_strings
else i for i in tmp]
except Exception:
raise click.BadParameter('Incorrect number of bins inputs')
ctx.meta['n_bins'] = value
return value
return click.option(
'-b', '--nbins', type=click.STRING, default='auto',
'-b', '--nbins', type=click.STRING, default='doane',
help='The number of bins for the different histograms in the '
' figure, seperated by commas. For example, if three histograms '
'are in the plots, input something like `100,auto,50`',
'figure, seperated by commas. For example, if three histograms '
'are in the plots, input something like `100,doane,50`. All the '
'possible bin options can be found in https://docs.scipy.org/doc/'
'numpy/reference/generated/numpy.histogram.html. Be aware that '
'for some corner cases, the option `auto` and `fd` can lead to '
'MemoryError.',
callback=callback, **kwargs)(func)
return custom_n_bins_option
......
......@@ -13,6 +13,18 @@ from tabulate import tabulate
from .. import (far_threshold, plot, utils, ppndf)
def check_list_value(values, desired_number, name, name2='systems'):
if values is not None and len(values) != desired_number:
if len(values) == 1:
values = values * desired_number
else:
raise click.BadParameter(
'#{} ({}) must be either 1 value or the same as '
'#{} ({} values)'.format(name, values, name2, desired_number))
return values
class MeasureBase(object):
"""Base class for metrics and plots.
This abstract class define the framework to plot or compute metrics from a
......@@ -300,7 +312,6 @@ class PlotBase(MeasureBase):
if not self._titles and self._title is not None:
self._titles = [self._title] * 2
self._x_label = ctx.meta.get('x_label')
self._y_label = ctx.meta.get('y_label')
self._grid_color = 'silver'
......@@ -545,18 +556,16 @@ class Epc(PlotBase):
class Hist(PlotBase):
''' Functional base class for histograms'''
def __init__(self, ctx, scores, evaluation, func_load):
def __init__(self, ctx, scores, evaluation, func_load, nhist_per_system=2):
super(Hist, self).__init__(ctx, scores, evaluation, func_load)
self._nbins = ctx.meta.get('n_bins', [])
self._nbins = ctx.meta.get('n_bins', ['doane'])
self._nhist_per_system = nhist_per_system
self._nbins = check_list_value(
self._nbins, self.n_systems * nhist_per_system, 'n_bins',
'histograms')
self._thres = ctx.meta.get('thres')
if self._thres is not None and len(self._thres) != self.n_systems:
if len(self._thres) == 1:
self._thres = self._thres * self.n_systems
else:
raise click.BadParameter(
'#thresholds must be the same as #systems (%d)'
% self.n_systems
)
self._thres = check_list_value(
self._thres, self.n_systems, 'thresholds')
self._criterion = ctx.meta.get('criterion')
self._no_line = ctx.meta.get('no_line', False)
self._nrows = ctx.meta.get('n_row', 1)
......@@ -607,7 +616,8 @@ class Hist(PlotBase):
def _get_title(self, idx, dev_file, eval_file):
title = self._legends[idx] if self._legends is not None else None
title = title or self._title_base
title = '' if title is not None and not title.replace(' ', '') else title
title = '' if title is not None and not title.replace(
' ', '') else title
return title or ''
def _plot_legends(self):
......@@ -643,7 +653,7 @@ class Hist(PlotBase):
def _density_hist(self, scores, n, **kwargs):
n, bins, patches = mpl.hist(
scores, density=True,
bins='auto' if len(self._nbins) <= n else self._nbins[n],
bins=self._nbins[n],
**kwargs
)
return (n, bins, patches)
......
......@@ -63,7 +63,7 @@ def test_roc():
with runner.isolated_filesystem():
result = runner.invoke(commands.roc, ['-e', '--output',
'test.pdf', '--legends', 'A,B',
'test.pdf', '--legends', 'A,B',
dev1, test1, dev2, test2])
if result.output:
click.echo(result.output)
......@@ -130,20 +130,20 @@ def test_hist():
result = runner.invoke(commands.hist, [dev1])
if result.output:
click.echo(result.output)
assert result.exit_code == 0, (result.exit_code, result.output)
assert result.exit_code == 0, (result.exit_code, result.output, result.exception)
with runner.isolated_filesystem():
result = runner.invoke(commands.hist, ['--criterion',
'min-hter', '--no-line',
'--output', 'HISTO.pdf', '-b',
'30,100', dev1, dev2])
'--output', 'HISTO.pdf', '-b',
'30,100,30,100', dev1, dev2])
if result.output:
click.echo(result.output)
assert result.exit_code == 0, (result.exit_code, result.output)
with runner.isolated_filesystem():
result = runner.invoke(commands.hist, ['-e', '--criterion', 'eer','--output',
'HISTO.pdf', '-b', '30,20',
'HISTO.pdf', '-b', '30,20,30,20',
'-sp', 221, '-lg', 'A,B',
dev1, test1, dev2, test2])
if result.output:
......@@ -159,7 +159,7 @@ def test_evaluate():
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(commands.evaluate, [dev1])
assert result.exit_code == 0, (result.exit_code, result.output)
assert result.exit_code == 0, (result.exit_code, result.output, result.exception)
with runner.isolated_filesystem():
result = runner.invoke(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment