1 %> @ingroup graphicsapi
3 %> @brief BioMarker Table
5 %> This is old code that is still in use
for its ability to draw the
"Peak Location" plots
7 %> It was originally a
class to facilitate biomarker comparison between different datasets/methods. It can draw the
8 %>
"grades" curves (e.g. loadings, p-values, feature frequency histograms) and Peak Location Plots thereof.
11 %> have an option to visualize loadings/grades vectors
as "Peak Location" plots @ref
vis_log_grades
13 %> If want to use
this class directly, use @ref demo_bmtable.m
as a template to perform the following settings:
15 %> <li>Assign @c blocks, @c datasets, @c peakdetectors, @c arts, @c units, and @c data_hint (optional)</li>
16 %> <li>Setup the @c grid property</li>
17 %> <li>Call draw_pl() or draw_lines()</li>
26 %> @c i_block, @c i_dataset, @c i_peakdetector, @c i_art, @c i_unit point to elements in respective properties;
27 %> @c flag_sig indicated whether to draw significance threshold line; @c params if passed to get_gradeslegend() and
28 %> get_grades() (please note: post-training!); @c peakidxs, @c peak_y, @c y are calculated with shake_grid().
29 grid = struct('i_block', {}, 'i_dataset', {}, 'i_peakdetector', {}, 'i_art', {}, 'i_unit', {}, 'params', {}, 'flag_sig', {}, ...
30 'peakidxs', {}, 'peak_y', {}, 'y', {});
33 %> datasets are used only if @ref rowname_type is 'dataset'. They are used to get row names (will be dataset.title)
35 %> If not used, <code>grid.i_dataset</code> will be irrelevant.
37 %> Cell of @ref
block objects
41 %> Cell of @ref
bmart objects
43 %> Cell of @ref
bmunit objects
45 %> Vector of indexes. As many elements
as @c grid rows. Each element is the index of a grid column.
47 %> Significance level. Maybe the granularity of this will change (doubt it).
48 sig_threshold = -log10(0.05);
49 %> Vector of indexes. As many elements
as @c grid rows. Each element is the index of a grid column.
51 %> ='dataset'. May be @c 'dataset' or '
block'
52 rowname_type = 'dataset';
53 %> ='dataset'. May be @c 'dataset' or '
block'
55 %> Used to draw the hint spectrum
57 %> =3.5 Width for lineas and markers. Note that this number will be actually multiplied by the global SCALE.
59 %> =0. Whether or not to train the blocks
63 properties(SetAccess=protected)
64 % %> Dimensions (number of datasets) X (number of blocks)
66 %> Row-wise information.
68 %> Column-wise information.
70 %> Whether the x-axis is reverse or not
75 properties(SetAccess=protected)
82 % o.classtitle = 'BMTable';
83 % o.flag_ui = 0; % Not published in GUI
86 function o = check_booted(o)
93 o.flag_data = ~isempty(o.datasets);
98 %-------------------------------------------------------------------------------------------------------------------------------
100 %> Draws all the grades curves from one row of the grid
102 %> This function draws the grades for each item of one given row of the @ref
bmtable::grid. Each grade is drawn separately, creating
103 %> a multi-panel figure; however, it does not use sub-plots; instead, it draws the whole figure from scratch in one single box-less
105 function o = draw_lines(o, i_row)
106 o = o.check_booted();
108 nj = size(o.grid, 2);
109 flag_sig = ~isempty(o.sig_j) && length(o.sig_j) >= i_row && o.sig_j(i_row) > 0;
111 stuff = struct('ytick', [], 'yticklabel', {}, 'hh', [], 'legends', {});
114 % Determines x-extremities among all x-axes among all blocks in case
118 wn1 = min(wn1, min(o.grid{i_row, j}.x));
119 wn2 = max(wn2, max(o.grid{i_row, j}.x));
125 cel = o.grid{i_row, j};
126 yformat = o.units{cel.i_unit}.yformat;
128 y1 =
cy(dl.maxy, dl);
129 y2 =
cy(dl.miny, dl);
131 stuff.ytick(end+1) = y1;
132 stuff.yticklabel{end+1} = sprintf(yformat, dl.maxy);
134 % Significance hachure
for current box
136 o.draw_significance(o.grid{i_row, o.sig_j(i_row)},
cy([dl.maxy, dl.miny], dl));
139 % The grades curve itself
140 stuff = o.draw_line(i_row, j, stuff);
142 % Significance threshold line
143 if flag_sig && o.sig_j(i_row) == j % I.e., the curve that gives the significance level is the very one just drawn.
149 plot([wn1, wn2, wn2, wn1, wn1], [y1, y1, y2, y2, y1],
'-',
'Color', [1, 1 1]*0,
'LineWidth',
scaled(2));
151 % ???????????????????
152 % plot([wn1, wn2], [1, 1]*y2,
'-',
'Color', [1, 1 .1]*0,
'Linewidth', 2);
154 % plot([wn1, wn2], [1, 1]*dl.axisylim(1),
'-k',
'LineWidth', 2);
159 stuff.ytick(end+1) = y2;
160 stuff.yticklabel{end+1} = sprintf(yformat, dl.miny);
165 % Fabricates the x ticks
166 xtick = floor(max(wn1, wn2)/100)*100:-100:ceil(min(wn1, wn2)/100)*100;
167 for i = 1:length(xtick);
168 if xtick(i)/200 == floor(xtick(i)/200)
169 hhfrank(end+1) = text(xtick(i), -.05,
format_number(xtick(i)), 'HorizontalAlignment', 'center');
173 ymin =
cy(dl.miny, dl);
174 ymax =
cy(dl.maxy, dl);
176 plot([1, 1]*xtick(i), ymin+[0, EHEH_NEGO], 'k', 'LineWidth',
scaled(2));
177 plot([1, 1]*xtick(i), ymax+[0, -EHEH_NEGO], 'k', 'LineWidth', scaled(2));
183 % Now fabricates the y ticks
184 xtext =
iif(o.flag_reverse, wn2+scaled(3), wn1-scaled(3));
185 for i = 1:length(stuff.ytick)
186 hhfrank(end+1) = text(xtext, stuff.ytick(i), stuff.yticklabel{i},
'HorizontalAlignment',
'right');
192 set(gca,
'YLim', [-0.005, 1.005]);
193 set(gca,
'XLim', [min(wn1, wn2)-1, max(wn1, wn2)+1]);
195 set(gca,
'XDir',
'reverse');
197 legend(stuff.hh, stuff.legends);
200 set(gcf,
'Color', [1, 1, 1]);
207 %-------------------------------------------------------------------------------------------------------------------------------
209 %> Draws the Peak Locations (PL) plot
210 function o = draw_pl(o, flag_wntext, flag_sig)
211 o = o.check_booted();
214 if ~exist('flag_wntext', 'var')
218 if ~exist('flag_sig', 'var')
222 MARKER_MAXSIZE = 17*SCALE;
225 [ni, nj] = size(o.grid);
227 % Determines x-extremities among all x-axes among all blocks
232 wn1 = min(wn1, min(o.grid{i, j}.x));
233 wn2 = max(wn2, max(o.grid{i, j}.x));
241 xlim = [floor(round(wn1/50)*50/100)*100, ceil(round(wn2/50)*50/100)*100];
247 % % Significance indication
249 %
for i_dataset = 1:ni
250 % height = i_dataset;
252 % item = o.items{i_dataset};
255 %
for i = 1:length(item.bmresults)
256 %
if strcmp(item.bmresults{i}.prefix,
'bmresult_p')
257 % bm_significance = item.bmresults{i};
262 % bmresult_draw_significance(bm_significance, o, [height+.45, height-.45]);
266 if ~isempty(o.data_hint)
267 hint = mean(o.data_hint.X, 1);
268 hint = -hint/max(hint)*.5*ni+ni+1;
274 % Draw the vertical lines which will help identify the wavenumbers
276 for x = ceil(wn1/50)*50:50:floor(wn2/50)*50
278 % vertical bmresult cannot overlap the "natural" matlab tick
279 if x/200 == floor(x/200)
285 plot([x, x], [uf, ni+1-uf], '--', 'LineWidth', 1.5*SCALE, 'Color', .5*[1, 1, 1]);
291 FLAG_ONEMARKERSCALE = 0;
293 if FLAG_ONEMARKERSCALE
299 ycalc = abs(cel.y(cel.peakidxs));
300 miny = min([ycalc(:)
', miny]);
301 maxy = max([ycalc(:)', maxy]);
307 % Calculates x
for "rowname"
308 xspan = abs(wn2-wn1);
310 xtext =
iif(o.flag_reverse, wn2+xspc, wn1-xspc);
315 % Significance hachure
for current line
316 flag_sig = ~isempty(o.sig_j) && length(o.sig_j) >= i && o.sig_j(i) > 0;
318 o.draw_significance(o.grid{i, o.sig_j(i)}, [height+.45, height-.45]);
321 plot(xlim, height*[1, 1],
'k',
'LineWidth',
scaled(3)); % Horizontal line
325 hh(end+1) = text(xtext, height, o.get_rowname(i),
'HorizontalAlignment',
'right');
329 % Makes box before the markers so that the markers appear on top
330 set(gca,
'XTick', ceil(wn1/100)*100:200:ceil(wn2/100)*100);
331 set(gca,
'XLim', [min(wn1, wn2)-1, max(wn1, wn2)+1]);
332 % set(gca,
'XGrid',
'on');
333 % set(gca,
'XMinorGrid',
'on');
334 % set(gca,
'XMinorTick',
'on');
335 set(gca,
'YTick', []); %1:ni);
336 % set(gca,
'YTickLabel', []);
337 set(gca,
'YLim', [0, ni+1]);
339 set(gca,
'XDir',
'reverse');
341 set(gca,
'YDir',
'reverse');
349 ll = o.add_legend(i, j, ll);
351 art = o.arts{cel.i_art};
352 y = cel.y(cel.peakidxs);
354 if sum(y == Inf) == numel(y)
357 ycalc(y == Inf) = max(y(y ~= Inf));
359 x = cel.x(cel.peakidxs);
360 no_markers = length(x);
362 if max(ycalc) ~= min(ycalc)
363 if ~FLAG_ONEMARKERSCALE
367 markersizes = (ycalc-miny)/(maxy-miny)*(MARKER_MAXSIZE-MARKER_MINSIZE)+MARKER_MINSIZE;
369 markersizes = ones(1, no_markers)*MARKER_MAXSIZE;
372 for i_marker = 1:no_markers
374 htemp = plot(x(i_marker), height, 'LineStyle', 'none', 'Marker', art.marker, 'Color', art.color, ...
375 'MarkerSize', markersizes(i_marker)*art.markerscale, 'LineWidth', o.linewidth*SCALE); %, 'MarkerFaceColor', 'w');
377 text(x(i_marker), height-.1, sprintf('%4.0f', x(i_marker)));
392 %=================================================================================================================================
398 methods(Access=protected)
399 %> Draws a line an updates 'stuff' structure. This structure contains yticks, yticklabels,
400 %> handles for legends and legend texts
401 function stuff = draw_line(o, i, j, stuff)
405 art = o.arts{cel.i_art};
410 % Draws the curve <----------------------------
411 if ~o.units{cel.i_unit}.flag_hist
412 % htemp will be used
for legend
if there is no peak idx
413 htemp = plot(cel.x,
cy(cel.y, dl),
'Color', art.color,
'LineWidth', o.linewidth*SCALE);
415 htemp = stem(cel.x,
cy(cel.y, dl),
'Color', art.color,
'LineWidth', o.linewidth*SCALE*1.5,
'Marker',
'none',
'BaseValue',
cy(0, dl));
420 if o.units{cel.i_unit}.flag_zeroline
421 plot([cel.x(1), cel.x(end)], [1, 1]*
cy(0, dl),
'Color', art.color,
'LineWidth', o.linewidth*SCALE);
422 stuff.ytick(end+1) =
cy(0, dl);
423 stuff.yticklabel{end+1} =
'0';
426 % Legend: tries to use marker
as symbol, otherwise the line
427 if ~isempty(cel.peakidxs)
428 stuff.hh(end+1) = plot(cel.x(cel.peakidxs),
cy(ypeaks(cel.peakidxs), dl), ...
429 'Color', art.color, 'Marker', art.marker, 'LineWidth', o.linewidth*SCALE, 'MarkerSize', 15*sqrt(SCALE), 'LineStyle', 'none' ...
432 stuff.hh(end+1) = htemp;
435 if strcmp(o.colname_type, 'dataset')
436 if isempty(o.datasets)
437 irerror('Wants to take legend from dataset, but datasets property is empty!');
439 stuff.legends{end+1} = o.datasets(cel.i_dataset).title;
440 elseif strcmp(o.colname_type,
'block')
441 stuff.legends{end+1} = o.get_gradeslegend(cel);
447 function o = draw_significance(o, cel, yy)
450 flagg = cel.y >= o.sig_threshold;
452 spc = abs(cel.x(2)-cel.x(1));
454 % Non-significant intervals will ba hatched
455 flag_in = 0; % Inside a non-significant
456 for i = 1:length(flagg)+1
457 if flag_in && (i > length(flagg) || flagg(i))
459 draw_hachure([cel.x(i-1)-spc/4, y2, abs(cel.x(mark)-cel.x(i-1))+spc/2, y1-y2]);
461 elseif ~flag_in && i <= length(flagg) && ~flagg(i)
471 %> Empowers grid with calculated fields
473 %> <h3>grid.params</h3>
474 %> Grid cell parameters have defaults "idx_fea"=1 and "flag_abs"=1
475 function o = shake_grid(o)
477 [ni, nj] = size(o.grid);
482 if ~isempty(cel.params)
483 cel.params =
setbatch(struct(), cel.params); % Converts {name, value, ...} into proper fields
485 if ~isfield(cel.params,
'idx_fea')
486 cel.params.idx_fea = 1;
488 if ~isfield(cel.params, 'flag_abs')
489 cel.params.flag_abs = 1;
493 cel.
block = o.get_initialblock(cel);
494 if o.flag_data && o.flag_train
496 cel.
block = cel.
block.train(o.datasets(cel.i_dataset));
499 cel.y = o.get_grades(cel);
500 cel.x = o.get_grades_x(cel);
501 if cel.i_peakdetector > 0 && numel(o.peakdetectors) > 0
502 pd = o.peakdetectors{cel.i_peakdetector};
503 pd = pd.
boot(cel.x, cel.y);
504 cel.peakidxs = pd.use([], cel.y);
511 o.flag_reverse = cel.x(1) > cel.x(end);
519 %> Calculations before draw_lines()
521 %> First pass finds minima and maxima
for each column across all rows. This will ensure that all draw_lines() (
using one row only)
522 %> will nevertheless use y-scales that will be the same
for any row wich which draw_lines() is called.
524 %> Second pass will transfer the minima and maxima to absolute graphical coordinates,
as I am not
using subplots, but drawing the
525 %> figure from scratch instead
526 function o = calc_cols(o)
527 [ni, nj] = size(o.grid);
529 % First pass: Searches
for maximum and minimum over all row cases of method j
535 % ytemp(ytemp == Inf) = 0; % This intervention prevents log(0) from flattening some scale
537 maxy = max(maxy, max(ytemp));
538 miny = min(miny, min(ytemp));
540 if o.units{o.grid{i, j}.i_unit}.flag_zeroline || o.units{o.grid{i, j}.i_unit}.flag_zero
547 o.coldata(j).maxy = maxy;
548 o.coldata(j).miny = miny;
553 spacing = (1-FRAMEPERC)/(nj-1);
554 height = FRAMEPERC/nj;
556 % Second pass: calculates absolute y limits, offsets and scales
559 o.coldata(j).scale = 1/(o.coldata(j).maxy-o.coldata(j).miny)*FRAMEPERC/nj;
560 o.coldata(j).offset = ypos;
572 o.coldata(j).axisylim = [ypos-a1, ypos+height+a2];
574 ypos = ypos+height+spacing;
578 %> Makes legends: plots the markers then calls the legend() function passing the appropriate handles to it.
580 %> Currently used only by the PL plot. draw_lines() does not use it
581 function o = make_legend(o, ll)
584 ss = cell(1, n); % strings
585 hh = zeros(1, n); % handles
587 cel = o.grid{ll(i).i, ll(i).j};
588 art = o.arts{cel.i_art};
589 % plots somewhere out of the plot area, just to give the handle to legend()
590 hh(i) = plot(0, 10, 'Marker', art.marker, 'Color', art.color, 'MarkerSize', 10, 'LineWidth', o.linewidth*SCALE, 'LineStyle', 'none');
593 if strcmp(o.colname_type, 'dataset')
594 ss{i} = o.datasets(cel.i_dataset).title;
595 elseif strcmp(o.colname_type,
'block')
596 ss{i} = o.get_gradeslegend(cel);
603 %> Checks
if new (
block, params)
case is found;
if not, adds
case
604 function ll = add_legend(o, i, j, ll)
608 idxs = find([ll.i_block] == cel.i_block);
610 if compare(cel.params, ll(idx).params)
617 ll(end+1).i_block = cel.i_block;
618 ll(end).params = cel.params;
625 function z = get_rowname(o, i)
626 if ~isempty(o.rowname_j)
633 switch o.rowname_type
635 z = o.datasets(cel.i_dataset).title;
637 z = o.get_gradeslegend(cel);
641 function blk = get_initialblock(o, cel)
642 blk = o.blocks{cel.i_block};
648 %> Sensitive to
"idx_fea"=1 and
"flag_abs"=1 in @c params
649 function g = get_grades(o, cel)
654 g = blk.L(:, params.idx_fea)';
658 elseif isa(blk, '
fsel')
659 if ~isempty(blk.grades)
662 g = zeros(1, numel(blk.grades_x));
669 function x = get_grades_x(o, cel)
671 % params = cel.params;
675 elseif isa(blk, '
fsel')
678 irerror(sprintf('Cannot get grades x for
object of class "%s"', class(blk)));
682 %> @brief Grades legend for cell
684 %> Backwards, calls of get_L_fea_names(), then sequence of get_description calls
686 %> others: makes default with idx_fea and flag_abs
687 function s = get_gradeslegend(o, cel)
691 s = blk.get_description();
693 names = blk.get_L_fea_names(params.idx_fea);
694 s = cat(2, s, ' ', names{1});
Feature Construction - Linear Transformations base class.
Feature Selection (FSel) class.
function compare(in o1, in o2)
Representation of a Unit.
Art stuff for BioMarker Tables.
function setbatch(in o, in params)
function boot(in o)
Configures the structure to deal with new type of data.
function draw_hint_curve(in x, in y, in color)
function draw_threshold_line(in x, in y, in width, in color)
Visualization - Grades vector calculated by any as_grades.
function iif(in cond, in x1, in x2)
function resize_legend_markers(in size, in flag_line)
Visualization - Loadings plots or Peak Location plots for loadings vectors.
Analysis Session (AS) base class.
function protect_against_inf(in y)
Cascade block: sequence of blocks represented by a block.
function draw_hachure(in position)
function draw_loadings_pl(in x, in L, in x_hint, in hint, in legends, in flag_abs, in peakd, in colorindexes)