How to make a general case of inserting ones in any type of matrix, in the non-principal diagonal - matlab

The title might be confusing, here's a particular example to explain myself. Also, I'm not sure how do you call the diagonal that starts in (1,2) and goes onward: (2,3) ; (3,4) and so on. Non-principal, non-main diagonal, not sure at all.
3x3 case
-1 1 0
-1 0 1
0 -1 1
4x4 case
-1 1 0 0
-1 0 1 0
-1 0 0 1
0 -1 1 0
0 -1 0 1
0 0 -1 1
So if the original matrix was a 4x4 (or any other size), I am able to make a matrix the size of the second example. I now have to insert the -1 and 1's in this fashion. This means n-1 number of -1's inserted if j=1, and then, a n-1 number of ones in the non-principal diagonal. When this is done, it's the same but for j=2 and the next non-principal diagonal, and so on.
Thing is, I'm thinking all the time about loops, and too many cases arise, because what I want is to be able to do this for any possible dimension, not for a particular case.
But then I saw this post Obtaining opposite diagonal of a matrix in Matlab
With this answer: A(s:s-1:end-1)
And it seems like a much cleaner way of doing it, since my own way (not finished since I'm not able to figure all the cases) has too many conditions. With a sentence like that, I could choose the diagonal, insert ones, and do it as many times as required, depending of the n dimension.
This leaves the problem of inserting the -1's, but I guess I could manage something.

It seems to mee that you want to obtain the following matrix B of size n × (n-1)*n/2
n = 4;
idx = fliplr(fullfact([n n]));
idx(diff(idx')<=0,:) = [];
m = size(idx,1);
B = zeros(m,n);
B(sub2ind(size(B),1:m,idx(:,1)')) = -1;
B(sub2ind(size(B),1:m,idx(:,2)')) = 1;

Approach #1
Here's a vectorized approach that has more memory requirements than a non-vectorized or for-loop based one. So, it could be tried out for small to medium sized datasizes.
The basic idea is this. For n=4 as an example, we take
-1 1 0 0
-1 0 1 0
-1 0 0 1
as the basic building block, replicate it n-1 i.e. 3 times and then remove the rows that aren't supposed to be part of the final output as per the requirements of the problem. Because of this very nature, this solution has more memory requirements, as we need to remove rows 6,8,9 for n = 4 case. But this gives us the opportunity to work with everything in one go.
N = n-1; %// minus 1 of the datasize, n
blksz = N*(N+1); %// number of elements in a (n-1)*n blocksize that is replicated
b1 = [-1*ones(N,1) eye(N)] %// Create that special starting (n-1)*n block
idx1 = find(b1~=0) %// find non zero elements for the starting block
idx2 = bsxfun(#plus,idx1,[0:N-1]*(blksz+N)) %// non zero elements for all blocks
b1nzr = repmat(b1(b1~=0),[1 N]) %// elements for all blocks
vald_ind = bsxfun(#le,idx2,[1:N]*blksz) %// positions of valid elements all blocks
mat1 = zeros(N,blksz) %// create an array for all blocks
mat1(idx2(vald_ind)) = b1nzr(vald_ind) %// put right elements into right places
%// reshape into a 3D array, join/concatenate along dim3
out = reshape(permute(reshape(mat1,N,N+1,[]),[1 3 2]),N*N,[])
%// remove rows that are not entertained according to the requirements of problem
out = out(any(out==1,2),:)
Approach #2
Here's a loop based code that could be easier to get a hold on if you have to explain it to yourself or just people and most importantly scales up pretty well on performance criteria across varying datasizes.
start_block = [-1*ones(n-1,1) eye(n-1)] %// Create that special starting (n-1)*n block
%// Find starting and ending row indices for each shifted block to be repeated
ends = cumsum([n-1:-1:1])
starts = [1 ends(1:end-1)+1]
out = zeros(sum(1:n-1),n) %// setup all zeros array to store output
for k1 = 1:n-1
%// Put elements from shifted portion of start_block for creating the output
out(starts(k1):ends(k1),k1:end) = start_block(1:n-k1,1:n-k1+1)
end
With n=4, the output -
out =
-1 1 0 0
-1 0 1 0
-1 0 0 1
0 -1 1 0
0 -1 0 1
0 0 -1 1

I don't know if I understood properly, but is this what you are looking for:
M=rand(5);
k=1; % this is to select the k-th diagonal
D=diag(ones(1,size(M,2)-abs(k)), k);
M(D==1)=-1;
M =
0.9834 -1.0000 0.8402 0.6310 0.0128
0.8963 0.1271 -1.0000 0.3164 0.6054
0.8657 0.6546 0.3788 -1.0000 0.5765
0.8010 0.8640 0.2682 0.4987 -1.0000
0.5550 0.2746 0.1529 0.7386 0.6550

Related

How to get indexes of logical matrix without using find in matlab?

Let's assume my matrix A is the output of comparison function i.e. logical matrix having values 0 and 1's only. For a small matrix of size 3*4, we might have something like:
A =
1 1 0 0
0 0 1 0
0 0 1 1
Now, I am generating another matrix B which is of the same size as A, but its rows are filled with indexes of A and any leftover values in each row are set to zero.
B =
1 2 0 0
3 0 0 0
3 4 0 0
Currently, I am using find function on each row of A to get matrix B. Complete code can be written as:
A=[1,1,0,0;0,0,1,0;0,0,1,1];
[rows,columns]=size(A);
B=zeros(rows,columns);
for i=1:rows
currRow=find(A(i,:));
B(i,1:length(currRow))=currRow;
end
For large martixes, "find" function is taking time in the calculation as per Matlab Profiler. Is there any way to generate matrix B faster?
Note:
Matrix A is having more than 1000 columns in each row but non-zero elements are never more than 50. Here, I am taking Matrix B as the same size as A but Matrix B can be of much smaller size column-wise.
I would suggest using parfor, but the overhead is too much here, and there are more issues with it, so it is not a good solution.
rows = 5e5;
cols = 1000;
A = rand(rows, cols) < 0.050;
I = uint16(1:cols);
B = zeros(size(A), 'uint16');
% [r,c] = find(A);
tic
for i=1:rows
% currRow = find(A(i,:));
currRow = I(A(i,:));
B(i,1:length(currRow)) = currRow;
end
toc
#Cris suggests replacing find with an indexing operation. It increases the performance by about 10%.
Apparently, there is not a better optimization unless B is required to be in that specific form you tell. I suggest using [r,c] = find(A); if the indexes are not required in a matrix form.

How to get the logical matrix corresponding to "scatter" plot?

If I have a two column matrix A like below, I can plot the scatter plot using scatter/plot command. I would like to get the matrix corresponding to such outputs as in hist command. hist command gives the vector output too.
A=[7 1;3 2; 4 3]
For example out=scatter(A(:,1),A(:,2)) must give something like below:
[0 0 0;
0 0 0;
0 1 0;
0 0 1;
0 0 0;
0 0 0;
1 0 0]
Only the indices (7,1), (3,2) and (4,3) are only ones. Or Can someone give me a snippet code to realize this without using loops?
You can use a combination of sparse and full where you can specify the non-zero row and column locations, and the rest of the matrix would be zero:
A = [7 1; 3 2; 4 3];
B = full(sparse(A(:,1), A(:,2), 1, max(A(:,1)), max(A(:,2)))) == 1;
The sparse command takes in the row and column locations of what is non-zero for the first two inputs, the third input is what the non-zero location would be for each row and column location. We can specify a constant to mean that every non-zero location gets the same coefficient, which is 1. We can also specify the size of the matrix, where in this case the rows and columns of the output correspond to the largest number in the first and second columns respectively. Because this is a sparse matrix, you will want to convert this to a full matrix and because you want it to be logical, you will want to compare all elements with the number 1.
We thus get for the output, which is B:
B =
7×3 logical array
0 0 0
0 0 0
0 1 0
0 0 1
0 0 0
0 0 0
1 0 0
Alternatively, we can use sub2ind to create linear indices to index into a pre-allocated matrix of logical false and set only those non-zero row locations to true:
A = [7 1; 3 2; 4 3];
B = false(max(A(:,1)), max(A(:,2)));
ind = sub2ind(size(B), A(:,1), A(:,2));
B(ind) = true;
We first allocate the matrix, then calculate the linear indices to index into the matrix, then finally set the right locations to true. The output here would be the same as the sparse approach.
Just to add: rayryeng's solution is fine if you really want your result to be logical in the sense that it is equal to one if there is anything at the coordinate and zero otherwise. Still, since you added a note on hist, I was wondering if you actually want to count the number of times a specific coordinate is hit. In this case, consider using
S = histcounts2(A(:,2),A(:,1));
if you have access to R2015b+. If not, there is a hist2 function on fileexchange you can use for the purpose.
Here is my solution. Matlab provides a command called accumarray.
S = logical(accumarray(A, 1) )
will give the result too.

MATLAB manual error?

I read interesting article about correct memory usage in MATLAB. Here it is: Link at official website
And here I see example:
If your data contains many zeros, consider using sparse arrays, which
store only nonzero elements. The following example compares the space
required for storage of an array of mainly zeros:
A = diag(1e3,1e3); % Full matrix with ones on the diagonal
As = sparse(A) % Sparse matrix with only nonzero elements
I tried to implement it in my code and find interesting moment:
A = diag(1e3,1e3) does not create matrix with ones on the diagonal! It creates matrix of zeros with only one nonzero element:
clear A
A = diag(1e3,1e3);
find(A);
ans =
1001001
A(1001001)
ans =
1000
Ok. I read about diag function in help and see this:
D = diag(v) returns a square diagonal matrix with the elements of
vector v on the main diagonal.
Ok! So it really doesn't create diagonal matrix if v consist of 1 element! Is it mistake at help?
BUT. One more question: why it works this way?
diag(5,5)
ans =
0 0 0 0 0 5
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
I expect to get matrix 5x5 with 5 value at (1,1) or (5,5). Why it creates 6x6 matrix and why 5 is a (1,6) element?
Some time ago they fix documentation:
Manual: diag
you are using the 2nd overloaded version of diag:
D = diag(v,k) places the elements of vector v on the kth diagonal. k=0 represents the main diagonal, k>0 is above the main diagonal, and k<0 is below the main diagonal.
So your command A = diag(5,5) will construct a matrix where the diagonal elements of 5th diagonal above the main diagonal will be equal to the vector [5]. Thus the resulting value where only A(1,6) has a value.
If you want to have a 1e3x1e3 Matrix with ones on the diagonal try
A = diag(ones(1,1e3));
The article is incorrect.
A = diag(1e3,1e3);
does not produce a matrix with ones on the diagonal. The code should instead read:
A = eye(1e3,1e3);
Now reading your question again, I understood it really and have to rewrite my answer. You are refering to this part of the documentation:
A = diag(1e3,1e3); % Full matrix with ones on the diagonal
As = sparse(A) % Sparse matrix with only nonzero elements
whos
Name Size Bytes Class
A 1001x1001 8016008 double array
As 1001x1001 4020 double array (sparse)
That example is definitely wrong, probably it should be:
A=eye(1e3,1e3)
As=sparse(A);
Which creates a 1000x1000 matrix with ones on the main diagonal.
The bug is reported to mathworks

Adding additional ones that surround other values of one in a vector in MATLAB

Given a vector of zeros and ones in MATLAB, where the zeros represent an event in time, I would like to add additional ones before and after the existing ones in order to capture additional variation.
Example: I would like to turn [0;0;1;0;0] into [0;1*;1;1*;0] where 1* are newly added ones.
Assuming A to be the input column vector -
%// Find all neighbouring indices with a window of [-1 1]
%// around the positions/indices of the existing ones
neigh_idx = bsxfun(#plus,find(A),[-1 1])
%// Select the valid indices and set them in A to be ones as well
A(neigh_idx(neigh_idx>=1 & neigh_idx<=numel(A))) = 1
Or use imdilate from Image Processing Toolbox with a vector kernel of ones of length 3 -
A = imdilate(A,[1;1;1])
You can do it convolving with [1 1 1], and setting to 1 all values greater than 0. This works for column or row vactors.
x = [0;0;1;0;0];
y = double(conv(x, [1 1 1],'same')>0)
Purely by logical indexing:
>> A = [0 1 1 0 0];
>> A([A(2:end) 0] == 1 | [0 A(1:end-1)] == 1) = 1;
>> disp(A);
A =
1 1 1 1 0
This probably merits an explanation. The fact that it's a 3 element local neighbourhood makes this easy. Essentially, take two portions of the input array:
Portion #1: A starting from the second element to the last element
Portion #2: A starting from the first element to the second-last element
We place the first portion into a new array and add 0 at the end of this array, and check to see which locations are equal to 1 in this new array. This essentially shifts the array A over to the left by 1. Whichever locations in this first portion are equal to 1, we set the corresponding locations in A to be 1. The same thing for the second portion where we are effectively shifting the array A over to the right by 1. To shift to the right by 1, we prepend a 0 at the beginning, then extract out the second portion of the array. Whichever locations in this second portion are equal to 1 are also set to 1.
At the end of this operation, you would essentially shift A to the left by 1 and save this as a separate array. Also, you would shift to the right by 1 and save this as another array. With these two, you simply overlap on top of the original to obtain the final result.
The benefit of this method over its predecessors in this post is that this doesn't require computations of any kind (bsxfun, conv, imdilate etc.) and purely relies on indexing into arrays and using logical operators1. This also handles boundary conditions and can work on either row or column vectors.
Some more examples with boundary cases
>> A = [0 0 1 1 0];
>> A([A(2:end) 0] == 1 | [0 A(1:end-1)] == 1) = 1
A =
0 1 1 1 1
>> A = [0 0 0 0 1];
>> A([A(2:end) 0] == 1 | [0 A(1:end-1)] == 1) = 1
A =
0 0 0 1 1
>> A = [1 0 1 0 1];
>> A([A(2:end) 0] == 1 | [0 A(1:end-1)] == 1) = 1
A =
1 1 1 1 1
1: This post is dedicated to Troy Haskin, one who believes that almost any question (including this one) can be answered by logical indexing.

Matlab:Efficient assignment of values in a sparse matrix

I'm working in Matlab and I have the next problem:
I have a B matrix of nx2 elements, which contains indexes for the assignment of a big sparse matrix A (almost 500,000x80,000). For each row of B, the first column is the column index of A that has to contain a 1, and the second column is the column index of A that has to contain -1.
For example:
B= 1 3
2 5
1 5
4 1
5 2
For this B matrix, The Corresponding A matrix has to be like this:
A= 1 0 -1 0 0
0 1 0 0 -1
1 0 0 0 -1
-1 0 0 1 0
0 -1 0 0 1
So, for the row i of B, the corresponding row i of A must be full of zeros except on A(i,B(i,1))=1 and A(i,B(i,2))=-1
This is very easy with a for loop over all the rows of B, but it's extremely slow. I also tried the next formulation:
A(:,B(:,1))=1
A(:,B(:,2))=-1
But matlab gave me an "Out of Memory Error". If anybody knows a more efficient way to achieve this, please let me know.
Thanks in advance!
You can use the sparse function:
m = size(B,1); %// number of rows of A. Or choose larger if needed
n = max(B(:)); %// number of columns of A. Or choose larger if needed
s = size(B,1);
A = sparse(1:s, B(:,1), 1, m, n) + sparse(1:s, B(:,2), -1, m, n);
I think you should be able to do this using the sub2ind function. This function converts matrix subscripts to linear indices. You should be able to do it like so:
pind = sub2ind(size(A),1:n,B(:,1)); % positive indices
nind = sub2ind(size(A),1:n,B(:,2)); % negative indices
A(pind) = 1;
A(nind) = -1;
EDIT: I (wrongly, I think) assumed the sparse matrix A already existed. If it doesn't exist, then this method wouldn't be the best option.