Scala Breeze expansion matrix - scala

In Octave/Matlab I can expand an identity matrix as follows.
>> I = eye(3)
I =
Diagonal Matrix
1 0 0
0 1 0
0 0 1
>> A = [ 3 2 3 2 2 1 3 2 2 1 ]
>> E = I(:, A)
E =
0 0 0 0 0 1 0 0 0 1
0 1 0 1 1 0 0 1 1 0
1 0 1 0 0 0 1 0 0 0
How can I achieve the same thing (i.e. obtain E from A, possibly using I) with Scala/Breeze ?

Got it. Actually very similar to Octave.
scala> val I = DenseMatrix.eye[Int](3)
I: breeze.linalg.DenseMatrix[Int] =
1 0 0
0 1 0
0 0 1
scala> val A = DenseMatrix(2, 1, 2, 1, 1, 0, 2, 1, 1, 0) // zero based in breeze
scala> I(::, A.toArray.toSeq)
res26: breeze.linalg.SliceMatrix[Int,Int,Int] =
0 0 0 0 0 1 0 0 0 1
0 1 0 1 1 0 0 1 1 0
1 0 1 0 0 0 1 0 0 0
The caveats are:
the matrices must contain Integers
indices are 0 based (as opposed to 1 based in Octave/Matlab)

Related

Filter islands based on length in a binary array - MATLAB

I have a binary array and I would like to flip values based on the length which they repeat. as an example
Ar = [0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
Ideally I would like to flip the 1's which repeat only 2 or fewer times resulting in the following.
Ar = [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
From what I have located online, the Diff function is most commenly used to locate and remove sequences. But from what I have located, it appears to target all instances.
Simply use imopen from Image Processing toolbox with a kernel of 3 ones -
imopen(Ar,[1,1,1])
Sample run -
>> Ar = [0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
>> out = imopen(Ar,[1,1,1]);
>> [Ar(:) out(:)]
ans =
0 0
1 0
0 0
0 0
0 0
1 0
1 0
0 0
0 0
0 0
1 1
1 1
1 1
1 1
1 1
0 0
0 0
0 0
0 0
1 1
1 1
1 1
1 1
1 1
1 1
Vectorized solution without using I.P. toolbox -
function out = filter_islands_on_length(Ar, n)
out = Ar;
a = [0 Ar 0];
d = diff(a);
r = find(d);
s0 = r(1:2:end);
s1 = r(2:2:end);
id_arr = zeros(1,numel(Ar));
m = (s1-s0) <= n;
id_arr(s0(m)) = 1;
id_arr(s1(m)) = -1;
out(cumsum(id_arr)~=0) = 0;
Sample runs -
>> Ar
Ar =
0 1 0 0 0 1 1 0 0 0 1 1 1
>> filter_islands_on_length(Ar, 2)
ans =
0 0 0 0 0 0 0 0 0 0 1 1 1
>> filter_islands_on_length(Ar, 1)
ans =
0 0 0 0 0 1 1 0 0 0 1 1 1
Another solution requiring no toolbox but needs Matlab 2016a or later:
n = 3; % islands shorter than n will be removed
Ar = movmax(movsum(Ar,n),[ceil(n/2-1) floor(n/2)])==n;

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

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

Transform vector with repeating values into a matrix in Matlab

I have a vector of n size, which I would like to transform into the Boolean matrix of nxm, where m is a number of unique values in that vector.
a = repmat(1:5:20,1,3)
a =
1 6 11 16 1 6 11 16 1 6 11 16
The result I would like to have is the matrix 12x4:
1 0 0 0
0 1 0 0
0 0 1 0
...
0 0 0 1
Any ideas how to do that without for loop?
You can try this:
a = repmat(1:5:20,1,3);
b = unique(a);
bsxfun(#eq, a', b)
The result would be:
ans =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
a = repmat(1:5:20,1,3)
b=unique(a);
c = repmat(a',1,numel(b))== repmat(b,numel(a),1);
but in general a loop will be faster, repmat is to be avoided.
So, now with a loop:
a = repmat(1:5:20,1,3)
b=unique(a);
c=false(numel(a),numel(b));
for ii=1:numel(a)
c(ii,:) = a(ii)==b;
end