IRootLab
An Open-Source MATLAB toolbox for vibrational biospectroscopy
irdata.m
Go to the documentation of this file.
1 %> @brief Dataset class
2 %>
3 %> <h3>The @c X property</h3>
4 %> The @c X property has dimensions [@ref no]x[@ref nf]. Each row represents one physical spectrum. Each column represents a
5 %> "feature".
6 %>
7 %> <h3>Classes and Class Levels</h3>
8 %> In IRootLab, dataset @c classes are 0-based, so valid classes will range from @c 0 to @c (\ref nc-1). @c Classes correspond to elements in the
9 %> @c classlabels property.
10 %>
11 %> Negative classes have special meanings. See @ref get_negative_meaning.m
12 %>
13 %> The class labels may define a <bold>multi-level labelling system</bold>, with different
14 %> levels separated by a vertical slash ("|"). See the example:
15 %> @code
16 %> dataset1.classlabels = { ...
17 %> 'Normal |UK', ...
18 %> 'LowGrade|UK', ...
19 %> 'HiGrade |UK', ...
20 %> 'Normal |IR', ...
21 %> 'LowGrade|IR', ...
22 %> 'HiGrade |IR' ...
23 %> };
24 %> @endcode
25 %> In the example above, the first level represents the cancer grade, whereas the second level represents the country.
26 %> If a spectrum was taken from an individual who is from Ireland and has Low-grade cancer, its class wil be 4 (remember
27 %> that the classes are zero-based!). There are resources to work with class levels (see blmisc_classlabels_hierarchy)
28 %>
30 classdef irdata < irobj
31  properties
32  %> number of "observations" (e.g. spectra)
33  no;
34  %> number of features (i.e., variables)
35  nf;
36  %> a vector [no, nf]
37  nonf;
38  %> number of groups
39  no_groups;
40  %> number of classes
41  nc;
42 
43  %> [no]x[nf] matrix. Data matrix
44  X = [];
45  %> [no]x[1] vector. Classes. Zero-based (first class is class zero).
46  %>
47  %> Classes may be negative, with special meanings for negative values (see @ref get_negative_meaning.m)
48  classes = [];
49  %> Cell of strings. Class labels
50  classlabels = {};
51 
52  %> (optional) [no]x[1] Cell of strings. Group codes (e.g. patient names)
53  groupcodes = {};
54  %> (optional) [no]x[1] Cell of strings. Observation names (e.g. file names of the individual spectra)
55  obsnames = {};
56 
57  filename = '';
58  %> mat or txt
59  filetype = '';
60 
61  %> feature x-axis
62  fea_x = [];
63  %> (optional) Cell of strings. Name of each feature
64  fea_names = {};
65 
66  %> x-axis name, defaults to 'Wavenumber (cm^{-1})'
67  xname = 'Wavenumber';
68  %> x-axis unit, defaults to 'cm^{-1}'
69  xunit = 'cm^{-1}';
70  %> y-axis name, defaults to 'Absorbance'
71  yname = 'Absorbance';
72  %> y-axis unit, defaults to 'a.u.'
73  yunit = 'a.u.';
74 
75 
76  % Image properties
77  %> Height of image. Spectra start counting from the bottom left upwards.
78  height;
79  %> Width of image. Width is actually calculated as @c no/height . If result is not integer, an error will occur.
80  width;
81  %> ='ver'. States how the pixels are organized.
82  %> 'ver': bottom-up, left-right
83  %> 'hor': left-right, bottom-up
84  direction = 'ver';
85 
86  %> Output (instead of classes). For regression instead of classification
87  Y = [];
88 
89  %> For easier access than groupcodes
90  groupnumbers = [];
91  obsids = [];
92 
93  splitidxs = [];
94  end;
95 
96  properties(SetAccess=protected)
97  %> fields to be split or merged when dataset is split or merged
98  rowfieldnames = {'groupcodes', 'groupnumbers', 'obsnames', 'obsids', 'classes', 'X', 'Y', 'splitidxs'};
99  flags_cell = [1, 0, 1, 0, 0, 0, 0, 0];
100  end;
101 
102  methods(Access=protected)
103  %> HTML inner body
104  function s = do_get_html(o)
105  s = '';
106 
107  s = cat(2, s, '<h1>Data classes</h1><center>', 10);
108  nl = o.get_no_levels();
109 
110 
111  % List of class labels with number of spectra and number of groups per class
112  da = o;
113  pie = data_split_classes(da);
114  n = numel(pie);
115  cc = cell(n+2, 4);
116  cc(1, 1:4) = {'Index', 'Class label', 'Number of rows', 'Number of groups'};
117  for j = 1:n
118  cc(j+1, 1:4) = {j, pie(j).classlabels{1}, pie(j).no, pie(j).no_groups};
119  end;
120  cc(end, 1:4) = {'', 'Total', da.no, da.no_groups};
121  s = cat(2, s, cell2html(cc));
122 
123 
124  % PER-LEVEL list of class labels with number of spectra and number of groups per class
125  if nl > 1
126  for i = 1:nl
127  s = cat(2, s, '<h2>Level ', int2str(i), '</h2>', 10);
129  da = data_select_hierarchy(o, i);
130  pie = data_split_classes(da);
131  n = numel(pie);
132  cc = cell(n+2, 3);
133  cc(1, 1:3) = {'Class label', 'Number of rows', 'Number of groups'};
134  for j = 1:n
135  cc(j+1, 1:3) = {pie(j).classlabels{1}, pie(j).no, pie(j).no_groups};
136  end;
137  cc(end, 1:3) = {'Total', da.no, da.no_groups};
138  s = cat(2, s, cell2html(cc));
139  end;
140  end;
141 
142 
143  % Negative classes (outliers etc)
144  if any(o.classes < 0)
145  s = cat(2, s, '<h2>Negative classes</h2>', 10);
146  neg = unique(o.classes(o.classes < 0));
147  nneg = numel(neg);
148 
149  cc = cell(nneg+1, 3);
150  cc(1, 1:3) = {'Class', 'Meaning', 'Number of rows'};
151 
152  for i = 1:nneg
153  cc(i+1, :) = {neg(i), get_negative_meaning(neg(i)), sum(o.classes == neg(i))};
154  end;
155  s = cat(2, s, cell2html(cc));
156  end;
157 
158  s = cat(2, s, '<hr />', 10);
159 
160  % Class means (figure)
161  v = vis_means();
162  figure;
163  v.use(o);
164  maximize_window([], 2);
165  s = cat(2, s, irreport.save_n_close());
166 
167 
168  % List of groups
169  if ~isempty(o.groupcodes)
170  s = cat(2, s, '<h2>Group list</h2>', 10);
171 
172  gg = unique(o.groupcodes);
173  gg = gg(:); % To make sure that it is a column vector
174 
175  for i = 1:numel(gg)
176  gg{i, 2} = sum(strcmp(o.groupcodes, gg{i, 1}));
177  end;
178  gg = [{'Group code', 'Number of rows'}; gg]; % Adds title
179  s = cat(2, s, cell2html(gg));
180  end;
181 
182  s = cat(2, s, '</center>', 10, do_get_html@irobj(o));
183  end;
184  end;
185 
186 
187  methods
188  %> Constructor
189  function data = irdata()
190  data.classtitle = 'Dataset';
191  data.color = [5, 171, 191]/255;
192  end;
193 
194 
195 
196  %> no getter
197  function z = get.no(data)
198  z = size(data.X, 1);
199  end;
200 
201  %> nf getter
202  function z = get.nf(data)
203  z = size(data.X, 2);
204  end
205 
206  %> nonf getter
207  function z = get.nonf(data)
208  z = size(data.X);
209  end;
210 
211  %> nc getter
212  function z = get.nc(data)
213  z = length(data.classlabels);
214  end;
215 
216  %> no_groups getter
217  function z = get.no_groups(data)
218  z = length(unique(data.groupcodes));
219  end;
220 
221  function z = get.width(data)
222  if data.height <= 0
223  irerror('Invalid height!');
224  end;
225 
226  z = data.no/data.height;
227  if z ~= floor(z)
228  irerror('Number of rows is not divisible by the dataset height!');
229  end;
230  end;
231 
232  %> Converts group codes to group indexes
233  %> Indexes will point to the "unique(data.groupcodes)" vector
234  function idxs = get_groupidxs_from_groupcodes(data, codes)
235  n = numel(codes);
236  idxs = zeros(1, n);
237  ref = unique(data.groupcodes);
238  for i = 1:n
239  ii = find(strcmp(codes{i}, ref));
240  if isempty(ii)
241  irerror(sprintf('Group code %s not present in dataset!', codes{i}));
242  end;
243  idxs(i) = ii;
244  end;
245  end;
246 
247  %> Converts group indexes to observation indexes
248  %> CAUTION: be sure that idxs_codes contains indexes that point to
249  %> the "unique(data.groupcodes)" vector
250  function obsidxs = get_obsidxs_from_groupidxs(data, groupidxs)
251  group_code_list = unique(data.groupcodes);
252  v = 1:data.no; %> index vector
253 
254  %> Counts to pre-allocate
255  cnt = 0;
256  for i = 1:length(groupidxs)
257  cnt = cnt+sum(strcmp(group_code_list{groupidxs(i)}, data.groupcodes));
258  end;
259 
260  %> Loops again to fill
261  obsidxs = zeros(1, cnt);
262  ptr = 1;
263  for i = 1:length(groupidxs)
264  vtemp = v(strcmp(group_code_list{groupidxs(i)}, data.groupcodes));
265  vlen = length(vtemp);
266  obsidxs(ptr:ptr+vlen-1) = vtemp;
267  ptr = ptr+vlen;
268  end;
269  end;
270 
271  %> Returns the number of levels in @c classlabels
272  function nl = get_no_levels(data)
273  nl = 0;
274  for i = 1:data.nc
275  nl = max(nl, sum(data.classlabels{i} == '|')+1);
276  end;
277  end;
278 
279 
280  %> Checks if internal variables are synchronized with some troubleshooting.
281  function data = check(data)
282  if isempty(data.fea_x) || ~isempty(data.X)
283  data.fea_x = 1:data.nf;
284  end;
285  end;
286 
287 
288 
289  %> Copies structure fields to object fields
290  %> Contains a dictionary with many old property names for backward
291  %> compatibility
292  %> Also works when the input is an object.
293  function data = import_from_struct(data, DATA)
294  temp = setxor(properties(data)', {'nf', 'nonf', 'no_groups', 'nc', 'width'})';
295  propmap = repmat(temp, 1, 2);
296  propmap = [propmap; {'x', 'fea_x'; 'class_labels', 'classlabels'; 'idspectrum_s', 'obsids'; 'file_names', 'obsnames'; ...
297  'colony_codes', 'groupcodes'; 'group_codes', 'groupcodes'}]; % Names that changed over time
298 
299 
300  if ~isa(DATA, 'irdata') && ~isa(DATA, 'struct')
301  irerror(['DATA argument is of class "', class(DATA), '" but should be "irdata"']);
302  end;
303 
304  ff = fields(DATA);
305  for i = 1:size(propmap, 1)
306  sold = propmap{i, 1};
307  snew = propmap{i, 2};
308  if ismember(sold, ff)
309  data.(snew) = DATA.(sold);
310  end;
311  end;
312  end;
313 
314 
315 
316 
317 
318 
319 
320 
321  %> retains only labels corresponding to classes that exist in the
322  %> dataset, and classes are renumbered accordingly
323  function data = eliminate_unused_classlabels(data)
324  uncl = unique(data.classes(data.classes >= 0)); % gets used classes
325  data.classlabels = data.classlabels(uncl+1);
326 
327  for j = 1:numel(uncl)
328  data.classes(data.classes == uncl(j)) = j-1;
329  end;
330  end;
331 
332  %> @brief Populates from a time series
333  %>
334  %> This function makes X and Y. X will be a Toeplitz matrix.
335  %>
336  %>
337  %> Inputs:
338  %> @param signal vector s(n)
339  %> @param no_inputs dimensionality of the input data space (aka number of features or nf)
340  %> @param future "prediction task", which will be to predict s(n+future)
341  function data = mount_from_signal(signal, no_inputs, future)
342 
343  len_signal = length(signal);
344 
345 
346  %>This is the maximum number of rows of the dataset before something blows
347  no_rows = len_signal-no_inputs-future;
348 
349 
350  %>Mounts X and Y
351  X = zeros(no_rows, no_inputs);
352  Y = zeros(no_rows, 1);
353 
354  for i = 1:no_rows
355  %> each data row will stand for [s(n) s(n-1) s(n-2) ...]. This way the dot product between the row and the
356  %> coefficients of a linear filter is a causal convolution.
357  X(i, :) = signal(i+no_inputs-1:-1:i);
358  Y(i, 1) = signal(i+no_inputs-1+future);
359  end;
360 
361  data.X = X;
362  data.Y = Y;
363  end;
364 
365 
366 
367  %> Gets a list with all properties except the ones that will be
368  %> split
369  function pp = get_props_to_copy(data)
370  pp = setxor(properties(data), data.rowfieldnames);
371  pp = setxor(pp, {'flag_params', 'rowfieldnames', 'flags_cell'});
372  end;
373 
374  %> Makes copy with empty fields whose names are in .rowfieldnames
375  %> Additionally, resets .height
376  function dnew = copy_emptyrows(data)
377  dnew = data;
378  dnew.height = [];
379  rr = data.rowfieldnames;
380 
381  for i = 1:numel(rr)
382  if data.flags_cell(i)
383  dnew.(rr{i}) = {};
384  else
385  dnew.(rr{i}) = [];
386  end;
387  end;
388 % tt = tic();
389 % global TIMEFSG;
390 % pp = data.get_props_to_copy();
391 % TIMEFSG = TIMEFSG+toc(tt);
392 % dnew = feval(class(data));
393 % for j = 1:length(pp)
394 % dnew.(pp{j}) = data.(pp{j});
395 % end
396  end;
397 
398 
399 
400 
401 
402 
403 
404 
405 
406  %> Splits dataset into one or more datasets using row maps
407  %>
408  %> @param map 1D or 2D cell array of row indexes
409  %> @param feamap (optional
410  %> @retval out Matrix of datasets.
411  function out = split_map(data, map, feamap, fext)
412 
413  if ~iscell(map)
414  map = {map};
415  end;
416 
417  flag_fext = nargin > 3 && ~isempty(fext);
418  if flag_fext
419  if ~fext.flag_trainable
420  irerror('fext parameter must be trainable, otherwise it does not make sense!');
421  end;
422  fext = fext.boot();
423  end;
424 
425  flag_feamap = nargin >= 3 && ~isempty(feamap);
426 
427  rr = data.rowfieldnames;
428  nr = numel(rr);
429  flags = arrayfun(@(k) ~isempty(data.(rr{k})), 1:nr);
430 
431  [nrow, ncol] = size(map);
432 
433  dnew0 = data.copy_emptyrows(); % Model copy
434 
435  if nrow == 0 || ncol == 0
436  out = [];
437  else
438  %s = '-<';
439  for i = nrow:-1:1 % Goes backwards to pre-allocate although MATLAB doesn't know.
440  for j = 1:ncol
441  %> prepares a clone, except for the fields in rowfieldnames
442  dnew = dnew0;
443 
444  %> maps the rowfieldnames fields
445  idxs = map{i, j};
446  for k = 1:nr
447  if flags(k) %> maps only the fields that are not empty. This allows fields to
448  %> be used or not as necessary and no error will occur.
449  dnew.(rr{k}) = data.(rr{k})(idxs, :);
450  end;
451  end;
452 
453  dnew = dnew.eliminate_unused_classlabels();
454 
455  if flag_fext
456  if j == 1
457  fextnow = fext.boot();
458  fextnow = fextnow.train(dnew);
459  end;
460  dnew = fextnow.use(dnew);
461  end;
462 
463 
464 
465 
466  if flag_feamap
467  out(i, j, :) = dnew.select_features(feamap);
468  else
469  out(i, j) = dnew;
470  end;
471  %s = cat(2, s, sprintf('+split%d,%d+', i, j));
472  end;
473  end;
474  %s = cat(2, s, sprintf('>-\n'));
475  %irverbose(s, 0);
476  end;
477  end;
478 
479 
480 
481 
482  %> Splits dataset into one or more datasets using its own splitidxs property
483  %>
484  %> @param map 1D or 2D cell array of row indexes
485  %> @retval out Matrix of datasets.
486  function out = split_splitidxs(data)
487  out = data.split_map(splitidxs2maps(data.splitidxs));
488  end;
489 
490 
491  %> Maps rows. Single-output version of split_map()
492  %>
493  %> Returns new object
494  function out = map_rows(data, idxnew)
495  out = data.split_map({idxnew});
496  end;
497 
498 
499  %> Manual feature selection.
500  %>
501  %> Inputs:
502  %> idxs: list of column indexes to select, or cell thereof
503  function out = select_features(data, idxs)
504 % nfold = data.nf;
505  if ~iscell(idxs)
506  out = data;
507  out.X = data.X(:, idxs);
508  if ~isempty(data.fea_x)
509  out.fea_x = out.fea_x(idxs);
510  end;
511  if ~isempty(out.fea_names)
512  out.fea_names = out.fea_names(idxs);
513  end;
514  else
515  nell = numel(idxs);
516  for i = 1:nell
517  if i == 1
518  out(nell) = data; % Pre-allocation
519  out(1) = data;
520  elseif i < nell
521  out(i) = data;
522  end;
523  out(i).X = data.X(:, idxs{i});
524  if ~isempty(data.fea_x)
525  out(i).fea_x = out(i).fea_x(idxs{i});
526  end;
527  if ~isempty(data.fea_names)
528  out(i).fea_names = out(i).fea_names(idxs{i});
529  end;
530  end;
531  end;
532 %> irverbose(sprintf('INFO (data_select_features()): # features before: %>d; # features after: %>d.\n', nfold, data.nf));
533  end;
534 
535 
536  %> @brief Transforms dataset using loadings matrix L
537  %>
538  %> data.X = data.X*L;
539  %> data.xlabel = 'Factor';
540  %> data.ylabel = 'Score';
541  %>
542  %> @param L[nf][any] Loadings matrix
543  %> @param L_fea_prefix=[] Prefix to make new feature names.
544  function data = transform_linear(data, L, L_fea_prefix)
545  data.X = data.X*L;
546  data.fea_x = 1:data.nf;
547  data.xname = 'Factor';
548  data.xunit = [];
549  data.yname = 'Score';
550  data.yunit = [];
551 
552  % Makes feature names
553  if exist('L_fea_prefix', 'var') && ~isempty(L_fea_prefix)
554  data.fea_names = cell(1, data.nf);
555  for i = 1:data.nf
556  data.fea_names{i} = [L_fea_prefix int2str(i)];
557  end;
558  else
559  data.fea_names = {};
560  end;
561  end;
562 
563 
564  %> @brief Returns the names of the features.
565  %>
566  %> This function checks the \c fea_names property and if it is empty, it makes feature names on-the-fly using
567  %> the \c fea_x property.
568  %>
569  %> @param idxs Optional list of indexes to be returned
570  function names = get_fea_names(data, idxs)
571  if ~exist('idxs', 'var')
572  idxs = 1:data.nf;
573  end;
574 
575  if ~isempty(data.fea_names)
576  names = data.fea_names(idxs);
577  else
578  names = cell(1, length(idxs));
579  for i = 1:length(idxs)
580  names{i} = sprintf('Feature %g', round(data.fea_x(idxs(i))*10)/10);
581  end;
582  end;
583  end;
584 
585  %> @brief fills in the @ref groupnumbers property based on the @ref groupcodes property.
586  function data = make_groupnumbers(data)
587  if isempty(data.groupcodes)
588  irverbose('INFO: Dataset groupcodes is empty!', 1);
589  return;
590  end;
591 
592  % Determines the groups
593  codes = unique(data.groupcodes);
594  ng = numel(codes);
595  data.groupnumbers = zeros(data.no, 1);
596 
597  for i = 1:ng
598  data.groupnumbers(strcmp(codes{i}, data.groupcodes)) = i;
599  end;
600  end;
601 
602 
603  %> @brief Makes the dataset properties consistent with each other
604  %>
605  %> This is both an assertion routine and a "fixing" routine. The two parts are implemented sequentially, so it will be easy to split
606  %> this in the future.
607  %>
608  %> The assertion part will do a number of checks and throw an error if there is no hope of making it a consistent dataset. Fatal
609  %> problems will be:
610  %> @arg not empty row fields (listed in the @ref irdata::rowfieldnames read-only property) of different sizes
611  %> @arg @ref irdata::fea_x with number of elements different from @ref irdata::nf
612  %>
613  %> The subsewquent fix part may do a number of works on the dataset:
614  %> @arg autofill the "classes" vector if it is empty (and create a default class label)
615  %> @arg autogenerate the "fea_x" vector if it is empty
616  %> @arg add elements to @ref irdata::classlabels if class numbers surpass the number of labels
617  %>
618  function data = assert_fix(data)
619  %%%%%% Assertion
620 
621  %%% Consistent number of rows
622  nref = -1;
623  for i = 1:numel(data.rowfieldnames)
624  ni = size(data.(data.rowfieldnames{i}), 1);
625  if nref == -1
626  if ni > 0
627  nref = ni;
628  iref = i;
629  end;
630  else
631  if ni > 0
632  if ni ~= nref
633  irerror(sprintf('Fields "%s" and "%s" have different numbers of rows!', data.rowfieldnames{iref}, data.rowfieldnames{i}));
634  end;
635  end;
636  end;
637  end;
638 
639  %%% Consistent fea_x and nf
640  if ~isempty(data.fea_x) && size(data.fea_x, 2) ~= data.nf
641  irerror(sprintf('dataset has %d features, but x-axis vector has %d elements!', data.nf, size(data.fea_x, 2)));
642  end;
643 
644 
645 
646  %%%%%% Fixing
647 
648  %%% Classes
649  if data.no > 0 && isempty(data.classes)
650  data.classes = zeros(data.no, 1);
651  data.classlabels = {'Class 0'};
652  end;
653 
654  %%% fea_x
655  if data.nf > 0 && isempty(data.fea_x)
656  data.fea_x = 1:data.nf;
657  end;
658 
659  %%% Class labels
660  if ~isempty(data.classlabels) && ~iscell(data.classlabels)
661  irerror('"classlabels" must be a cell!');
662  end;
663  nceff = max(data.classes)+1;
664  ncthought = numel(data.classlabels);
665  if nceff > ncthought
666  nl = data.get_no_levels();
667  ss = char('|'*ones(1, nl-1));
668  for i = nceff:-1:ncthought+1
669  data.classlabels{i} = sprintf('Class %d%s', i-1, ss);
670  end;
671  end;
672  end;
673 
674  %> Gets weights for each class
675  %>
676  %> Weights are inversely proportional to the number of observations in each class.
677  %>
678  %> Weights are normalized, so that their sum equals one
679  %> @param exponent =1. Exponent to power all weights before they are normalized to sum=1
680  function ww = get_weights(data, exponent)
681  ww = zeros(1, data.nc);
682  for i = 1:data.nc
683  ww(i) = 1/sum(data.classes == (i-1));
684  end;
685 
686  if nargin > 1 && ~isempty(exponent)
687  ww = ww.^exponent;
688  end;
689  ww = ww/sum(ww);
690  end;
691 
692 
693 
694  %> Changes direction and swaps width and height
695  %>
696  %> This is called "transpose2" because MATLAB objects have a built-in
697  %> "transpose" already
698  function data = transpose2(data)
699  hei = data.height;
700  if isempty(hei)
701  irerror('Height not provided and dataset does not have height.');
702  end;
703  wid = data.no/hei;
704  if floor(wid) ~= wid
705  irerror(sprintf('Height %d not divisible by %d!', hei, data.no));
706  end;
707 
708  if strcmp(data.direction, 'hor')
709  data.direction = 'ver';
710  else
711  data.direction = 'hor';
712  end;
713  data.height = wid;
714  end;
715 
716  %> Asserts that there is no NaN in data.X
717  function data = assert_not_nan(data)
718  if any(isnan(data.X))
719  irerror('Dataset X property has NaNs!!!');
720  end;
721  if any(isnan(data.classes))
722  irerror('Dataset classes property has NaNs!!!');
723  end;
724  end;
725  end;
726 end
function irverbose(in s, in level)
function splitidxs2maps(in splitidxs)
function data_select_hierarchy(in data, in hierarchy)
Property classlabels
Cell of strings. Class labels.
Definition: irdata.m:60
function maximize_window(in h, in aspectratio, in normalizedsize)
function irerror(in s)
Dataset class.
Definition: irdata.m:30
Pre-processing block base class.
Definition: pre.m:2
Feature Extraction (Fext) base class.
Definition: fext.m:4
function boot(in o)
Configures the structure to deal with new type of data.
function cell2html(in cc, in flag_header, in flag_1stcolumn)
function data_split_classes(in data, in hierarchy)
Visualization - Class means.
Definition: vis_means.m:2
Select some given class levels.
Analysis Session (AS) base class.
Definition: as.m:6
function get_negative_meaning(in x)
Report base class.
Definition: irreport.m:8
Base class.
Definition: irobj.m:33
Property rowfieldnames
fields to be split or merged when dataset is split or merged
Definition: irdata.m:126
Property fea_x
feature x-axis
Definition: irdata.m:77
Property nf
number of features (i.e., variables)
Definition: irdata.m:39