plot.py 19.8 KB
Newer Older
André Anjos's avatar
André Anjos committed
1 2 3 4
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Mon 23 May 2011 14:36:14 CEST

André Anjos's avatar
André Anjos committed
5

Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
6
def log_values(min_step=-4, counts_per_step=4):
André Anjos's avatar
André Anjos committed
7 8 9 10 11 12 13 14
  """Computes log-scaled values between :math:`10^{M}` and 1

  This function computes log-scaled values between :math:`10^{M}` and 1
  (including), where :math:`M` is the ``min_ste`` argument, which needs to be a
  negative integer.  The integral ``counts_per_step`` value defines how many
  values between two adjacent powers of 10 will be created.  The total number
  of values will be ``-min_step * counts_per_step + 1``.

15

André Anjos's avatar
André Anjos committed
16
  Parameters:
17

André Anjos's avatar
André Anjos committed
18 19 20
    min_step (:py:class:`int`, optional): The power of 10 that will be the
      minimum value.  E.g., the default ``-4`` will result in the first number
      to be :math:`10^{-4}` = ``0.00001`` or ``0.01%``
21

André Anjos's avatar
André Anjos committed
22 23 24 25
    counts_per_step (:py:class:`int`, optional): The number of values that will
      be put between two adjacent powers of 10.  With the default value ``4``
      (and default values of ``min_step``), we will get ``log_list[0] ==
      1e-4``, ``log_list[4] == 1e-3``, ..., ``log_list[16] == 1``.
26 27


André Anjos's avatar
André Anjos committed
28 29 30
  Returns:

    list: A list of logarithmically scaled values between :math:`10^{M}` and 1.
31 32

  """
André Anjos's avatar
André Anjos committed
33

34
  import math
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
35
  return [math.pow(10., i * 1. / counts_per_step) for i in range(min_step * counts_per_step, 0)] + [1.]
36

André Anjos's avatar
André Anjos committed
37 38

def roc(negatives, positives, npoints=100, CAR=False, **kwargs):
39
  """Plots Receiver Operating Characteristic (ROC) curve.
André Anjos's avatar
André Anjos committed
40

41
  This method will call ``matplotlib`` to plot the ROC curve for a system which
André Anjos's avatar
André Anjos committed
42
  contains a particular set of negatives (impostors) and positives (clients)
André Anjos's avatar
André Anjos committed
43 44 45
  scores. We use the standard :py:func:`matplotlib.pyplot.plot` command. All
  parameters passed with exception of the three first parameters of this method
  will be directly passed to the plot command.
André Anjos's avatar
André Anjos committed
46

André Anjos's avatar
André Anjos committed
47 48 49
  The plot will represent the false-alarm on the horizontal axis and the
  false-rejection on the vertical axis.  The values for the axis will be
  computed using :py:func:`bob.measure.roc`.
André Anjos's avatar
André Anjos committed
50

51
  .. note::
André Anjos's avatar
André Anjos committed
52

53 54 55
    This function does not initiate and save the figure instance, it only
    issues the plotting command. You are the responsible for setting up and
    saving the figure as you see fit.
André Anjos's avatar
André Anjos committed
56 57


André Anjos's avatar
André Anjos committed
58
  Parameters:
André Anjos's avatar
André Anjos committed
59

André Anjos's avatar
André Anjos committed
60 61 62
    negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier. See
      (:py:func:`bob.measure.roc`)
André Anjos's avatar
André Anjos committed
63

André Anjos's avatar
André Anjos committed
64 65 66
    positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier. See
      (:py:func:`bob.measure.roc`)
André Anjos's avatar
André Anjos committed
67

André Anjos's avatar
André Anjos committed
68
    npoints (:py:class:`int`, optional): The number of points for the plot. See
André Anjos's avatar
André Anjos committed
69
      (:py:func:`bob.measure.roc`)
André Anjos's avatar
André Anjos committed
70

André Anjos's avatar
André Anjos committed
71 72 73
    CAR (:py:class:`bool`, optional): If set to ``True``, it will plot the CAR
      over FAR in using :py:func:`matplotlib.pyplot.semilogx`, otherwise the
      FAR over FRR linearly using :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
74

André Anjos's avatar
André Anjos committed
75 76
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
77 78 79 80


  Returns:

81 82 83
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
84 85 86

  """

87
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
88
  from . import roc as calc
89
  out = calc(negatives, positives, npoints)
André Anjos's avatar
André Anjos committed
90
  if not CAR:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
91
    return pyplot.plot(100.0 * out[0, :], 100.0 * out[1, :], **kwargs)
André Anjos's avatar
André Anjos committed
92
  else:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
93
    return pyplot.semilogx(100.0 * out[0, :], 100.0 * (1 - out[1, :]), **kwargs)
94

André Anjos's avatar
André Anjos committed
95

Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
96
def roc_for_far(negatives, positives, far_values=log_values(), **kwargs):
André Anjos's avatar
André Anjos committed
97
  """Plots the ROC curve for the given list of False Acceptance Rates (FAR).
98 99 100

  This method will call ``matplotlib`` to plot the ROC curve for a system which
  contains a particular set of negatives (impostors) and positives (clients)
André Anjos's avatar
André Anjos committed
101 102 103
  scores. We use the standard :py:func:`matplotlib.pyplot.semilogx` command.
  All parameters passed with exception of the three first parameters of this
  method will be directly passed to the plot command.
104

André Anjos's avatar
André Anjos committed
105 106 107
  The plot will represent the False Acceptance Rate (FAR) on the horizontal
  axis and the Correct Acceptance Rate (CAR) on the vertical axis.  The values
  for the axis will be computed using :py:func:`bob.measure.roc_for_far`.
108 109 110 111 112 113 114 115

  .. note::

    This function does not initiate and save the figure instance, it only
    issues the plotting command. You are the responsible for setting up and
    saving the figure as you see fit.


André Anjos's avatar
André Anjos committed
116 117 118 119 120
  Parameters:

    negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier. See
      (:py:func:`bob.measure.roc`)
121

André Anjos's avatar
André Anjos committed
122 123 124
    positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier. See
      (:py:func:`bob.measure.roc`)
125

André Anjos's avatar
André Anjos committed
126 127
    far_values (:py:class:`list`, optional): The values for the FAR, where the
      CAR should be plotted; each value should be in range [0,1].
128

André Anjos's avatar
André Anjos committed
129 130
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
131 132 133 134


  Returns:

135 136 137
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.semilogx`.
138 139 140 141 142 143

  """

  from matplotlib import pyplot
  from . import roc_for_far as calc
  out = calc(negatives, positives, far_values)
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
144
  return pyplot.semilogx(100.0 * out[0, :], 100.0 * (1 - out[1, :]), **kwargs)
145 146


André Anjos's avatar
André Anjos committed
147
def precision_recall_curve(negatives, positives, npoints=100, **kwargs):
André Anjos's avatar
André Anjos committed
148
  """Plots a Precision-Recall curve.
André Anjos's avatar
André Anjos committed
149

André Anjos's avatar
André Anjos committed
150 151 152 153 154 155
  This method will call ``matplotlib`` to plot the precision-recall curve for a
  system which contains a particular set of ``negatives`` (impostors) and
  ``positives`` (clients) scores. We use the standard
  :py:func:`matplotlib.pyplot.plot` command. All parameters passed with
  exception of the three first parameters of this method will be directly
  passed to the plot command.
André Anjos's avatar
André Anjos committed
156

157
  .. note::
André Anjos's avatar
André Anjos committed
158

159 160 161
    This function does not initiate and save the figure instance, it only
    issues the plotting command. You are the responsible for setting up and
    saving the figure as you see fit.
André Anjos's avatar
André Anjos committed
162 163


André Anjos's avatar
André Anjos committed
164 165 166 167 168 169 170 171 172
  Parameters:

    negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier. See
      (:py:func:`bob.measure.precision_recall_curve`)

    positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier. See
      (:py:func:`bob.measure.precision_recall_curve`)
André Anjos's avatar
André Anjos committed
173

André Anjos's avatar
André Anjos committed
174
    npoints (:py:class:`int`, optional): The number of points for the plot. See
André Anjos's avatar
André Anjos committed
175
      (:py:func:`bob.measure.precision_recall_curve`)
André Anjos's avatar
André Anjos committed
176

André Anjos's avatar
André Anjos committed
177 178
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
179 180


André Anjos's avatar
André Anjos committed
181 182
  Returns:

183 184 185
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
186

André Anjos's avatar
André Anjos committed
187 188
  """

189
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
190 191
  from . import precision_recall_curve as calc
  out = calc(negatives, positives, npoints)
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
192
  return pyplot.plot(100.0 * out[0, :], 100.0 * out[1, :], **kwargs)
André Anjos's avatar
André Anjos committed
193 194 195


def epc(dev_negatives, dev_positives, test_negatives, test_positives,
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
196
        npoints=100, **kwargs):
André Anjos's avatar
André Anjos committed
197 198 199 200 201 202 203
  """Plots Expected Performance Curve (EPC) as defined in the paper:

  Bengio, S., Keller, M., Mariéthoz, J. (2004). The Expected Performance Curve.
  International Conference on Machine Learning ICML Workshop on ROC Analysis in
  Machine Learning, 136(1), 1963–1966. IDIAP RR. Available:
  http://eprints.pascal-network.org/archive/00000670/

204
  This method will call ``matplotlib`` to plot the EPC curve for a system which
André Anjos's avatar
André Anjos committed
205 206
  contains a particular set of negatives (impostors) and positives (clients)
  for both the development and test sets. We use the standard
André Anjos's avatar
André Anjos committed
207 208 209
  :py:func:`matplotlib.pyplot.plot` command. All parameters passed with
  exception of the five first parameters of this method will be directly passed
  to the plot command.
André Anjos's avatar
André Anjos committed
210

André Anjos's avatar
André Anjos committed
211 212
  The plot will represent the minimum HTER on the vertical axis and the cost on
  the horizontal axis.
André Anjos's avatar
André Anjos committed
213

214
  .. note::
André Anjos's avatar
André Anjos committed
215

216 217 218
    This function does not initiate and save the figure instance, it only
    issues the plotting commands. You are the responsible for setting up and
    saving the figure as you see fit.
André Anjos's avatar
André Anjos committed
219 220


André Anjos's avatar
André Anjos committed
221 222 223 224 225 226 227 228 229
  Parameters:

    dev_negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier, from the
      development set. See (:py:func:`bob.measure.epc`)

    dev_positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier, from the
      development set. See (:py:func:`bob.measure.epc`)
André Anjos's avatar
André Anjos committed
230

André Anjos's avatar
André Anjos committed
231 232 233
    test_negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier, from the test
      set. See (:py:func:`bob.measure.epc`)
André Anjos's avatar
André Anjos committed
234

André Anjos's avatar
André Anjos committed
235 236 237
    test_positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier, from the test set.
      See (:py:func:`bob.measure.epc`)
André Anjos's avatar
André Anjos committed
238

André Anjos's avatar
André Anjos committed
239
    npoints (:py:class:`int`, optional): The number of points for the plot. See
André Anjos's avatar
André Anjos committed
240 241
      (:py:func:`bob.measure.epc`)

André Anjos's avatar
André Anjos committed
242 243
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
244 245 246 247


  Returns:

248 249 250
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
251 252 253

  """

254
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
255 256
  from . import epc as calc

André Anjos's avatar
André Anjos committed
257
  out = calc(dev_negatives, dev_positives, test_negatives, test_positives,
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
258 259
             npoints)
  return pyplot.plot(out[0, :], 100.0 * out[1, :], **kwargs)
260

André Anjos's avatar
André Anjos committed
261 262 263 264 265 266 267 268 269

def det(negatives, positives, npoints=100, axisfontsize='x-small', **kwargs):
  """Plots Detection Error Trade-off (DET) curve as defined in the paper:

  Martin, A., Doddington, G., Kamm, T., Ordowski, M., & Przybocki, M. (1997).
  The DET curve in assessment of detection task performance. Fifth European
  Conference on Speech Communication and Technology (pp. 1895-1898). Available:
  http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.4489&rep=rep1&type=pdf

André Anjos's avatar
André Anjos committed
270 271 272 273 274
  This method will call ``matplotlib`` to plot the DET curve(s) for a system
  which contains a particular set of negatives (impostors) and positives
  (clients) scores. We use the standard :py:func:`matplotlib.pyplot.plot`
  command. All parameters passed with exception of the three first parameters
  of this method will be directly passed to the plot command.
André Anjos's avatar
André Anjos committed
275

276 277
  The plot will represent the false-alarm on the horizontal axis and the
  false-rejection on the vertical axis.
André Anjos's avatar
André Anjos committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291

  This method is strongly inspired by the NIST implementation for Matlab,
  called DETware, version 2.1 and available for download at the NIST website:

  http://www.itl.nist.gov/iad/mig/tools/

  .. note::

    This function does not initiate and save the figure instance, it only
    issues the plotting commands. You are the responsible for setting up and
    saving the figure as you see fit.

  .. note::

292
    If you wish to reset axis zooming, you must use the Gaussian scale rather
André Anjos's avatar
André Anjos committed
293
    than the visual marks showed at the plot, which are just there for
André Anjos's avatar
André Anjos committed
294 295 296
    displaying purposes. The real axis scale is based on
    :py:func:`bob.measure.ppndf`.  For example, if you wish to set the x and y
    axis to display data between 1% and 40% here is the recipe:
André Anjos's avatar
André Anjos committed
297 298 299

    .. code-block:: python

André Anjos's avatar
André Anjos committed
300
      import bob.measure
301
      from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
302
      bob.measure.plot.det(...) #call this as many times as you need
André Anjos's avatar
André Anjos committed
303
      #AFTER you plot the DET curve, just set the axis in this way:
304
      pyplot.axis([bob.measure.ppndf(k/100.0) for k in (1, 40, 1, 40)])
André Anjos's avatar
André Anjos committed
305 306

    We provide a convenient way for you to do the above in this module. So,
André Anjos's avatar
André Anjos committed
307 308
    optionally, you may use the :py:func:`bob.measure.plot.det_axis` method
    like this:
André Anjos's avatar
André Anjos committed
309 310 311

    .. code-block:: python

André Anjos's avatar
André Anjos committed
312 313
      import bob.measure
      bob.measure.plot.det(...)
André Anjos's avatar
André Anjos committed
314
      # please note we convert percentage values in det_axis()
André Anjos's avatar
André Anjos committed
315
      bob.measure.plot.det_axis([1, 40, 1, 40])
André Anjos's avatar
André Anjos committed
316 317


André Anjos's avatar
André Anjos committed
318 319 320 321 322
  Parameters:

    negatives (array): 1D float array that contains the scores of the
      "negative" (noise, non-class) samples of your classifier. See
      (:py:func:`bob.measure.det`)
323

André Anjos's avatar
André Anjos committed
324 325 326
    positives (array): 1D float array that contains the scores of the
      "positive" (signal, class) samples of your classifier. See
      (:py:func:`bob.measure.det`)
327

André Anjos's avatar
André Anjos committed
328
    npoints (:py:class:`int`, optional): The number of points for the plot. See
André Anjos's avatar
André Anjos committed
329
      (:py:func:`bob.measure.det`)
330

André Anjos's avatar
André Anjos committed
331 332
    axisfontsize (:py:class:`str`, optional): The size to be used by
      x/y-tick-labels to set the font size on the axis
333

André Anjos's avatar
André Anjos committed
334 335
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
336 337 338 339


  Returns:

340 341 342
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.plot`.
343 344

  """
André Anjos's avatar
André Anjos committed
345 346 347 348 349 350 351 352 353 354 355 356

  # these are some constants required in this method
  desiredTicks = [
      "0.00001", "0.00002", "0.00005",
      "0.0001", "0.0002", "0.0005",
      "0.001", "0.002", "0.005",
      "0.01", "0.02", "0.05",
      "0.1", "0.2", "0.4", "0.6", "0.8", "0.9",
      "0.95", "0.98", "0.99",
      "0.995", "0.998", "0.999",
      "0.9995", "0.9998", "0.9999",
      "0.99995", "0.99998", "0.99999"
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
357
  ]
André Anjos's avatar
André Anjos committed
358 359 360 361 362 363 364 365 366 367 368

  desiredLabels = [
      "0.001", "0.002", "0.005",
      "0.01", "0.02", "0.05",
      "0.1", "0.2", "0.5",
      "1", "2", "5",
      "10", "20", "40", "60", "80", "90",
      "95", "98", "99",
      "99.5", "99.8", "99.9",
      "99.95", "99.98", "99.99",
      "99.995", "99.998", "99.999"
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
369
  ]
André Anjos's avatar
André Anjos committed
370 371

  # this will actually do the plotting
372
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
373 374 375
  from . import det as calc
  from . import ppndf

376
  out = calc(negatives, positives, npoints)
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
377
  retval = pyplot.plot(out[0, :], out[1, :], **kwargs)
André Anjos's avatar
André Anjos committed
378 379 380

  # now the trick: we must plot the tick marks by hand using the PPNDF method
  pticks = [ppndf(float(v)) for v in desiredTicks]
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
381
  ax = pyplot.gca()  # and finally we set our own tick marks
André Anjos's avatar
André Anjos committed
382 383 384 385 386 387 388
  ax.set_xticks(pticks)
  ax.set_xticklabels(desiredLabels, size=axisfontsize)
  ax.set_yticks(pticks)
  ax.set_yticklabels(desiredLabels, size=axisfontsize)

  return retval

389

André Anjos's avatar
André Anjos committed
390 391 392
def det_axis(v, **kwargs):
  """Sets the axis in a DET plot.

393
  This method wraps the :py:func:`matplotlib.pyplot.axis` by calling
André Anjos's avatar
André Anjos committed
394 395 396 397 398
  :py:func:`bob.measure.ppndf` on the values passed by the user so they are
  meaningful in a DET plot as performed by :py:func:`bob.measure.plot.det`.


  Parameters:
399

André Anjos's avatar
André Anjos committed
400 401 402 403 404
    v (``sequence``): A sequence (list, tuple, array or the like) containing
      the X and Y limits in the order ``(xmin, xmax, ymin, ymax)``. Expected
      values should be in percentage (between 0 and 100%).  If ``v`` is not a
      list or tuple that contains 4 numbers it is passed without further
      inspection to :py:func:`matplotlib.pyplot.axis`.
André Anjos's avatar
André Anjos committed
405

André Anjos's avatar
André Anjos committed
406 407
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.axis`.
André Anjos's avatar
André Anjos committed
408 409


André Anjos's avatar
André Anjos committed
410 411 412
  Returns:

    object: Whatever is returned by :py:func:`matplotlib.pyplot.axis`.
André Anjos's avatar
André Anjos committed
413 414 415 416

  """

  import logging
417
  logger = logging.getLogger("bob.measure")
André Anjos's avatar
André Anjos committed
418

419
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
420 421 422 423
  from . import ppndf

  # treat input
  try:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
424 425 426 427
    tv = list(v)  # normal input
    if len(tv) != 4:
      raise IndexError
    tv = [ppndf(float(k) / 100) for k in tv]
428
    cur = pyplot.axis()
André Anjos's avatar
André Anjos committed
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

    # limits must be within bounds
    if tv[0] < cur[0]:
      logger.warn("Readjusting xmin: the provided value is out of bounds")
      tv[0] = cur[0]
    if tv[1] > cur[1]:
      logger.warn("Readjusting xmax: the provided value is out of bounds")
      tv[1] = cur[1]
    if tv[2] < cur[2]:
      logger.warn("Readjusting ymin: the provided value is out of bounds")
      tv[2] = cur[2]
    if tv[3] > cur[3]:
      logger.warn("Readjusting ymax: the provided value is out of bounds")
      tv[3] = cur[3]

  except:
    tv = v

447 448
  return pyplot.axis(tv, **kwargs)

André Anjos's avatar
André Anjos committed
449

Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
450
def cmc(cmc_scores, logx=True, **kwargs):
André Anjos's avatar
André Anjos committed
451 452 453 454 455 456 457 458 459 460
  """Plots the (cumulative) match characteristics and returns the maximum rank.

  This function plots a CMC curve using the given CMC scores, which can be read
  from the our score files using the
  :py:func:`bob.measure.load.cmc_four_column` or
  :py:func:`bob.measure.load.cmc_five_column` methods.  The structure of the
  ``cmc_scores`` parameter is relatively complex.  It contains a list of pairs
  of lists.  For each probe object, a pair of list negative and positive scores
  is required.

461

André Anjos's avatar
André Anjos committed
462
  Parameters:
463

André Anjos's avatar
André Anjos committed
464 465
    cmc_scores (array): 1D float array containing the CMC values (See
      :py:func:`bob.measure.cmc`)
466

André Anjos's avatar
André Anjos committed
467 468 469
    logx (:py:class:`bool`, optional): If set (the default), plots the rank
      axis in logarithmic scale using :py:func:`matplotlib.pyplot.semilogx` or
      in linear scale using :py:func:`matplotlib.pyplot.plot`
470

André Anjos's avatar
André Anjos committed
471 472
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
473

474

André Anjos's avatar
André Anjos committed
475 476 477
  Returns:

    int: The number of classes (clients) in the given scores.
André Anjos's avatar
André Anjos committed
478

479
  """
André Anjos's avatar
André Anjos committed
480

481
  from matplotlib import pyplot
André Anjos's avatar
André Anjos committed
482
  from . import cmc as calc
483

André Anjos's avatar
André Anjos committed
484 485 486
  out = calc(cmc_scores)

  if logx:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
487
    pyplot.semilogx(range(1, len(out) + 1), out * 100, **kwargs)
André Anjos's avatar
André Anjos committed
488
  else:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
489
    pyplot.plot(range(1, len(out) + 1), out * 100, **kwargs)
André Anjos's avatar
André Anjos committed
490 491

  return len(out)
492 493


Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
494
def detection_identification_curve(cmc_scores, far_values=log_values(), rank=1, logx=True, **kwargs):
André Anjos's avatar
André Anjos committed
495
  """Plots the Detection & Identification curve over the FAR
496

André Anjos's avatar
André Anjos committed
497 498 499 500
  This curve is designed to be used in an open set identification protocol, and
  defined in Chapter 14.1 of [LiJain2005]_.  It requires to have at least one
  open set probe item, i.e., with no corresponding gallery, such that the
  positives for that pair are ``None``.
501

André Anjos's avatar
André Anjos committed
502 503 504 505 506
  The detection and identification curve first computes FAR thresholds based on
  the out-of-set probe scores (negative scores).  For each probe item, the
  **maximum** negative score is used.  Then, it plots the detection and
  identification rates for those thresholds, which are based on the in-set
  probe scores only. See [LiJain2005]_ for more details.
507

André Anjos's avatar
André Anjos committed
508
  .. [LiJain2005] **Stan Li and Anil K. Jain**, *Handbook of Face Recognition*, Springer, 2005
509 510


André Anjos's avatar
André Anjos committed
511
  Parameters:
512

André Anjos's avatar
André Anjos committed
513 514
    cmc_scores (array): 1D float array containing the CMC values (See
      :py:func:`bob.measure.cmc`)
515

André Anjos's avatar
André Anjos committed
516 517
    rank (:py:class:`int`, optional): The rank for which the curve should be
      plotted
518

André Anjos's avatar
André Anjos committed
519 520
    far_values (:py:class:`list`, optional): The values for the FAR, where the
      CAR should be plotted; each value should be in range [0,1].
521

André Anjos's avatar
André Anjos committed
522 523 524
    logx (:py:class:`bool`, optional): If set (the default), plots the rank
      axis in logarithmic scale using :py:func:`matplotlib.pyplot.semilogx` or
      in linear scale using :py:func:`matplotlib.pyplot.plot`
André Anjos's avatar
André Anjos committed
525

André Anjos's avatar
André Anjos committed
526 527
    kwargs (:py:class:`dict`, optional): Extra plotting parameters, which are
      passed directly to :py:func:`matplotlib.pyplot.plot`.
André Anjos's avatar
André Anjos committed
528 529 530 531


  Returns:

532 533 534
    :py:class:`list` of :py:class:`matplotlib.lines.Line2D`: The lines that
    were added as defined by the return value of
    :py:func:`matplotlib.pyplot.plot`.
535 536 537

  """

538
  import numpy
539
  from matplotlib import pyplot
540
  from . import far_threshold, detection_identification_rate
541

Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
542 543 544 545
  # for each probe, for which no positives exists, get the highest negative
  # score; and sort them to compute the FAR thresholds
  negatives = sorted(max(neg) for neg, pos in cmc_scores if (
      pos is None or not numpy.array(pos).size) and neg is not None)
546
  if not negatives:
Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
547 548
    raise ValueError(
        "There need to be at least one pair with only negative scores")
549 550 551 552

  # compute thresholds based on FAR values
  thresholds = [far_threshold(negatives, [], v, True) for v in far_values]

Amir Mohammadi's avatar
lint  
Amir Mohammadi committed
553 554 555 556
  # compute detection and identification rate based on the thresholds for
  # the given rank
  rates = [
      100. * detection_identification_rate(cmc_scores, t, rank) for t in thresholds]
557 558 559 560 561 562

  # plot curve
  if logx:
    return pyplot.semilogx(far_values, rates, **kwargs)
  else:
    return pyplot.plot(far_values, rates, **kwargs)