Creating big size diagonal matrix - matlab

I have a 512x512 random matrix which I want to put elements of it on the main diagonal of another matrix which elements are all zero so that my diagonal matrix would be 262144x262144.Of course it gives me low memory error.I also tried sparse function but it also does not work.Can anyone help me with this?
here is the code:
A=randn(512,512);
A=A(:);
Z=sparse(diag(A));

Not an expert on sparse matrices, but looking at the docs, I believe you can do something like this -
sparse(1:262144,1:262144,A(:))
Sample run -
>> A
A =
1 7 4
8 7 6
9 7 2
>> n = 9
n =
9
>> sparse(1:n,1:n,A(:))
ans =
(1,1) 1
(2,2) 8
(3,3) 9
(4,4) 7
(5,5) 7
(6,6) 7
(7,7) 4
(8,8) 6
(9,9) 2
>> full(sparse(1:n,1:n,A(:)))
ans =
1 0 0 0 0 0 0 0 0
0 8 0 0 0 0 0 0 0
0 0 9 0 0 0 0 0 0
0 0 0 7 0 0 0 0 0
0 0 0 0 7 0 0 0 0
0 0 0 0 0 7 0 0 0
0 0 0 0 0 0 4 0 0
0 0 0 0 0 0 0 6 0
0 0 0 0 0 0 0 0 2

Related

matlab encoding integers to matrix of vectors of 0's and 1's [duplicate]

This question already has answers here:
Creating Indicator Matrix
(6 answers)
Closed 8 years ago.
I have a 1118x1 vector of values from 0 to 10 as such:
5
5
3
4
7
4
1
.
.
I need to encode each value into a 11x1118 Matrix of zeros where the k+1th values is a 1.
For example the first value is a 5 so the 5+1=6 value in the first column with be 1
0
0
0
0
0
1
0
0
0
0
0
I need to do this for all values up to 1118.
I assume I just need a for loop but am completely lost as to how to do it
You can use for example sub2ind. Try the following code:
x = [4;3;1;1;4;7];
y = zeros(11,numel(x));
y(sub2ind(size(y),x+1,(1:numel(x))')) = 1
y =
0 0 0 0 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 1 0 0 0 0
1 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Or a direct approach using loop:
v = [5 5 3 4 7 4 1...]; # your vector
M = zeros(11, length(v)); # the final matrix
for i = 1:length(v)
e = v(i);
M(e + 1, i) = 1;
end

Calculating a partial cumulative sum for a square matrix

Let's say I have a square matrix M:
M = [0 0 0 0 0 1 9; 0 0 0 0 0 4 4; 0 0 1 1 6 1 1; 0 1 2 9 2 1 0; 2 1 8 3 2 0 0; 0 8 1 1 0 0 0; 14 2 0 1 0 0 0]
0 0 0 0 0 1 9
0 0 0 0 0 4 4
0 0 1 1 6 1 1
M = 0 1 2 9 2 1 0
2 1 8 3 2 0 0
0 8 1 1 0 0 0
14 2 0 1 0 0 0
Now I'd like to calculate two different cumulative sums: One that goes from the top of each column to the element of the column, that is a diagonal element of the matrix, and one that goes from the bottom of the column to the same diagonal element.
The resulting matrix M'should therefore be the following:
0 0 0 0 0 1 9
0 0 0 0 0 4 5
0 0 1 1 6 2 1
M' = 0 1 3 9 4 1 0
2 2 8 5 2 0 0
2 8 1 2 0 0 0
14 2 0 1 0 0 0
I hope the explanation of what I'm trying to achieve is comprehensible enough. Since my matrices are much larger than the one in this example, the calculation should be efficient as well...but so far I couldn't even figure out how to calculate it "inefficiently".
In one line using some flipping and the upper triangular function triu:
Mp = fliplr(triu(fliplr(cumsum(M)),1)) ...
+flipud(triu(cumsum(flipud(M)),1)) ...
+flipud(diag(diag(flipud(M))));
The following will do the job:
Mnew = fliplr(triu(cumsum(triu(fliplr(M)),1))) + flipud(triu(cumsum(triu(flipud(M)),1)));
Mnew = Mnew - fliplr(diag(diag(fliplr(Mnew)))) + fliplr(diag(diag(fliplr(M))));
But is it the fastest method?
I think logical indexing might get you there faster

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

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

Please help me to test my hypothesis with the neighbor elements in the matrix

I could not figure out the last part of my research so if anyone could help me I would be really appreciated for the help.. :)
Say that my original matrix is,
X =
0 0 0 0 0
0 0 12 9 0
0 4 9 15 0
0 11 19 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
and after finding the average of the non-zeros I will get something like below:
new_x =
0 0 0 0 0
0 0 **9.0000** 9.0000 0
0 4.0000 9.0000 **9.0000** 0
0 **8.3333** **8.0000** 0 0
0 2.0000 4.0000 8.0000 0
0 4.0000 5.0000 8.0000 0
0 0 0 0 0
Note that any elements that are greater than 10 are the 'center' and we want to find the average of the non-zeros with the radius of say 1 m. where 1 meter = 1 element away from the center.
** ** means the center.
For this part I have used the following (from gnovice):
X=[0 0 0 0 0; 0 0 12 9 0; 0 4 9 15 0; 0 11 19 0 0;
0 2 4 8 0; 0 4 5 8 0; 0 0 0 0 0];
kernel=[0 1 0; 1 0 1; 0 1 0];
sumx=conv2(X,kernel,'same');
nx=conv2(double(X>0),kernel,'same');
index=(X>10);
new_x=X;
new_x(index)=sumx(index)./max(nx(index),1);
So my question is that I want to compare the neighbor elements with its center whether they are equal, lesser, or greater. If it is greater or equal then '1' or else '0'.Also whatever elements that are outside the radius can be ignored and replaced with '0'.
For example, the 9 in the middle is within the radius of 12, 15, and 19 centers, so take the minimum center of those `min[9.000, 9.000, 8.000] = 8.000.
In this case 4 will not take into the consideration as it is not called the 'center' as well as [ 8 4 5 and 8 ] in the last two rows.
So I want something like below:
Test_x =
0 0 0 0 0
0 0 1 1 0
0 0 1 1 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
I have put this first part in the forum before and I am really appreciated for every suggestion earlier.
Please give me some ideas to start with. I have tried using a loop but it didnt seem to work very well. Any MATLAB function that can do the job for me..
Thank you so much for the help.
Beginner at MATLAB
I think I found the solution for this question by using Jonas techniques. Thank you for the help Jonas and gnovie:)
X=[0 0 0 0 0; 0 0 12 9 0; 0 4 9 15 0; 0 11 19 0 0; 0 2 4 8 0; 0 4 5 8 0; 0 0 0 0 0];
kernel=[0 1 0; 1 0 1; 0 1 0];
sumx=conv2(X,kernel,'same');
nx=conv2(double(X>0),kernel,'same');
avg_x=X;
avg_x(avg_x<10)=0;
index=(avg_x>10);
avg_x(index)=sumx(index)./max(nx(index),1);
avg_x =
0 0 0 0 0
0 0 9.0000 0 0
0 0 0 9.0000 0
0 8.3333 8.0000 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
tmp_x=avg_x;
maxVal=max(avg_x(:))+1;
tmp_x(tmp_x==0)=maxVal;
tmp_x=imerode(tmp_x,kernel);
Test_x=X>=tmp_x;
I think what you want to do is create a new array based on new_x that replaces every element by the minimum of its 4-connected neighbours. Then you can compare the new array to new_x.
Here's a way to do this (requires the image processing toolbox)
tmp_x = new_x;
maxVal = max(new_x(:))+1;
tmp_x(tmp_x == 0) = maxVal; %# replace all the zeros in tmp_x with something large
tmp_x = imerode(tmp_x,kernel); %# kernel is the same as in the OP
Test_x = new_x >= tmp_x; %# put ones wherever the value is
%# greater or equal the neighbour's minimum
%# only keep 1's that are next to 'centers'
Test_x = Test_x .* imdilate(X>10,strel('disk',1))
Test_x =
0 0 0 0 0
0 0 1 1 0
0 0 1 1 0
0 1 1 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
Note that I get one more ones with this logic than you do.