Why doesn't `spdiags` put a vector in the correct place? - matlab

I have a vector that has 1's in some places, and I want to create a diagonal using the vector. The vector is called one_vec_two:
n = 4;
one_vec_two = zeros(n*n, 1);
one_vec_two(1,1) = 1;
for k=0:(n-1)
one_vec_two(k*n+1, 1) = 1;
end
non_zero_vecs = [one_vec_two];
placement = [n-1];
A = spdiags(non_zero_vecs, placement, n*n, n*n);
fullA = full(A);
disp(A)
The first element of the vector one_vec_two is a 1:
>> one_vec_two(1)
ans =
1
And, I placed the vector starting at diagonal n-1, which is 3. But, when I go to column 4, I don't see it:
>> fullA(1,4)
ans =
0
Why isn't MATLAB putting my vector in the correct spot?

As per the doc for spdiag,
Note In this syntax, if a column of B is longer than the diagonal it is replacing, and m >= n, spdiags takes elements of super-diagonals from the lower part of the column of B, and elements of sub-diagonals from the upper part of the column of B.
it is placing the lower part of your vector into the location specified. Hence the result is as expected.
It looks like you want something like
A = spdiags(non_zero_vecs([end-placement+1:end 1:end-placement]), placement, n*n, n*n)
or
A = spdiags(non_zero_vecs, -placement, n*n, n*n)'
which both do the same thing, just in slightly different ways.

Related

Comparing matrices of different size in matlab and storing values that are close

I have two matrices A and B. A(:,1) corresponds to an x-coordinate, A(:,2) corresponds to a y-coordinate, and A(:,3) corresponds to a certain radius. All three values in a row describe the same circle. Now let's say...
A =
[1,4,3]
[8,8,7]
[3,6,3]
B =
[1,3,3]
[1, 92,3]
[4,57,8]
[5,62,1]
[3,4,6]
[9,8,7]
What I need is to be able to loop through matrix A and determine if there are any rows in matrix B that are "similar" as in the x value is within a range (-2,2) of the x value of A (Likewise with the y-coordinate and radius).If it satisfies all three of these conditions, it will be added to a new matrix with the values that were in A. So for example I would need the above data to return...
ans =
[1,4,3]
[8,8,7]
Please help and thank you in advance to anyone willing to take the time!
You can use ismembertol.
result = A(ismembertol(A,B,2,'ByRows',1,'DataScale',1),:)
Manual method
A = [1,4,3;
8,8,7;
3,6,3];
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]; % example matrices
t = 2; % desired threshold
m = any(all(abs(bsxfun(#minus, A, permute(B, [3 2 1])))<=t, 2), 3);
result = A(m,:);
The key is using permute to move the first dimension of B to the third dimension. Then bsxfun computes the element-wise differences for all pairs of rows in the original matrices. A row of A should be selected if all the absolute differences with respect to any column of B are less than the desired threshold t. The resulting variable m is a logical index which is used for selecting those rows.
Using pdist2 (Statistics and Machine Learning Toolbox)
m = any(pdist2(A, B, 'chebychev')<=t, 2);
result = A(m,:);
Ths pdist2 function with the chebychev option computes the maximum coordinate difference (Chebychev distance, or Lāˆž metric) between pairs of rows.
With for loop
It should work:
A = [1,4,3;
8,8,7;
3,6,3]
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]
index = 1;
for i = 1:size(A,1)
C = abs(B - A(i,:));
if any(max(C,[],2)<=2)
out(index,:) = A(i,:);
index = index + 1
end
end
For each row of A, computes the absolute difference between B and that row, then checks if there exists a row in which the maximum is less than 2.
Without for loop
ind = any(max(abs(B - permute(A,[3 2 1])),[],2)<=2);
out = A(ind(:),:);

How can I properly loop through a simple 2D matrix?

I don't understand why I can't find any clear answer on this! Such a basic thing...
I have a normal 2D matrix, such as this one:
matrix = zeros(w, h);
Where w, h āˆˆ Z+, which means w and h are positive integers greater than zero.
I need to loop through the matrix and do something with it's element. For this task, we can assume h=3. Therefore, I tried this:
test = zeros(w, 3);
for i = 1:test
point = test(:,i);
[point(0), point(1), point(2)] = MY_CONVERSION(point(0), point(1),point(2));
test(:,i) = point;
end
The MY_CONVERSION is varargin function and for 3 parameters defined as:
function [rho, alpha, z] = MY_CONVERSION(r, alpha, epsilon)
Of course, in real code the test contains data, not just zeros. But this code alone could, for example, fill matrix with random numbers. But it doesn't work.
I also tried using three variables to store the column values:
for i = 1:Xi
[a,b,c] = Xi(:,i);
[a,b,c] = mysph2cyl(a, b, c);
rho(:,i) = a,b,c;
end
Matlab marks it as incorrect syntax:
An assignment statement makes an assignment to multiple values, but
the expression on the right side of the assignment does not appear to
produce multiple values.
The first piece of code is what you need to get it working. However, your for loop is incorrect. You probably want to iterative over all of the columns, so do for i = 1 : size(test,2), not test. size(test,2) determines how many columns your matrix has.
Therefore:
test = zeros(w, 3);
for i = 1:size(test,2) %// Change here
point = test(:,i);
[point(0), point(1), point(2)] = MY_CONVERSION(point(0), point(1),point(2));
test(:,i) = point;
end
Also, your second piece of code, you need to fix that for loop problem like I mentioned above. The first statement inside the for loop is superfluous. You don't need it. The third line of code needs to have [] surrounding a,b,c.
First, matrix = zeros(w, h); creates confusion: you are probably thinking of w as width and h as height. But the first argument of zeros is height: for example, zeros(2,3) is
0 0 0
0 0 0
Generally: row index first, column index second.
Then you have for i = 1:test but test is a matrix. You need a number here.
Here is a working example similar to your first fragment:
w = 2; h =3;
test = zeros(w, h);
for i = 1:h
point = test(:,i);
point = cos(point)*i; % sample conversion
test(:,i) = point;
end
Output:
1 2 3
1 2 3

Search for 1-D sequence in multidimensional array in Matlab

I have an array with n dimensions, and I have a sequence along one dimension at a certain location on all other dimensions. How do I find the location of this sequence? Preferably without loops.
I use matlab. I know what dimension it should be in, but the sequence isnt necessarily there. Find and == dont work. I could make an nd find function using crosscorrelation but Im guessing this is already implemented and I just dont know what function to call.
example:
ND = rand(10,10,10,10);
V = ND(randi(10),randi(10),randi(10),:);
[I1, I2, I3] = find(ND==V);
Edit: The sequence to be found spans the entire dimension it is on, I did not mention this in my original formulation of the problem. Knedlsepp`s solution solves exactly the problem I had, but Luis' solution solves a more general problem for when the sequence doesn't necessarily span the entire dimension.
As there are multiple ways to interpret your question, I will clarify: This approach assumes a 1D sequence of size: numel(V) == size(ND, dimToSearch). So, for V = [1,2] and ND = [1,2,1,2] it is not applicable. If you want this functionality go with Luis Mendo's answer, if not this will likely be faster.
This will be a perfect opportunity to use bsxfun:
We start with some example data:
ND = rand(10,10,10,10);
V = ND(3,2,:,3);
If you don't have the vector V given in the correct dimension (in this case [1,1,10,1]) you can reshape it in the following way:
dimToSearch = 3;
Vdims = ones(1, ndims(ND));
Vdims(dimToSearch) = numel(V);
V = reshape(V, Vdims);
Now we generate a cell that will hold the indices of the matches:
I = cell(1, ndims(ND));
At this point we compute the size of ND if it were collapsed along the dimension dimToSearch (we compute dimToSearch according to V, as at this point it will have the correct dimensions):
dimToSearch = find(size(V)>1);
collapsedDims = size(ND);
collapsedDims(dimToSearch) = 1;
Finally the part where we actually look for the pattern:
[I{:}] = ind2sub(collapsedDims, find(all(bsxfun(#eq, ND, V), dimToSearch)));
This is done in the following way: bsxfun(#eq, ND, V) will implicitly repmat the array V so it has the same dimensions as ND and do an equality comparison. After this we do a check with all to see if all the entries in the dimension dimToSearch are equal. The calls to find and ind2sub will then generate the correct indices to your data.
Let d be the dimension along which to search. I'm assuming that the sought sequence V may be shorter than size(ND,d). So the sequence may appear once, more than once, or never along each dimension-d- "thread".
The following code uses num2cell to reshape ND into a cell array such that each dimension-d-thread is in a different cell. Then strfind is applied to each cell to determine matches with V, and the result is a cell array with the same dimensions as ND, but where the dimension d is a singleton. The contents of each cell tell the d-dimension-positions of the matches, if any.
Credit goes to #knedlsepp for his suggestion to use num2cell, which greatly simplified the code.
ND = cat(3, [1 2 1 2; 3 4 5 6],[2 1 0 5; 0 0 1 2] ); %// example. 2x4x2
V = 1:2; %// sought pattern. It doesn't matter if it's a row, or a column, or...
d = 2; %// dimension along which to search for pattern V
result = cellfun(#(x) strfind(x(:).', V(:).'), num2cell(ND,d), 'UniformOutput', 0);
This gives
ND(:,:,1) =
1 2 1 2
3 4 5 6
ND(:,:,2) =
2 1 0 5
0 0 1 2
V =
1 2
result{1,1,1} =
1 3 %// V appears twice (at cols 1 and 3) in 1st row, 1st slice
result{2,1,1} =
[] %// V doesn't appear in 2nd row, 1st slice
result{1,1,2} =
[] %// V appears appear in 1st row, 2nd slice
result{2,1,2} =
3 %// V appears once (at col 3) in 2nd row, 2nd slice
One not very optimal way of doing it:
dims = size(ND);
Vrep = repmat(V, [dims(1), dims(2), dims(3), 1]);
ND_V_dist = sqrt(sum(abs(ND.^2-Vrep.^2), 4));
iI = find(ND_V_dist==0);
[I1, I2, I3] = ind2sub([dims(1), dims(2), dims(3)], iI);

Using elements of a vector to set elements of a matrix

I have a vector whose elements identify the indices (per column) that I need to set in a different matrix. Specifically, I have:
A = 7
1
2
and I need to create a matrix B with some number of rows of zeros, except for the elements identified by A. In other words, I want B:
B = zeros(10, 3); % number of rows is known; num columns = size(A)
B(A(1), 1) = 1
B(A(2), 2) = 1
B(A(3), 3) = 1
I would like to do this without having to write a loop.
Any pointers would be appreciated.
Thanks.
Use linear indexing:
B = zeros(10, 3);
B(A(:).'+ (0:numel(A)-1)*size(B,1)) = 1;
The second line can be written equivalently with sub2ind (may be a little slower):
B(sub2ind(size(B), A(:).', 1:numel(A))) = 1;

Get the neighbors of a matrix element

I have a matrix and for each element I want to get the index of its surrounding elements. All these results have to be stored into a matrix in the following way. Each row of the matrix corresponds to a matrix element and each of the columns of this matrix contain s the neighbor indexes. For example, for a 4x4 matrix we will get a 16x8 result array. Some of the matrix elements do not have 8 neighbors.
There is an example, I think it is working, I there any way to avoid for loop?:
ElementNeighbors = [];
for n = 1:numel(Matrix)
NeighborsMask = [ n-1 n+1 n+size(matrix,1) n-size(Matrix,1) n-size(Matrix,1)-1 n-size(Matrix,1)+1 ...
n+size(Matrix,1)-1 n+size(Matrix,1)+1 ];
ElementNeighbors = [ElementNeighbors ; NeighborsMask ];
end
ElementNeighbors (ElementNeighbors ==0|ElementNeighbors <0) = NaN;
Given the linear indices of a matrix M(n,m), you can convince yourself that the top left neighbor of element M(i,j) = M(i-1, j-1) = M(i-1 + n * (j-2))
In "linear index" space that means the offset of this element is
-n-1
Doing this for all other locations, we find
-n-1 | -1 | n-1
-n | x | n => [-n-1, -n, -n+1, -1, +1, +n-1, +n, +n+1]
-n+1 | +1 | n+1
Thus you can create a vector offset with the above values (replacing n with the first dimension). For example, if M is (5x4), then
offset = [-6 -5 -4 -1 1 4 5 6];
You then create all the indices:
indices = bsxfun(#plus, (1:m*n), offset(:));
bsxfun is a cool shorthand for "do this function on these elements; where one element has a singleton dimension and the other doesn't, expand accordingly". You could do the same with repmat, but that creates unnecessary intermediate matrices (which can sometimes be very large).
That command will create a (8 x m*n) matrix of indices of all 8 neighbors, including ones that may not really be the neighbors... something you need to fix.
Several possible approaches:
pad the matrix before you start
don't care about wrapping, and just get rid of the elements that fall off the edge
create a mask for all the ones that are "off the edge".
I prefer the latter. "Off the edge" means:
going up in the top row
going left in the left column
going down in the bottom row
going right in the right column
In each of these four cases there are 3 indices that are 'invalid'. Their position in the above matrix can be determined as follows:
mask = zeros(size(M));
mask(:,1) = 1;
left = find(mask == 1);
mask(:,end) = 2;
right = find(mask == 2);
mask(1,:) = 3;
top = find(mask == 3);
mask(end,:) = 4;
bottom = find(mask == 4);
edgeMask = ones(8,m*n);
edgeMask(1:3, top) = 0;
edgeMask([1 4 6], left) = 0;
edgeMask([3 5 8], right) = 0;
edgeMask(6:8, bottom) = 0;
Now you have everything you need - all the indices, and the "invalid" ones. Without loops.
If you were feeling ambitious you could turn this into a cell array but it will be slower than using the full array + mask. For example if you want to find the average of all the neighbors of a value, you can do
meanNeighbor = reshape(sum(M(indices).*edgeMask, 1)./sum(edgeMask, 1), size(M));
EDIT re-reading your question I see you wanted a M*N, 8 dimension. My code is transposed. I'm sure you can figure out how to adapt it...
ATTRIBUTION #Tin helpfully suggested many great edits to the above post, but they were rejected in the review process. I cannot totally undo that injustice - but would like to record my thanks here.
EXTENDING TO DIFFERENT REGIONS AND MULTIPLE DIMENSIONS
If you have an N-dimensional image matrix M, you could find the neighbors as follows:
temp = zeros(size(M));
temp(1:3,1:3,1:3) = 1;
temp(2,2,2) = 2;
offsets = find(temp==1) - find(temp==2);
If you want a region that is a certain radius in size, you could do
sz = size(M);
[xx yy zz] = meshgrid(1:sz(1), 1:sz(2), 1:sz(3));
center = round(sz/2);
rr = sqrt((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2);
offsets = find(rr < radius) - find(rr < 0.001);
You can probably figure out how to deal with the problem of edges along the lines shown earlier for the 2D case.
Untested - please see if you notice any problems with the above.