Mercurial > octave
view scripts/statistics/cov.m @ 33571:742d8fc77688 default tip @
Support setting breakpoints in get and set methods of classdef properties (bug #65610).
* cdef-class.cc (cdef_class::cdef_class_rep::get_method): Also check for any
`get` or `set` methods of `classdef` properties.
* bp-table.cc (user_code_provider::operator ()): Support getting (closest) user
code to `get` or `set` methods of `classdef` classes.
(user_code_provider::populate_function_cache): Add `get` and `set` methods to
function cache for `classdef` classes.
* pt-eval.cc (tree_evaluator::get_user_code): Support getting user code for
`get` or `set` methods of `classdef` properties.
* test/classdef-debug/classdef_breakpoints2.m: Add handle class with get and
set methods for new self tests.
* test/classdef-debug/test-classdef-breakpoints.tst: Add new tests for adding
and clearing breakpoints in `set` and `get` methods of `classdef` properties by
line number or function name. Make sure breakpoints are deleted in existing
tests also on test failures. Fix syntax error in 69eb4c27d8c8.
* test/classdef-debug/module.mk: Add new file to build system.
* etc/NEWS.10.md: Add note about new feature.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Sat, 20 Apr 2024 13:13:50 +0200 |
parents | 2e484f9f1f18 |
children |
line wrap: on
line source
######################################################################## ## ## Copyright (C) 1995-2024 The Octave Project Developers ## ## See the file COPYRIGHT.md in the top-level directory of this ## distribution or <https://octave.org/copyright/>. ## ## This file is part of Octave. ## ## Octave is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## <https://www.gnu.org/licenses/>. ## ######################################################################## ## -*- texinfo -*- ## @deftypefn {} {@var{c} =} cov (@var{x}) ## @deftypefnx {} {@var{c} =} cov (@var{x}, @var{y}) ## @deftypefnx {} {@var{c} =} cov (@dots{}, @var{opt}) ## @deftypefnx {} {@var{c} =} cov (@dots{}, @var{nanflag}) ## Compute the covariance matrix. ## ## The covariance between two variable vectors @var{A} and @var{B} is ## calculated as: ## @tex ## $$ ## \sigma_{ij} = {1 \over N-1} \sum_{i=1}^N (a_i - \bar{a})(b_i - \bar{b}) ## $$ ## where $\bar{a}$ and $\bar{b}$ are the mean values of $a$ and $b$ and $N$ is ## the length of the vectors $a$ and $b$. ## @end tex ## @ifnottex ## ## @example ## cov (@var{a},@var{b}) = 1/(N-1) * SUM_i (@var{a}(i) - mean (@var{a})) * (@var{b}(i) - mean (@var{b})) ## @end example ## ## @noindent ## where @math{N} is the length of the vectors @var{a} and @var{b}. ## @end ifnottex ## ## If called with one argument, compute @code{cov (@var{x}, @var{x})}. If ## @var{x} is a vector, this is the scalar variance of @var{x}. If @var{x} is ## a matrix, each row of @var{x} is treated as an observation, and each column ## as a variable, and the @w{(@var{i}, @var{j})-th} entry of ## @code{cov (@var{x})} is the covariance between the @var{i}-th and ## @var{j}-th columns in @var{x}. If @var{x} has dimensions n x m, the output ## @var{c} will be a m x m square covariance matrix. ## ## If called with two arguments, compute @code{cov (@var{x}, @var{y})}, the ## covariance between two random variables @var{x} and @var{y}. @var{x} and ## @var{y} must have the same number of elements, and will be treated as ## vectors with the covariance computed as ## @code{cov (@var{x}(:), @var{y}(:))}. The output will be a 2 x 2 ## covariance matrix. ## ## The optional argument @var{opt} determines the type of normalization to ## use. Valid values are ## ## @table @asis ## @item 0 [default]: ## Normalize with @math{N-1}. This provides the best unbiased estimator of ## the covariance. ## ## @item 1: ## Normalize with @math{N}. This provides the second moment around the ## mean. @var{opt} is set to 1 for N = 1. ## @end table ## ## The optional argument @var{nanflag} must appear last in the argument list ## and controls how NaN values are handled by @code{cov}. The three valid ## values are: ## ## @table @asis ## @item includenan [default]: ## Leave NaN values in @var{x} and @var{y}. Output will follow the normal ## rules for handling NaN values in arithmetic operations. ## ## @item omitrows: ## Rows containing NaN values are trimmed from both @var{x} and @var{y} ## prior to calculating the covariance. A NaN in one variable will remove ## that row from both @var{x} and @var{y}. ## ## @item partialrows: ## Rows containing NaN values are ignored from both @var{x} and @var{y} ## independently for each @var{i}-th and @var{j}-th covariance ## calculation. This may result in a different number of observations, ## @math{N}, being used to calculated each element of the covariance matrix. ## @end table ## ## Compatibility Note: Previous versions of @code{cov} treated rows ## @var{x} and @var{y} as multivariate random variables. This version ## attempts to maintain full compatibility with @sc{matlab} by treating ## @var{x} and @var{y} as two univariate distributions regardless of shape, ## resulting in a 2x2 output matrix. Code relying on Octave's previous ## definition will need to be modified when running this newer version of ## @code{cov}. The previous behavior can be obtained by using the ## NaN package's @code{covm} function as @code{covm (@var{x}, @var{y}, "D")}. ## @seealso{corr} ## @end deftypefn function c = cov (x, varargin) if (nargin < 1 || nargin > 4) print_usage (); endif opt = 0; is_y = false; nanflag = "includenan"; if (! (isnumeric (x) || islogical (x))) error ("cov: X must be a numeric vector or matrix"); endif if (isrow (x)) x = x.'; endif nvarg = numel (varargin); if (nvarg > 0) switch (nvarg) case 3 ## Only char input should be nanflag, must be last. if (ischar (varargin{1}) || ischar (varargin {2})) if (ischar (varargin{3})) error ("cov: only one NANFLAG parameter may be specified"); else error ("cov: NANFLAG parameter must be the last input"); endif endif y = varargin{1}; opt = double (varargin{2}); # opt should not affect output class. nanflag = lower (varargin {3}); is_y = true; case 2 if (ischar (varargin{1})) error ("cov: NANFLAG parameter must be the last input"); endif if (ischar (varargin{end})) nanflag = lower (varargin{end}); if ((isnumeric (varargin{1}) || islogical (varargin{1})) && ... isscalar (varargin{1}) && ... (varargin{1} == 0 || varargin{1} == 1)) opt = double (varargin {1}); else y = varargin{1}; is_y = true; endif else y = varargin{1}; opt = double (varargin{2}); is_y = true; endif case 1 if (ischar (varargin{end})) nanflag = lower (varargin{end}); elseif ((isnumeric (varargin{1}) || islogical (varargin{1})) && ... isscalar (varargin{1}) && ... (varargin{1} == 0 || varargin{1} == 1)) opt = double (varargin {1}); else y = varargin{1}; is_y = true; endif endswitch if (is_y) if (! (isnumeric (y) || islogical (y))) error ("cov: Y must be a numeric vector or matrix"); elseif (numel (x) != numel (y)) error ("cov: X and Y must have the same number of observations"); else ## Flatten to single array. Process the same as two-column x. x = [x(:), y(:)]; endif endif if (! any (strcmp (nanflag, {"includenan", "omitrows", "partialrows"}))) error ("cov: unknown NANFLAG parameter '%s'", nanflag); endif if ((opt != 0 && opt != 1) || ! isscalar (opt)) error ("cov: normalization paramter OPT must be 0 or 1"); endif endif if (ndims (x) > 2) ## Note: Matlab requires 2D inputs even if providing a y input results in ## reshaping for operation as cov (x(:), y(:)) (tested in 2022b). ## Octave permits arbitrarily shaped inputs for the cov(x,y) case as ## long as numel (x) == numel (y). Only when no y is provided is X ## restricted to 2D for consistent 2D columnwise behavior of cov. error ("cov: X must be a 2-D matrix or vector"); endif ## Special case: empty inputs. Output shape changes depends on number of ## columns. Inputs already verified as limited to 2D. if (isempty (x)) sx = size (x); if (sx == [0, 0]) c = NaN; elseif (sx(1) > 0) c = []; else c = NaN (sx(2)); endif if (isa (x, "single")) c = single (c); endif return; endif if (! strcmp (nanflag, "includenan") && all (nnx = ! isnan (x))) ## Avoid unnecessary slower nanflag processing. nanflag = "includenan"; endif switch (nanflag) case {"includenan", "omitrows"} if (strcmp (nanflag, "omitrows")) ## Trim any rows in x containing a NaN. x = x(all (nnx, 2), :); endif n = rows (x); if (n < 2) ## Scalars and empties force opt = 1. opt = 1; endif x -= sum (x, 1) / n; c = x' * x / (n - 1 + opt); case "partialrows" ## Find all NaN locations for adjusted processing later. NaN's will ## only be trimmed for the columns containing them when calculating ## covariance involving that row. Each output element of c might have a ## unique n, opt, and mean for each of the two vectors used to calculate ## that element based on the paired column total number of non-NaN rows. ## Separate out simple vector case. Project into dim3 for general case. if (iscolumn (x)) x = x(nnx); n = numel (x); if (n < 2) opt = 1; endif x -= sum (x, 1) / n; ## Matrix multiplication preserves output size compatibliity if x is ## trimmed to empty. c = (x' * x) / (n - 1 + opt); else ## Number of elements in each column pairing. n = nnx' * nnx; ## opt for each column pairing opt = opt * ones (columns (x)); opt (n < 2) = 1; ## Mean for each column pairing. ## Rotate x vectors into dim3, project into dim1 with non-nan array x = permute(x, [3, 2, 1]) .* permute (nnx, [2, 3, 1]); mu = x; mu (isnan (x)) = 0; # Exclude input NaNs from summation. mu = sum (mu, 3) ./ n; # Vectors trimmed to n=0 may create more NaNs. x -= mu; # Center x's. x(isnan (x)) = 0; # Exclude input and mean NaNs from output. ## Sum dim3 elements of x products to emulate x'*x. c = sum (permute (x, [2,1,3]) .* x, 3) ./ (n - 1 + opt); endif endswitch endfunction %!test %! x = rand (10); %! cx1 = cov (x); %! cx2 = cov (x, x); %! assert (size (cx1) == [10, 10]); %! assert (size (cx2) == [2, 2]); %!test %! x = [1:3]'; %! y = [3:-1:1]'; %! assert (cov (x, x), [1 1; 1 1]); %! assert (cov (x, x), cov ([x, x])); %! assert (cov (x, y), [1 -1; -1 1]); %! assert (cov (x, y), cov ([x, y])); %!test %! x = [1:3]'; %! y = [3:-1:1]'; %! assert (cov (single (x)), single (1)); %! assert (cov (single (x), x), single ([1 1; 1 1])); %! assert (cov (x, single (x)), single ([1 1; 1 1])); %! assert (cov (single (x), single (x)), single ([1 1; 1 1])); %!test %! x = [1:8]; %! c = cov (x); %! assert (isscalar (c)); %! assert (c, 6); %!test <*64395> %! x = [1, 2, 3]; %! assert (cov (x), 1, eps); %! assert (cov (x'), 1, eps); %! assert (cov (x, x), ones (2), eps); %! assert (cov (x', x), ones (2), eps); %! assert (cov (x, x'), ones (2), eps); %! assert (cov (x', x'), ones (2), eps); %!test %! x = [1 0; 1 0]; %! y = [1 2; 1 1]; %! z = [1/3 -1/6; -1/6 0.25]; %! assert (cov (x, y), z, eps); %! assert (cov (x, y(:)), z, eps); %! assert (cov (x, y(:)'), z, eps); %! assert (cov (x', y(:)), z .* [1, -1; -1, 1], eps); %! assert (cov (x(:), y), z, eps); %! assert (cov (x(:)', y), z, eps); ## Test scalar inputs & class preservation %!assert (cov (5), 0) %!assert (cov (1, 0), 0) %!assert (cov (1, 1), 0) %!assert (cov (1, 0.1), [0, 0; 0, 0]) %!assert (cov (1, 1.1), [0, 0; 0, 0]) %!assert (cov (1, 3), [0, 0; 0, 0]) %!assert (cov (single (5)), single (0)) %!assert (cov (single (5), 0), single (0)) %!assert (cov (single (5), 1), single (0)) %!assert (cov (single (5), 1.1), single ([0, 0; 0, 0])) %!assert (cov (5, single (0)), double (0)) %!assert (cov (5, single (1)), double (0)) %!assert (cov (5, single (1.1)), single ([0, 0; 0, 0])) %!assert (cov (5, single (1.1), 0), single ([0, 0; 0, 0])) %!assert (cov (5, single (1.1), 1), single ([0, 0; 0, 0])) %!assert (cov (5, 1.1, single (0)), double([0, 0; 0, 0])) ## Test opt %!test %! x = [1:5]; %! c = cov (x, 0); %! assert (c, 2.5); %! c = cov (x, 1); %! assert (c, 2); %! c = cov (double (x), single (1)); %! assert (class (c), "double"); %!assert (cov (2, 4), zeros(2)) %!assert (cov (2, 4, 0), zeros(2)) %!assert (cov (2, 4, 1), zeros(2)) %!assert (cov (NaN, 4), [NaN, NaN; NaN, 0]) %!assert (cov (NaN, 4, 0), [NaN, NaN; NaN, 0]) %!assert (cov (NaN, 4, 1), [NaN, NaN; NaN, 0]) ## Test logical inputs %!assert (cov (logical(0), logical(0)), double(0)) %!assert (cov (0, logical(0)), double(0)) %!assert (cov (logical(0), 0), double(0)) %!assert (cov (logical([0 1; 1 0]), logical([0 1; 1 0])), double ([1 1;1 1]./3)) %!assert (cov ([1 2 3], [3 4 5], 0), [1 1; 1 1]) %!assert (cov ([1 2 3], [3 4 5], false), [1 1; 1 1]) %!assert (cov ([1 2 3], [3 4 5], 1), [2/3 2/3; 2/3 2/3], eps) %!assert (cov ([1 2 3], [3 4 5], true), [2/3 2/3; 2/3 2/3], eps) ## Test empty and NaN handling (bug #50583) %!assert <*50583> (cov ([]), NaN) %!assert <*50583> (cov (single ([])), single (NaN)) %!assert <*50583> (cov ([], []), NaN (2, 2)) %!assert <*50583> (cov (single ([]), single([])), single (NaN (2, 2))) %!assert <*50583> (cov ([], single ([])), single (NaN (2, 2))) %!assert <*50583> (cov (single ([]), []), single (NaN (2, 2))) %!assert <*50583> (cov (ones(1, 0)), NaN) %!assert <*50583> (cov (ones(2, 0)), []) %!assert <*50583> (cov (ones(0, 1)), NaN) %!assert <*50583> (cov (ones(0, 2)), NaN (2, 2)) %!assert <*50583> (cov (ones(0, 6)), NaN (6, 6)) %!assert <*50583> (cov (ones(2, 0), []), NaN (2, 2)) %!assert <*50583> (cov (ones(0,6), []), NaN (2, 2)) %!assert <*50583> (cov (NaN), NaN) %!assert <*50583> (cov (NaN, NaN), NaN (2, 2)) %!assert <*50583> (cov (5, NaN), [0, NaN; NaN, NaN]) %!assert <*50583> (cov (NaN, 5), [NaN, NaN; NaN, 0]) %!assert <*50583> (cov (single (NaN)), single (NaN)) %!assert <*50583> (cov (NaN (2, 2)), NaN (2, 2)) %!assert <*50583> (cov (single (NaN (2, 2))), single (NaN (2, 2))) %!assert <*50583> (cov (NaN(2, 9)), NaN(9, 9)) %!assert <*50583> (cov (NaN(9, 2)), NaN(2, 2)) %!assert <*50583> (cov ([NaN, 1, 2, NaN]), NaN) %!assert <*50583> (cov ([1, NaN, 2, NaN]), NaN) ## Test NaN and nanflag option (bug #50571) %!test <*50571> %! x = magic(3); %! y = magic(3); %! x(3) = NaN; %! assert (cov (y, "omitrows"), cov(y)); %! assert (cov (y, "partialrows"), cov(y)); %! assert (cov (x), [NaN, NaN, NaN; NaN, 16, -8; NaN, -8, 7]); %! assert (cov (x, "omitrows"), ... %! [12.5, -10, -2.5; -10, 8, 2; -2.5, 2, 0.5], eps); %! assert (cov (x, "partialrows"), %! [12.5, -10, -2.5; -10, 16, -8; -2.5, -8, 7], eps); %! assert (cov (x, x), NaN(2,2)); %! assert (cov (x, y), [NaN, NaN; NaN, 7.5], eps); %! assert (cov (y, x), [7.5, NaN; NaN, NaN], eps); %! assert (cov (x, x, 'omitrows'), (471/56) * ones(2), eps); %! assert (cov (x, x, 'partialrows'), (471/56) * ones(2), eps); %! assert (cov (x, y, 'omitrows'), (471/56) * ones(2), eps); %! assert (cov (x, y, 'partialrows'), (471/56)*[1,1;1,0] + [0,0;0,7.5], eps); %! assert (cov (y, x, 'omitrows'), (471/56) * ones(2), eps); %! assert (cov (y, x, 'partialrows'), (471/56)*[0,1;1,1] + [7.5,0;0,0], eps); %! assert (cov (x, y, 0, 'omitrows'), (471/56) * ones(2), eps); %! assert (cov (x, y, 0, 'partialrows'), (471/56)*[1,1;1,0] + [0,0;0,7.5], eps); %!assert (cov ([NaN NaN NaN]), NaN) %!assert <*50571> (cov ([NaN NaN NaN], "omitrows"), NaN) %!assert <*50571> (cov ([NaN NaN NaN], "partialrows"), NaN) %!assert (cov (NaN(3)), NaN(3)) %!assert <*50571> (cov (NaN(3), "omitrows"), NaN(3)) %!assert <*50571> (cov (NaN(3), "partialrows"), NaN(3)) %!assert (cov ([NaN NaN NaN],[1 2 3]), [NaN, NaN; NaN 1]) %!assert <*50571> (cov ([NaN NaN NaN],[1 2 3], "omitrows"), [NaN, NaN; NaN NaN]) %!assert <*50571> (cov ([NaN NaN NaN],[1 2 3], "partialrows"), [NaN, NaN; NaN 1]) %!test <*50571> %! x = magic(3); %! x(4:6) = NaN; %! assert (cov (x), [7 NaN 1; NaN NaN NaN; 1 NaN 7]); %! assert (cov (x, "omitrows"), NaN(3)); %! assert (cov (x, "partialrows"), [7 NaN 1; NaN NaN NaN; 1 NaN 7]); %!assert <*50571> (cov (5, NaN, "omitrows"), NaN(2, 2)) %!assert <*50571> (cov (NaN, 5, "omitrows"), NaN(2, 2)) %!assert <*50571> (cov (5, NaN, "partialrows"), [0, NaN; NaN, NaN]) %!assert <*50571> (cov (NaN, 5, "partialrows"), [NaN, NaN; NaN, 0]) %!test <*50571> %! x = [1:10]'; %! y = x; %! x(2:2:10)=NaN; %! y(1:2:9)=NaN; %! assert (cov(x,y), NaN(2)); %! assert (cov(x,y,'omitrows'), NaN(2)); %! assert (cov(x,y,'partialrows'), [10 NaN; NaN, 10]); %! x(10) = 5; %! assert (cov(x,y), NaN(2)); %! assert (cov(x,y,'omitrows'), zeros(2)); %! assert (cov(x,y,'partialrows'), [8 0; 0, 10]); #!assert (cov ([NaN, NaN, 3]), NaN) #!assert <*50571> (cov ([NaN, NaN, 3], 'omitrows'), 0) #!assert <*50571> (cov ([NaN, NaN, 3], 'partialrows'), 0) %!assert (cov ([NaN, NaN, NaN; NaN, 2, 3; NaN, NaN, 3]), NaN(3)) %!assert <*50571> (cov ([NaN, NaN, NaN; NaN, 2, 3; NaN, NaN, 3], 'omitrows'), NaN(3)) %!assert <*50571> (cov ([NaN, NaN, NaN; NaN, 2, 3; NaN, NaN, 3], 'partialrows'), [NaN, NaN, NaN; NaN(2,1), zeros(2)]) %!assert <*50571> (cov ([NaN, NaN, NaN; NaN, 2, 3; NaN, NaN, 3], 0, 'partialrows'), [NaN, NaN, NaN; NaN(2,1), zeros(2)]) %!assert <*50571> (cov ([NaN, NaN, NaN; NaN, 2, 3; NaN, NaN, 3], 1, 'partialrows'), [NaN, NaN, NaN; NaN(2,1), zeros(2)]) %!test <*50571> %! x = magic(4); %! x([4 5 7 10 14 16]) = NaN; %! x1 = x(:,2); %! x2 = x(:,3); %! assert (cov(x1,x2),nan(2)); %! assert (cov(x1,x2,'omitrows'),zeros(2)); %! assert (cov(x1,x2, 'partialrows'), [4.5,0;0,39],eps); %! assert (cov(x1,x2,0,'partialrows'), [4.5,0;0,39],eps); %! assert (cov(x1,x2,1,'partialrows'), [2.25,0;0,26],eps); %! assert (cov(x), nan(4)); %! assert (cov(x,'omitrows'), nan(4)); %! assert (cov(x,'partialrows'), [31 0 -10.5 3.5; 0 4.5 0 NaN; -10.5 0 39 -1.5; 3.5 NaN -1.5 0.5], eps); %! assert (cov(x, 0, 'partialrows'), [31 0 -10.5 3.5; 0 4.5 0 NaN; -10.5 0 39 -1.5; 3.5 NaN -1.5 0.5], eps); %! assert (cov(x, 1,'partialrows'), [62/3 0 -5.25 1.75; 0 2.25 0 NaN; -5.25 0 26 -0.75; 1.75 NaN -0.75 0.25], eps); ## Test input validation %!error <Invalid call> cov () %!error <Invalid call> cov (1, 2, 3, 4, 5) %!error <X must be a> cov ("foo") %!error <X must be a> cov ({123}) %!error <X must be a> cov (struct()) %!error <X must be a 2-D> cov (ones (2, 2, 2)) %!error <X must be a 2-D> cov (ones (1, 0, 2)) %!error <only one NANFLAG> cov (1, "foo", 0, "includenan") %!error <only one NANFLAG> cov (1, 1, "foo", "includenan") %!error <normalization paramter OPT must be> cov (1, 2, []) %!error <normalization paramter OPT must be> cov (1, 2, 1.1) %!error <normalization paramter OPT must be> cov (1, 2, -1) %!error <normalization paramter OPT must be> cov (1, 2, [0 1]) %!error <Y must be a> cov (1, {123}) %!error <Y must be a> cov (1, struct()) %!error <X and Y must have the same number> cov (5,[1 2]) %!error <X and Y must have the same number> cov (ones (2, 2), ones (2, 2, 2)) %!error <X and Y must have the same number> cov (ones (2, 2), ones (3, 2)) %!error <X and Y must have the same number> cov (1, []) %!error <X and Y must have the same number> cov ([1, 2], ones(1, 0, 2)) %!error <unknown NANFLAG parameter 'foo'> cov (1, "foo") %!error <unknown NANFLAG parameter 'foo'> cov (1, "foo") %!error <unknown NANFLAG parameter 'foo'> cov (1, 2, "foo") %!error <unknown NANFLAG parameter 'foo'> cov (1, 2, 0, "foo") %!error <unknown NANFLAG parameter ''> cov (1, 2, 1, []) %!error <NANFLAG parameter must be the last> cov (1, "includenan", 1) %!error <NANFLAG parameter must be the last> cov (1, 1, "includenan", 1)