Find mean of non-zero elements - matlab

I am assuming that the mean fucntion takes a matrix and calculate its mean by suming all element of the array, and divide it by the total number of element.
However, I am using this functionality to calculate the mean of my matrix. Then I come across a point where I don't want the mean function to consider the 0 elements of my matrix. Specifically, my matrix is 1x100000 array, and that maybe 1/3 to 1/2 of its element is all 0. If that is the case, can I replace the 0 element with NULL so that the matlab wouldn't consider them in calculating the mean? What else can I do?

Short version:
Use nonzeros:
mean( nonzeros(M) );
A longer answer:
If you are working with an array with 100K entries, with a significant amount of these entries are 0, you might consider working with sparse representation. It might also be worth considering storing it as a column vector, rather than a row vector.
sM = sparse(M(:)); %// sparse column
mean( nonzeros(sM) ); %// mean of only non-zeros
mean( sM ); %// mean including zeros

As you were asking "What else can I do?", here comes another approach, which does not depend on the statistics Toolbox or any other Toolbox.
You can compute them mean yourself by summing up the values and dividing by the number of nonzero elements (nnz()). Since summing up zeros does not affect the sum, this will give the desired result. For a 1-dimensional case, as you seem to have it, this can be done as follows:
% // 1 dimensional case
M = [1, 1, 0 4];
sum(M)/nnz(M) % // 6/3 = 2
For a 2-dimensional case (or n-dimensional case) you have to specify the dimension along which the summation should happen
% // 2-dimensional case (or n-dimensional)
M = [1, 1, 0, 4
2, 2, 4, 0
0, 0, 0, 1];
% // column means of nonzero elements
mean_col = sum(M, 1)./sum(M~=0, 1) % // [1.5, 1.5, 4, 2.5]
% // row means of nonzero elements
mean_row = sum(M, 2)./sum(M~=0, 2) % // [2; 2.667; 1.0]

To find the mean of only the non-zero elements, use logical indexing to extract the non-zero elements and then call mean on those:
mean(M(M~=0))

Related

Reshape a 3D matrix to 4D matrix in MATLAB [duplicate]

I want to reshape pixel intensity by imagesize*1(column vector).
Imvect = reshape(I,imsize,1);
But why these error comes?
Error using reshape
To RESHAPE the number of elements must not change.
Let's start with the syntax used in the documentation:
B = reshape(A,sz1,...,szN)
What reshape does is to take the matrix A, straightens it out, and gives it a new size, that's determined by the 2nd, 3rd to the Nth argument. For this to be possible, you need to have the same number of elements in the input matrix as you have in the output matrix. You can't make a 1x5 vector turn into a 2x3 vector, as one element would be missing. The number of elements in the output matrix will be proportional to the product of sz1, sz2, ..., szN. Now, if you know you want N rows, but don't know exactly how many columns you have, you might use the [] syntax, that tells MATLAB to use as many columns as necessary to make the number of elements be equal.
So reshape(A, 2, [], 3) will become a 2xNx3 matrix, where, for a matrix with 24 elements, N will be 4.
Now, in your case this is not the case. numel(I) ~= imsize. If mod(numel(I), imsize) ~= 0 then your imsize is definitely incorrect. However, if mod(numel(I), imsize) == 0, then your error might be that you want imsize number of rows, and a number of columns that makes this possible. If it's the latter, then this should work:
Imvect = reshape(I,imsize, []);
If you simply want to make you matrix I a vector of size (numel(I), 1), then you should use the colon operator :, as such:
Imvect = I(:);
An alternative, if you really want to use reshape, is to specify that you want a single column, and let MATLAB select the number of rows, as such:
Imvect = reshape(I, [], 1);

Column-wise average over unequal number of values in matrix

I am looking for an easy way to obtain the column-wise average of a subset of values in a matrix (indexed by a logical matrix), preferably without having to use a loop. The problem that I am experiencing is that because the number of values in each column is different, matlab collapses the values-matrix and its column-wise mean becomes the total mean (of the entire matrix). Is there a specific function or easy workaround for this problem? See below for an example.
%% define value matrix and logical indexing matrix
values=[1 2 3 4; 5 6 7 8; 9 10 11 12];
indices=[1 0 0 1; 0 1 0 1; 1 1 0 0];
indices=indices==1; %convert to logical
%% calculate column-wise average
mean(values(indices),1)
accumarray-based approach
Use the column index as a grouping variable for accumarray:
[~, col] = find(indices);
result = accumarray(col(:), values(indices), [size(values,2) 1], #mean, NaN).';
Note that:
The second line uses (:) to force the first input to be a column vector. This is needed because the first line may produce col as a row or column vector, depending on the size of indices.
NaN is used as fill value. This is specified as the fifth input to accumarray.
The third input to accumarray defines output size. It is necessary to specify it explicitly (as opposed to letting accumarray figure it out) in case the last columns of indices only contain false.
Hacky approach
Multiply and divide element-wise by indices. That will turn unwanted entries into NaN, which can then be ignored by mean using the 'omitnan' option:
result = mean(values.*indices./indices, 1, 'omitnan');
Manual approach
Multiply element-wise by indices, sum each column, and divide element-wise by the sum of each column of indices:
result = sum(values.*indices, 1) ./ sum(indices, 1);

Reduce three dimensional array to a vector of significant numbers

Hi I have a three dimensional array in Matlab, something like <10 x 10 x 100> and I would like to reduce this array to a vector of significant numbers. For example I would like to take each matrix(picture) split it in half by columns, compute sum(left)-sum(right) and return this <1 x 100> vector back. Unfortunately I cannot figure out or find out how to do that. Is it possible? And how could I achieve it?
Thanks a lot for any help.
Here's a one-liner, given a matrix A:
result = -squeeze(diff(sum(reshape(A, [50 2 100]), 1), 1, 2)).';
How it works:
First, reshape the data into a 50-by-2-by-100 matrix where values from the left half of each matrix are in column 1 and values from the right half of each matrix are in column 2. Then apply sum down each column to get a 1-by-2-by-100 matrix. You can then take the difference between the columns with diff, although this subtracts the left column from the right, so you have to add a minus to negate the result. The resulting 1-by-1-by-100 matrix can be collapsed to a 100-by-1 column vector with squeeze, and this can be transposed into a row vector. Alternatively, you can use another reshape instead of the squeeze and transpose:
result = -reshape(diff(sum(reshape(A, [50 2 100]), 1), 1, 2), [1 100]);

How to work around a matrix dimension error with funtion intersect?

I have a 1000x1000 matrix A (containing values ​​from 0 to 150) and a 181x1 vector B. In my matrix A, I want to keep only those values ​​that are present in B while keeping A with the same size.
I tried with the function ismember but it doesn't give me the result that I expect. So I tried an other function
Here is what I do as code
A=A.*intersect(A,B,'stable');
But I have this error
Error using .*
Matrix dimensions must agree.
How can I do solve the problem?
All you need is ismember for this task as follows:
A = A.*ismember(A,B);
% ismember(A,B) gives the logical matrix containing 1's for the indexes whose values
% are present in `B` and 0's for all other indexes. When this logical matrix is
% element-wise multiplied with A, all the indexes of A whose elements are not in B
% become zero
Why does your code not work?
That's because with intersect(A, B, 'stable'), you get a column vector containing (most probably) less than or (very less probably) equal to the number of elements of A. Even if equal, you will get the same error when you multiply it element-wise with A since A is not a column vector. Element-wise multiplication requires the order of both matrices to be same because only that's when each element of a matrix can be multiplied with the corresponding element in the other matrix.
The code that I showed above with ismember takes care of this as already explained in the comments.
Create two matrices, A and B, with random numbers. C is an array with values that are both in A and B, using ismember we can select which values in A to keep.
A = randi([0 150], 1000, 1000);
B = randi([0 150], 181, 1);
C = intersect(A, B, 'stable');
A(~ismember(A, C)) = 0;

Checking equality of row elements in Matlab?

I have a matrix A in Matlab of dimension mxn. I want to construct a vector B of dimension mx1 such that B(i)=1 if all elements of A(i,:) are equal and 0 otherwise. Any suggestion? E.g.
A=[1 2 3; 9 9 9; 2 2 2; 1 1 4]
B=[0;1;1;0]
One way with diff -
B = all(diff(A,[],2)==0,2)
Or With bsxfun -
B = all(bsxfun(#eq,A,A(:,1)),2)
Here's another example that's a bit more obfuscated, but also does the job:
B = sum(histc(A,unique(A),2) ~= 0, 2) == 1;
So how does this work? histc counts the frequency or occurrence of numbers in a dataset. What's cool about histc is that we can compute the frequency along a dimension independently, so what we can do is calculate the frequency of values along each row of the matrix A separately. The first parameter to histc is the matrix you want to compute the frequency of values of. The second parameter denotes the edges, or which values you are looking at in your matrix that you want to compute the frequencies of. We can specify all possible values by using unique on the entire matrix. The next parameter is the dimension we want to operate on, and I want to work along all of the columns so 2 is specified.
The result from histc will give us a M x N matrix where M is the total number of rows in our matrix A and N is the total number of unique values in A. Next, if a row contains all equal values, there should be only one value in this row where all of the values were binned at this location where the rest of the values are zero. As such, we determine which values in this matrix are non-zero and store this into a result matrix, then sum along the columns of the result matrix and see if each row has a sum of 1. If it does, then this row of A qualifies as having all of the same values.
Certainly not as efficient as Divakar's diff and bsxfun method, but an alternative since he took the two methods I would have used :P
Some more alternatives:
B = var(A,[],2)==0;
B = max(A,[],2)==min(A,[],2)