How to account number of each column value in a matrix - matlab

I have Matrix A:
A= [ 1 1 0 2 2 2 2 0 0 3 3 0 0;
0 1 1 0 0 2 2 2 2 3 3 0 0 ];
and I want to make another matrix B, so that this matrix contains the number of value occurences of the original matrix A: ie each iith row of B contains how many times ii occurs in the corresponding columns of A. The number 0 may be ignored.
For example: in the 2nd column of A, only the number 1 occurs, to be specific two times --> hence B(1,2) = 2 and B(other,2) = 0.
For my example matrix A, the output should be
Res = [ 1 2 1 0 0 0 0 0 0 0 0 0 0;
0 0 0 1 1 2 2 1 1 0 0 0 0;
0 0 0 0 0 0 0 0 0 2 2 0 0 ];

This also is an opportunity for the (under-appreciated) accumarray
A= [ 1 1 0 2 2 2 2 0 0 3 3 0 0;
0 1 1 0 0 2 2 2 2 3 3 0 0 ];
N = size(A,2);
result = zeros(max(A(:)),N);
for ii=1:N
s = accumarray(nonzeros(A(:,ii)),1);
result(1:numel(s),ii) = s;
end
too bad only that accumarray can't do it all in one call :(
EDIT
Got it all in one accumarray call: :p
A= [ 1 1 0 2 2 2 2 0 0 3 3 0 0;
0 1 1 0 0 2 2 2 2 3 3 0 0 ];
N = size(A);
C = repmat(1:N(2),N(1),1);
result = accumarray([A(:)+1 C(:)], 1);
result = result(2:end,:)
EDIT2
If you have a 3 dimensional input matrix, it's easiest to first transform it into a 2 dimensional matrix and then process it using the above. The following code does this transform:
% example data:
A3d = repmat(A,[1 1 2])
A2d = reshape(permute(A3d,[1 3 2]),[],size(A3d,2))
Result:
A3d(:,:,1) =
1 1 0 2 2 2 2 0 0 3 3 0 0
0 1 1 0 0 2 2 2 2 3 3 0 0
A3d(:,:,2) =
1 1 0 2 2 2 2 0 0 3 3 0 0
0 1 1 0 0 2 2 2 2 3 3 0 0
A2d =
1 1 0 2 2 2 2 0 0 3 3 0 0
0 1 1 0 0 2 2 2 2 3 3 0 0
1 1 0 2 2 2 2 0 0 3 3 0 0
0 1 1 0 0 2 2 2 2 3 3 0 0

You can use
A= [ 1 1 0 2 2 2 2 0 0 3 3 0 0;
0 1 1 0 0 2 2 2 2 3 3 0 0 ];
cell2mat(arrayfun(#(b) sum(A == b),nonzeros(unique(A)), 'UniformOutput', false))
This results in
ans =
1 2 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 2 2 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 2 2 0 0
If, as asked in the comments, the matrix a has three dimensions, you need to sum the result along the third dimension:
sum(cell2mat(arrayfun(#(b) sum(A == b,1),nonzeros(unique(A)), 'UniformOutput', false)),3)

You could do this is one function call:
Res = histc(A, 1:3);
result as expected:
Res =
1 2 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 2 2 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 2 2 0 0

Another alternative using the SPARSE function (similar to #GuntherStruyf's second solution using ACCUMARRAY)
c = repmat(1:size(A,2), [size(A,1) 1]);
M = full(sparse(A(:)+1, c(:), 1));
M = M(2:end,:);
The result:
M =
1 2 1 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 2 2 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 2 2 0 0

Related

Convert adjacency matrix to specific edge list in MATLAB

If I have the matrix
1 0 0
0 0 1
0 0 0
and I want this form in MATLAB
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0
also I want the values of third row in result. i.e. ans= [1 0 0 0 0 0 0 1 0]
Here you go -
[X,Y] = ndgrid(1:size(A,1),1:size(A,2));
out = [X(:).' ; Y(:).' ; A(:).']
For the last part of your question, use the last row of out : out(end,:) or A(:).'.
Sample run -
>> A
A =
1 0 0
0 0 1
0 0 0
>> [X,Y] = ndgrid(1:size(A,1),1:size(A,2));
>> out = [X(:).' ; Y(:).' ; A(:).']
out =
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0

Linear span of a vector in MATLAB

I'm looking for a way to generate the spans of a given vector in MATLAB.
For example:
if a = [ 0 1 0 1] I need all vectors of the form [0 x 0 y], 1 <= x <= max1, 1 <= y <= max2,.
or if
a = [ 0 1 0 1 1 0] I need all vectors of the form [0 x 0 y z 0], 1 <= x <= max1, 1 <= y <= max2, 1<= z <= max3.
Note that the vector can have a variable number of 1's.
My first impression is that I would need a variable number of for loops, though I don't know if that is doable in MATLAB. Also any other ideas are welcome!
You don't need multiple for loops for this. The code below generates all required vectors as rows of a tall matrix. It actually creates the columns of the matrix one at a time. Each column will have numbers 1:m(i) arranged in the pattern where
each term repeats the number of times equal to the product of all m-numbers after m(i)
the whole pattern repeats the number of times equal to the product of all m-numbers before m(i)
This is what repmat(kron(1:m(i),ones(1,after)),1,before)' does. (Starting with R2015a you can use repelem to simplify this by replacing the kron command, but I don't have that release yet.)
a = [0 1 0 1 1 0];
m = [2 4 3]; // the numbers max1, max2, max3
A = zeros(prod(m), length(a));
i = 1; // runs through elements of m
for j=1:length(a) // runs through elements of a
if (a(j)>0)
before = prod(m(1:i-1));
after = prod(m(i+1:end));
A(:,j) = repmat(kron(1:m(i),ones(1,after)),1,before)';
i = i+1;
end
end
Output:
0 1 0 1 1 0
0 1 0 1 2 0
0 1 0 1 3 0
0 1 0 2 1 0
0 1 0 2 2 0
0 1 0 2 3 0
0 1 0 3 1 0
0 1 0 3 2 0
0 1 0 3 3 0
0 1 0 4 1 0
0 1 0 4 2 0
0 1 0 4 3 0
0 2 0 1 1 0
0 2 0 1 2 0
0 2 0 1 3 0
0 2 0 2 1 0
0 2 0 2 2 0
0 2 0 2 3 0
0 2 0 3 1 0
0 2 0 3 2 0
0 2 0 3 3 0
0 2 0 4 1 0
0 2 0 4 2 0
0 2 0 4 3 0

Grouping logical data in Matlab

I have a matrix that contains data of 0 & 1. I want to find groups of ones (not a specific size) in that matrix. Is it possible somehow?
Thanks in advance!
If you mean that you want to find all the "connected components in the matrix, say BW, simply use:
BW = logical([1 1 1 0 0 0 0 0
1 1 1 0 1 1 0 0
1 1 1 0 1 1 0 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 1 1 0
1 1 1 0 0 0 0 0]);
L = bwlabel(BW,4) %Result
This would yeild:
L =
1 1 1 0 0 0 0 0
1 1 1 0 2 2 0 0
1 1 1 0 2 2 0 0
1 1 1 0 0 0 3 0
1 1 1 0 0 0 3 0
1 1 1 0 0 0 3 0
1 1 1 0 0 3 3 0
1 1 1 0 0 0 0 0
Now if you want to find the size of various groups:
for ii=1:max(L(:))
length_vector(ii)=length(find(L==ii));
end
length_vector
This gives you:
length_vector =
24 4 5

How to count number of entries satisfying specific case?

A=[1 2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0;
0 1 2 3 4 5 0 0 0 0 0 0 0 0 0 0 0;
0 0 0 1 2 3 4 5 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5;
0 0 0 0 0 0 0 1 2 3 4 5 0 0 0 0 0;
0 0 0 0 0 0 0 1 2 3 4 5 0 0 0 0 0];
What is code to count number "1" in the column which only has elements which have value greater than 1 and less than 4.
note: zeros is ignored.
Thus, My expected output is res = 1 which is in second column only.
Here is what I tried:
res = sum( sum(A(2 :end,all(A>1&A<4))==1, 2),1 );
but zeros are still being counted in my code.
If I understand it correctly, you could do it this way:
Start by finding all columns that violate the first rule that elements of A my not be greater than 4
[~, del, ~] = find(A>=4)
Remove those columns:
A(:, unique(del)) = []
Which gives:
A =
1 2 3 0 0 0 0 0
0 1 2 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 1 2 3
0 0 0 2 3 0 0 0
0 0 0 2 3 0 0 0
Now we find all remaining columns that have a 1:
[~, c1, ~] = find(A == 1);
And all columns that have at least one value greater than 1 following the second requirement:
[~, c2, ~] = find(A > 1)
These vectors c1 and c2 with the column numbers we then simply intersect and count:
numel(intersect(c1, c2))
Now there are plenty of Matlab wizards on this forum, and my instinct tells me there is a better answer, so perhaps you should wait a bit.

Identify sequences of the same number in a matrix

I have matrix A
A= [0 0 2 2 2 2 0 0 1 1 1 0 3 3;
2 2 2 2 0 0 1 1 1 0 0 3 3 0;
As you can see, there are consecutive numbers in it; notice for example the 2 2 2 2 on the first and second row.
For each number occuring in this matrix (or at least for every number from 1 to the maximum number in my matrix) I want to have an output matrix that indicates sequences of this number and this number only in the original matrix.
So for example, for 1: there are three consecutive numbers on the first row and three on the second row: I want to indicate this in the first output matrix as follows:
Matrix 1 = [ 0 0 0 0 0 0 0 0 1 2 3 0 0 0;
0 0 0 0 0 0 0 1 2 3 0 0 0 0]
Same for number 2:
Matrix 2 = [ 0 0 1 2 3 4 0 0 0 0 0 0 0 0;
1 2 3 4 0 0 0 0 0 0 0 0 0 0]
and 3:
Matrix 3 = [ 0 0 0 0 0 0 0 0 0 0 0 0 1 2;
0 0 0 0 0 0 0 0 0 0 0 1 2 0]
As you can see, each output matrix shows counting forward for the consecutive occurrences of a number.
So in this case, I have 3 output matrices because matrix A has 3 as the biggest value there.
You can try this:
A= [0 0 2 2 2 2 0 0 1 1 1 0 3 3;
2 2 2 2 0 0 1 1 1 0 0 3 3 0];
result = arrayfun(#(b) (A == b).*cumsum((A == b),2),nonzeros(unique(A)), 'UniformOutput', false);
For this example, there will be 3 submatrices in the variable result.
result =
[2x14 double]
[2x14 double]
[2x14 double]
To access them, use the following syntax:
result{1}
result{2}
result{3}
Then you get:
ans =
0 0 0 0 0 0 0 0 1 2 3 0 0 0
0 0 0 0 0 0 1 2 3 0 0 0 0 0
ans =
0 0 1 2 3 4 0 0 0 0 0 0 0 0
1 2 3 4 0 0 0 0 0 0 0 0 0 0
ans =
0 0 0 0 0 0 0 0 0 0 0 0 1 2
0 0 0 0 0 0 0 0 0 0 0 1 2 0
~edit~
If, as asked in the comments, A is a 3D matrix, this code works just the same, but the structure of result is a bit different:
result =
[2x14x2 double]
[2x14x2 double]
[2x14x2 double]
To access these matrices, use for instance
result{1}(:,:,1) % for the results of comparing A(:,:,1) with value 1
result{1}(:,:,2) % for the results of comparing A(:,:,2) with value 1
Edited because the question changed
This is nowhere near to optimal but will do what you want
V = 1;
C = A' == V;
D = cumsum(C).*C
E = D'
now E will be Matrix1 in your example. Change V to 2 and 3 to obtain Matrix2 and Matrix3. If you have something like
A = [2 2 2 0 0 0 0 0 2 2 2]
then you will get
[1 2 3 0 0 0 0 0 4 5 6]
so it may not be what you want. It is not clear from your question if this is the case or not, but if not tell me and I will delete the answer
This is a loop-based solution to get you started:
A = [
0 0 2 2 2 2 0 0 1 1 1 0 3 3;
2 2 2 2 0 0 1 1 1 0 0 3 3 0
];
mx = max(A(:));
AA = cell(mx,1);
for num=1:mx
AA{num} = zeros(size(A));
for r=1:size(A,1)
idx = ( A(r,:) == num );
AA{num}(r,idx) = sum(idx):-1:1;
end
end
The result:
>> AA{1}
ans =
0 0 0 0 0 0 0 0 3 2 1 0 0 0
0 0 0 0 0 0 3 2 1 0 0 0 0 0
>> AA{2}
ans =
0 0 4 3 2 1 0 0 0 0 0 0 0 0
4 3 2 1 0 0 0 0 0 0 0 0 0 0
>> AA{3}
ans =
0 0 0 0 0 0 0 0 0 0 0 0 2 1
0 0 0 0 0 0 0 0 0 0 0 2 1 0
EDIT:
Updated code to work on matrix with three dimensions:
A = zeros(2,7,2);
A(:,:,1) = [2 2 2 0 0 1 1 ; 0 0 2 2 2 1 1];
A(:,:,2) = [1 1 2 2 2 0 0 ; 0 1 1 0 2 2 2];
mx = max(A(:));
AA = cell(mx,1);
for num=1:mx
AA{num} = zeros(size(A));
for p=1:size(A,3)
for r=1:size(A,1)
idx = ( A(r,:,p) == num );
AA{num}(r,idx,p) = 1:sum(idx);
end
end
end
The result:
%# contains consecutive numbers corresponding to number 1 in all slices
>> AA{1}
ans(:,:,1) =
0 0 0 0 0 1 2
0 0 0 0 0 1 2
ans(:,:,2) =
1 2 0 0 0 0 0
0 1 2 0 0 0 0
%# contains consecutive numbers corresponding to number 2 in all slices
>> AA{2}
ans(:,:,1) =
1 2 3 0 0 0 0
0 0 1 2 3 0 0
ans(:,:,2) =
0 0 1 2 3 0 0
0 0 0 0 1 2 3