Identify sequences of the same number in a matrix - matlab

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

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

matlab: how to create matrix by shift vector

I have row and column in matlab:
a = [1 0 3 ... a_k]; - row 1xk
b = [1;0;3; ... b_k]; - column kx1
I want to create new matrix's (A and B) mxn, that can be populate by shift row and column:
A = [1 0 3 0 0 0 0 ... 0;
0 1 0 3 0 0 0 ... 0;
0 0 1 0 3 0 0 ... 0;
...
0 0 0 0 0 0 ... 1 0 3 ]
B= [1 0 0 0 0 0 0 ... 0;
0 1 0 0 0 0 0 ... 0;
3 0 1 0 0 0 0 ... 0;
0 3 0 1 0 0 0 ... 0;
0 0 3 0 1 0 0 ... 0;
...
0 0 0 0 0 0 0 ... 3]
How can I do it?
Is this what you want?
>> a = [1 0 3];
>> m = 5; %// number of rows
>> A = convmtx(a,m)
A =
1 0 3 0 0 0 0
0 1 0 3 0 0 0
0 0 1 0 3 0 0
0 0 0 1 0 3 0
0 0 0 0 1 0 3
>> b = [1;0;3];
>> m = 4; %// number of columns
>> B = convmtx(b,m)
B =
1 0 0 0
0 1 0 0
3 0 1 0
0 3 0 1
0 0 3 0
0 0 0 3
You can do this in a slightly tricky way by using a combination of indexing and bsxfun. First we want to create an index matrix that represents the shift that we're trying to. It should look like this (at least for A):
1 2 3 4 ... k
k 1 2 3 ... k-1
etc
To create this, we can use bsxfun as follows:
index = mod(bsxfun(#plus,1:k,-(1:(k-2))'),k)+1;
We can then create the matrix A by using this as an index matrix for a:
A = a(index);
The matrix B is the same, just transposed:
B = b(index)';

what do the commas in (cl,:,k) in MATLAB do, when building a matrix

I know how to build a matrix within MATLAB but the example I am working on has is defined as
a(cl,:,k)=x*ang;
cl, k, x and ang are already defined. I just wondered what the (cl,:,k) does, in particular the role of the commas?
Also, if I were to replicate this within Excel then how would I do so?
The comma , in a(cl,:,k) is to separate different dimensions of the matrix a.
The colon : in a(cl,:,k) is to select all elements along this dimension (restricted by other dimensions), which is shorthand notation for 1:end. In other words, all elements a(cl, 1:end, k) are selected, where end is the size of the second dimension of a.
For example:
a = zeros(2, 3); // 2x3 matrix with all elements are 0
a(1, :) = [1 2 3]; // <=> a(1,1:3)=[1 2 3]; assign all elements to the first row
then, a will be
1 2 3
0 0 0
The commas separate the indices along different axes of the elements of the multi dimensional array you want to access
: means 1:end - here end will become the largest index possible along that axis
>> a = zeros(3,3,3)
a(:,:,1) =
0 0 0
0 0 0
0 0 0
a(:,:,2) =
0 0 0
0 0 0
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(1,:,1) = 1
a(:,:,1) =
1 1 1
0 0 0
0 0 0
a(:,:,2) =
0 0 0
0 0 0
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(2,1:end,2) = 2
a(:,:,1) =
1 1 1
0 0 0
0 0 0
a(:,:,2) =
0 0 0
2 2 2
0 0 0
a(:,:,3) =
0 0 0
0 0 0
0 0 0
>> a(1,1,:) = 5
a(:,:,1) =
5 1 1
0 0 0
0 0 0
a(:,:,2) =
5 0 0
2 2 2
0 0 0
a(:,:,3) =
5 0 0
0 0 0
0 0 0

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.

How to account number of each column value in a matrix

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