IRootLab
An Open-Source MATLAB toolbox for vibrational biospectroscopy
estlog.m
Go to the documentation of this file.
1 %> @brief Estimation logs base class.
2 %>
3 %> Records hits 3D matrix (:)(:)x(time)
4 classdef estlog < ttlog
5  properties(SetAccess=private)
6  collabels;
7  rowlabels;
8  end;
9 
10  properties
11  %> =0. Whether or not to record the "supports" (confidence levels issued by classifiers).
12  %> The default behaviour is to only count the hits for each "time" instant. Recording all the individual
13  %> supports can generate a lot of data, but will allow one to plot the distribution of supports later.
14  flag_support = 0;
15  end;
16 
17  properties
18  %> Whether there is a "refuse-to-decide" potential situation (usually the @ref decider of the object that
19  %> generated the @ref estlog has a threshold > 0). It is automatically determined by verifying whether there is a non-zero element in the first column
20  flag_rejected;
21  end;
22 
23  properties(SetAccess=protected)
24  %> Same size as hits, but it is a cell of vectors.
25  supports;
26  %> Whether the class is able to produce the sensitivity and specificity figures
27  flag_sensspec = 0;
28  end;
29 
30  properties % (SetAccess=protected)
31  %> 3D matrix: (number of groups)x(number of classes in @c labels_cols + 1)x(time)
32  hits = [];
33  end;
34 
35  methods
36  function flag = get.flag_rejected(o)
37  flag = ~isempty(o.hits) && any(o.hits(:, 1, :) > 0);
38  end;
39  end;
40 
41  methods(Access=protected) %, Abstract)
42  %> Abstract.
43  function o = do_record(o, pars) %#ok<*INUSD>
44  end;
45  %> Abstract.
46  function z = get_collabels(o) %#ok<*STOUT>
47  end;
48  %> Abstract.
49  function z = get_rowlabels(o)
50  end;
51  end;
52 
53  methods(Access=protected)
54  %>
55  function o = do_allocate(o, tt)
56  if ~o.flag_inc_t
57 % if nargin > 1 && tt > 1
58 % irverbose('Warning: do_allocate() called with tt > 1 but flag_inc_t is false, will be ignored', 1);
59 % end;
60  tt = 1;
61  end;
62  o.hits = zeros(numel(o.rowlabels), numel(o.collabels)+1, tt);
63  if o.flag_support
64 % o.rpropv(tt).sup = [];
65  o.supports = cell(numel(o.rowlabels), numel(o.collabels)+1, tt);
66  end;
67  end;
68  end;
69 
70 
71  methods
72  function o = estlog()
73  o.classtitle = 'Estimation';
74  o.flag_params = 0;
75  o.moreactions = [o.moreactions, {'extract_confusion'}];
76  end;
77 
78  %> Getter for @c collabels property, calls @c get_collabels() .
79  function z = get.collabels(o)
80  z = o.get_collabels();
81  end;
82 
83  %> Getter for @c rowlabels property, calls @c get_rowlabels() .
84  function z = get.rowlabels(o)
85  z = o.get_rowlabels();
86  end;
87 
88  %>
89  function o = record(o, pars)
90 % if isempty(o.rowlabels)
91 % irerror('Empty row labels!');
92 % end;
93 % if isempty(o.rowlabels)
94 % irerror('Empty row labels!');
95 % end;
96 
97 
98  if o.flag_inc_t || o.t == 0
99  o.t = o.t+1;
100  end;
101 
102  if size(o.hits, 3) < o.t
103  o.hits(numel(o.rowlabels), numel(o.collabels)+1, o.t) = 0; % Ellongates hits
104  end;
105 
106  o = o.do_record(pars);
107  end;
108 
109  %> @brief Calculates 0/w weights for (row)x(time)
110  %>
111  %> 0 < w < 1
112  %>
113  %> @param t =(1:o.t) Time vector to select only a few time instants
114  %> @param normtype = 0
115  %> @arg @c 0 Non-zero weights are all ones
116  %> @arg @c 1 Weights are normalized so the rows add to 1
117  %> @arg @c 2 Weights are normalized so that the columns add to 1
118  %>
119  %> @return A (no_rows)x(o.t) matrix of 0/w to be used as weights.
120  function W = get_weights(o, t, normtype)
121  % Pre-selection
122  if ~exist('t', 'var') || isempty(t) || any(t < 1)
123  C = o.hits(:, :, 1:o.t);
124  else
125  C = o.hits(:, :, t);
126  end;
127  if nargin < 3 || isempty(normtype)
128  normtype = 0;
129  end;
130 
131  S = permute(sum(C, 2), [1, 3, 2]);
132  B = S > 0;
133  if normtype == 0
134  W = B;
135  elseif normtype == 2
136  CB = sum(B, 2)+realmin; % How many time instants at any row actually have something in that row
137  W = B./repmat(CB, 1, size(B, 2));
138  elseif normtype == 1
139  CB = sum(B, 1)+realmin;
140  W = B./repmat(CB, size(B, 1), 1);
141  end;
142  end;
143 
144 
145  %> @brief Flexible function to return a calculation over the "hits" matrix (or parts thereof)
146  %>
147  %> Example: <code>get_C([], 1, 2, 1)</code> Gets average percentage with discounted rejected items
148  %>
149  %> @param t =(all). Pre-selection of specific times
150  %> @param flag_perc1 =0. Whether to calculate percentages already at each time instant
151  %> @param aggr Time-wise Aggregation:
152  %> @arg @c 0 - none</li>
153  %> @arg @c 1 - Sum</li>
154  %> @arg @c 2 - time-wise Sum -> row-wise normalization (rows sum to 1, except if total sum is zero)</li>
155  %> @arg @c 3 - Mean</li>
156  %> @arg @c 4 - Standard Deviation</li></ul></li>
157  %> @param flag_discount_rejected =1. Whether to discount the rejected items from percentages. Only applicable if @c flag_perc is true
158  %> @return C
159  %>
160  %> @note Percentual outputs range from 0 to 100, not from 0 to 1
161  function C = get_C(o, t, flag_perc, aggr, flag_discount_rejected)
162  % Pre-selection
163  if nargin < 2 || isempty(t) || any(t < 1)
164  C = o.hits(:, :, 1:o.t);
165  else
166  C = o.hits(:, :, t);
167  end;
168 
169  if nargin < 3 || isempty(flag_perc)
170  flag_perc = 0;
171  end;
172 
173  if nargin < 4 || isempty(aggr)
174  aggr = 0;
175  end;
176 
177  if nargin < 5 || isempty(flag_discount_rejected)
178  flag_discount_rejected = 1;
179  end;
180 
181  [nrow, ncol, nt] = size(C);
182 
183  if ~flag_perc && flag_discount_rejected
184  irverbose('INFO: estlog::get_C(): flag_discount_rejected ignored!');
185  end;
186 
187 
188  if flag_perc
189  for i = 1:nt
190  S = sum(C(:, :, i), 2);
191  C(:, :, i) = 100*C(:, :, i)./(repmat(S, 1, ncol)+realmin);
192  end;
193 
194  if flag_discount_rejected
195  for i = 1:nrow
196  for j = 1:nt
197  C(i, 2:end, j) = 100*C(i, 2:end, j)/(100-C(i, 1, j)+realmin);
198  end;
199  end;
200  end;
201  end;
202 
203  switch (aggr)
204  case 0
205  % Does nothing
206  case 1
207  if flag_perc
208  irerror('EstLog: Invalid aggregation: Sum of percentages does not make sense!');
209  end;
210 
211  C = sum(C, 3);
212  case 2 % time-wise Sum -> row-wise normalization (rows sum to 1, except if total sum is zero)
213  if flag_perc
214  irerror('EstLog: Invalid aggregation: Per-row normalization of percentages does not make sense!');
215  end;
216 
217  C = sum(C, 3);
218  S = sum(C, 2);
219  S(S == 0) = 1; % makes 0/0 divisions into 0/1 ones
220  C = C./repmat(S, 1, ncol);
221  case 3 % Mean
222  if ~flag_perc
223  irerror('EstLog: Invalid aggregation: Mean of hits does not make sense!');
224  end;
225 
226  S = sum(sum(C, 2) ~= 0, 3); % counts non-zero t-wise rows for each row
227  C = sum(C, 3)./(repmat(S, 1, ncol)+realmin);
228  case 4 % Standard deviation
229  if ~flag_perc
230  irerror('EstLog: Invalid aggregation: Standard deviation of hits does not make sense!');
231  end;
232 
233  T = zeros(nrow, ncol);
234  for i = 1:nrow
235  temp = C(i, :, :);
236  sel = sum(temp, 2) ~= 0;
237  T(i, :) = std(temp(1, :, sel), [], 3);
238  end;
239  C = T;
240  otherwise
241  irerror(sprintf('Invalid option: %d', aggr));
242  end;
243 
244  end;
245 
246 
247  %> <ul>
248  %> <li>Aggregation:<ul>
249  %> <li>@c 0 - INVALID</li>
250  %> <li>@c 1 - INVALID</li>
251  %> <li>@c 2 - INVALID</li>
252  %> <li>@c 3 - Mean</li>
253  %> <li>@c 4 - Standard Deviation</li></ul></li>
254  %> <li>@c 5 - Minimum</li></ul></li>
255  %> <li>@c 6 - Maximum</li></ul></li>
256  %> </ul>
257  %>
258  %> @brief Gets the recorded supports
259  function C = get_C_supp(o, t, aggr)
260  if ~o.flag_support
261  irerror('Not recording supports!');
262  end;
263 
264  % Pre-selection
265  if ~exist('t', 'var') || isempty(t) || sum(t < 1)
266  S = o.supports;
267  else
268  S = o.supports(:, :, t);
269  end;
270 
271 
272  [ni, nj, nk] = size(o.supports); %#ok<NASGU>
273  C = zeros(ni, nj);
274 
275  for i = 1:ni
276  for j = 1:nj
277  v = [S{i, j, :}];
278  if isempty(v)
279  v = 0;
280  end;
281 
282  switch (aggr)
283  case 3
284  C(i, j) = mean(v);
285  case 4
286  C(i, j) = std(v);
287  case 5
288  C(i, j) = min(v);
289  case 6
290  C(i, j) = max(v);
291  otherwise
292  irerror(sprintf('Invalid option: %d', aggr));
293  end;
294  end;
295  end;
296  end;
297 
298  function oc = get_confusion_from_C(o, C, flag_perc)
299  oc = irconfusion();
300  oc.collabels = o.get_collabels();
301  oc.rowlabels = o.get_rowlabels();
302  oc.flag_perc = flag_perc;
303  oc.C = C;
304  end;
305 
306  function oc = get_confusion_sup(o, t, aggr)
307  if ~exist('t', 'var')
308  t = [];
309  end;
310  C = o.get_C_supp(t, aggr);
311  oc = o.get_confusion_from_C(C);
312  end;
313 
314  %> @sa estlog::get_C()
315  function oc = get_confusion(o, t, flag_perc, aggr, flag_discount_rejected)
316  if ~exist('t', 'var')
317  t = [];
318  end;
319  if ~exist('flag_perc', 'var')
320  flag_perc = [];
321  end;
322  if ~exist('aggr', 'var')
323  aggr = [];
324  end;
325 
326  if ~exist('flag_discount_rejected', 'var')
327  flag_discount_rejected = 0;
328  end;
329 
330  C = o.get_C(t, flag_perc, aggr, flag_discount_rejected);
331  oc = o.get_confusion_from_C(C, flag_perc);
332  end;
333 
334  %> @param flag_individual Whether to print time snapshots as well.
335  function s = get_insane_html(o, pars)
336  if ~exist('pars', 'var'); pars = struct(); end;
337  if ~isfield(pars, 'flag_individual')
338  flag_individual = 0;
339  else
340  flag_individual = pars.flag_individual;
341  end;
342  if ~isfield(pars, 'flag_discount_rejected')
343  flag_discount_rejected = 0;
344  else
345  flag_discount_rejected = pars.flag_discount_rejected;
346  end;
347  if ~isfield(pars, 'flag_balls')
348  flag_balls = 0;
349  else
350  flag_balls = pars.flag_balls;
351  end;
352 
353 
354  s = ['<h1>', o.get_description(), '</h1>', 10];
355 
356  if flag_balls
357  % Confusion balls
358  oc = o.get_confusion([], 1, 3, flag_discount_rejected);
359 
360  vb = vis_balls();
361  figure;
362  vb.use(oc);
363  s = cat(2, s, irreport.save_n_close([], 0));
364  end;
365 
366 
367  % Confusion matrices
368  s = cat(2, s, '<h2>Confusion Matrices</h2>', 10);
369  s = cat(2, s, '<h3>Overall</h3>', 10);
370  s = cat(2, s, '<h4>Percentages</h4>', 10);
371  s = cat(2, s, '<h5>Mean</h5>', 10);
372  oc = o.get_confusion([], 1, 3, flag_discount_rejected); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
373  s = cat(2, s, '<h5>Standard Deviation</h5>', 10);
374  oc = o.get_confusion([], 1, 4);
375  s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
376  s = cat(2, s, '<h4>Accumulated hits</h4>', 10);
377  oc = o.get_confusion([], 0, 1); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
378 
379  if o.flag_support
380  s = cat(2, s, '<h4>Supports</h4>', 10);
381  s = cat(2, s, '<h5>Mean</h5>', 10);
382  oc = o.get_confusion_sup([], 3); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
383  s = cat(2, s, '<h5>Standard Deviation</h5>', 10);
384  oc = o.get_confusion_sup([], 4); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
385  end;
386  s = cat(2, s, '<hr/>', 10);
387 
388  if flag_individual
389  if o.t > 1
390  ss = {'Hits', 'Percentages'};
391  s = cat(2, s, '<h3>Individual</h3>', 10);
392  for i = 1:2
393  s = cat(2, s, '<h4>', ss{i}, '</h4>', 10);
394  for j = 1:o.t
395  oc = o.get_confusion(j, i-1, 0, flag_discount_rejected); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
396  end;
397  s = cat(2, s, '<hr/>', 10);
398  end;
399 
400  if o.flag_support
401  s = cat(2, s, '<h4>Supports (means)</h4>', 10);
402  for j = 1:o.t
403  oc = o.get_confusion_sup(j, 3); s = cat(2, s, '<center>', oc.get_html_table(), '</center>');
404  end;
405  s = cat(2, s, '<hr/>', 10);
406  end;
407  end;
408  end;
409  end;
410 
411  %> Generates one dataset per row containing percentages.
412  %> The i-th dataset, j-th feature corresponds to the classification rates of consufion cell (i, j) (rejected
413  %> included)
414  function dd = extract_datasets(o)
415  if o.t < 10
416  irwarning('Are you sure you want to extract datasets from estlog that has less than 10 confusion matrices?');
417  end;
418  [nr, nf, no] = size(o.hits); %#ok<NASGU>
419  rl = o.get_rowlabels();
420  C = o.get_C([], 1, 0);
421  C = permute(C, [3, 2, 1]);
422  for i = 1:nr
423  d = irdata;
424  d.X = C(:, :, i);
425  d.title = ['Row ', int2str(i), ' - ', rl{i}];
426  d.fea_x = 1:nf;
427  d.fea_names = strcat('Column "', ['Rejected', o.get_collabels()], '"');
428  d = d.assert_fix();
429  dd(i) = d;
430  end;
431  end;
432 
433  %> Each cell needs be a different dataset because number of supports (therefore dataset's @c no ) is different for each cell.
434  %>
435  %> @param ij (number of datasets)x(2) containing coordinates from the confusion matrix to extract supports from.
436  %> Please note that j=1 is the "rejected" column, not the first colummn class.
437  function dd = extract_datasets_sup(o, ij)
438  nd = size(ij, 1);
439  cl = o.get_collabels();
440  rl = o.get_rowlabels();
441  for id = 1:nd
442  i = ij(id, 1);
443  j = ij(id, 2);
444  d = irdata;
445  d.X = [o.supports{i, j, :}]';
446  if j == 1
447  collabel = 'Rejected';
448  else
449  collabel = cl{j-1};
450  end;
451  d.title = ['Supports ', rl{i}, '->', collabel];
452  d.fea_x = 1;
453  d.fea_names = {['Support ', rl{i}, '->', collabel]};
454  d = d.assert_fix();
455  dd(id) = d;
456  end;
457  end;
458 
459  %> Abstract. Classification rate, accuracy, performance or whatever.
460  function z = get_rate(o)
461  z = 0;
462  end;
463 
464  %> Abstract. Classification rate vector calculated time-wise.
465  function z = get_rates(o)
466  z = zeros(1, size(o.hits, 3));
467  end;
468 
469  function oc = extract_confusion(o)
470  oc = o.get_confusion([], 1, 3);
471  end;
472 
473  %> Whether the values returned by get_rate() and get_rates() are percentages or not. Returns TRUE
474  function z = get_flag_perc(o)
475  z = 1;
476  end;
477 
478  function z = get_unit(o)
479  z = '%';
480  end;
481  end;
482 end
function irverbose(in s, in level)
Visualization - Balls visualization for Confusion Matrices.
Definition: vis_balls.m:2
Base class for all ensemble classifiers.
Definition: aggr.m:6
function irerror(in s)
Dataset class.
Definition: irdata.m:30
Block that resolves estimato posterior probabilities into classes.
Definition: decider.m:10
Estimation logs base class.
Definition: estlog.m:4
function distribution(in x, in no_points, in range, in wid)
Property X
[no]x[nf] matrix. Data matrix
Definition: irdata.m:52
Class representing a Confusion matrix.
Definition: irconfusion.m:5
Analysis Session (AS) base class.
Definition: as.m:6
Report base class.
Definition: irreport.m:8
function irwarning(in s)
Train-Test Log.
Definition: ttlog.m:4