MATLAB - convert vector to unit vector [duplicate] - matlab

This question already has answers here:
Normalizing rows of matrix, so that their norm is equal to 1 (MATLAB)
(2 answers)
Closed 6 years ago.
I have a vector:
vector = [1 2 3;4 5 6; 7 9 0]
vector =
1 2 3
4 5 6
7 9 0
I want to take this and create a unit vector. We can get the magnitude by doing:
mag = sqrt(sum(vector'.^2))'
mag =
3.7417
8.7750
11.4018
When we try to divide each element by the magnitude I get an error:
vector./mag
Error using ./
Matrix dimensions must agree.
Essentially I must divide every vector element in each row by every row in the mag vector. How can I do this?

The other answers give the correct result but you can vectorize the calculation for faster calculation.
ret = bsxfun(#rdivide, vector, mag)
I recommend using the bsxfun, it is a very useful function for matrix calculation.

The problem is that, as the error message says, the dimensions of vector and mag don't match.
You want to divide every element of the first row of vector by mag(1).
What you need is repmat(), which "repeats copies of array".
Writing
repmat(mag,1,3)
returns a 3x3 matrix such that every column is an exact copy of mag:
3.7417 3.7417 3.7417
8.7750 8.7750 8.7750
11.4018 11.4018 11.4018
So you can use the one-liner:
vector./repmat(mag,1,3)
ans =
0.26726 0.53452 0.80178
0.45584 0.56980 0.68376
0.61394 0.78935 0.00000
That way, the first row of vector, i.e., [1 2 3], is divided element-by-element by [3.7417 3.7417 3.7417].
In other words, every element of vector is divided by the correct magnitude.

You can use matrix operators in MATLAB:
result = diag(1./mag)*vector;
If dimension of mag can be too big you can use sparse version of it:
result = spdiags(1./mag,0,speye(numel(mag)))*vector;

A simple solution is the use of a for-loop:
vector = [1 2 3; 4 5 6; 7 9 0];
mag = sqrt(sum(vector'.^2))';
A = [];
for i = 1:numel(mag)
A(i,:) = vector(i,:)./mag(i);
end

vector = [1 2 3; 4 5 6 ;7 9 0] ;
[n,m]=size(vector);
for i=1:n
normv=norm(vector(i,:),2);
nvector(i,:)=vector(i,:)/normv;
end
the nvector will contain the normalized vector of each line

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

Filter elements from a 3D matrix without loop

I have a 3d matrix H(i,j,k) with dimensions (i=1:m,j=1:n,k=1:o). I will use a simple case with m=n=o = 2:
H(:,:,1) =[1 2; 3 4];
H(:,:,2) =[5 6; 7 8];
I want to filter this matrix and project it to an (m,n) matrix by selecting for each j in 1:n a different k in 1:0.
For instance, I would like to retrieve (j,k) = {(1,2), (2,1)}, resulting in matrix G:
G = [5 2; 7 4];
This can be achieved with a for loop:
filter = [2 1]; % meaning filter (j,k) = {(1,2), (2,1)}
for i = 1:length(filter)
G(:,i) = squeeze(H(:,i,filter(i)));
end
But I'm wondering if it is possible to avoid the for loop via some smart indexing.
You can create all the linear indices to get such an output with the expansion needed for the first dimension with bsxfun. The implementation would look like this -
szH = size(H)
offset = (filter-1)*szH(1)*szH(2) + (0:numel(filter)-1)*szH(1)
out = H(bsxfun(#plus,[1:szH(1)].',offset))
How does it work
(filter-1)*szH(1)*szH(2) and (0:numel(filter)-1)*szH(1) gets the linear indices considering only the third and second dimension elements respectively. Adding these two gives us the offset linear indices.
Add the first dimenion linear indices 1:szH(1) to offset array in elementwise fashion with bsxfun to give us the actual linear indices, which when indexed into H would be the output.
Sample run -
H(:,:,1) =
1 2
3 4
H(:,:,2) =
5 6
7 8
filter =
2 1
out =
5 2
7 4

Magnitude of each column in a matrix [duplicate]

This question already has answers here:
Vector norm of an array of vectors in MATLAB
(4 answers)
Closed 5 years ago.
I have an input matrix that has 3 rows and 1000 columns. Each column represents and x, y, z variable. I want to find the magnitude of each column and store that in an output matrix that has 1 row and 1000 columns.
This is my current attempt but it doesn't seem to be working:
output(1,:) = norm(input(3,:));
my input matrix looks like:
x1, x2,...,x1000
y1, y2,...,y1000
z1, z2,...,z1000
I want my output matrix to look like:
[magnitude(x1,y1,z1), magnitude(x2,y2,z2),...,magnitude(x1000,y1000,z1000)]
Any help would be greatly appreciated.
norm(input(3,:)) will give you the norm of the 1000 elements of the third row.
Easy solution is to just run a for loop.
output = zeros(1,1000); %Preallocate space
for i = 1:length(output)
output(i) = norm(input(:, i));
end
MATLAB's norm function only works for single vectors. Let A be the name of the matrix which columns you want to find the norm to. Then this command does the job:
norm_A = sqrt(sum(A.*A));
Here is an example:
>> A = [1:5; 1:5; 1:5]
A =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
>> norm_A = sqrt(sum(A.*A))
norm_A =
1.7321 3.4641 5.1962 6.9282 8.6603

Averaging every n elements of a vector in matlab

I would like to average every 3 values of an vector in Matlab, and then assign the average to the elements that produced it.
Examples:
x=[1:12];
y=%The averaging operation;
After the operation,
y=
[2 2 2 5 5 5 8 8 8 11 11 11]
Therefore the produced vector is the same size, and the jumping average every 3 values replaces the values that were used to produce the average (i.e. 1 2 3 are replaced by the average of the three values, 2 2 2). Is there a way of doing this without a loop?
I hope that makes sense.
Thanks.
I would go this way:
Reshape the vector so that it is a 3×x matrix:
x=[1:12];
xx=reshape(x,3,[]);
% xx is now [1 4 7 10; 2 5 8 11; 3 6 9 12]
after that
yy = sum(xx,1)./size(xx,1)
and now
y = reshape(repmat(yy, size(xx,1),1),1,[])
produces exactly your wanted result.
Your parameter 3, denoting the number of values, is only used at one place and can easily be modified if needed.
You may find the mean of each trio using:
x = 1:12;
m = mean(reshape(x, 3, []));
To duplicate the mean and reshape to match the original vector size, use:
y = m(ones(3,1), :) % duplicates row vector 3 times
y = y(:)'; % vector representation of array using linear indices

How can I get counterdiagonals of a matrix and concatenate them?

Short Version
How can I do concatMap in MATLAB? I'm trying to build a single vector from a series of smaller, differently sized vectors. I know I can do:
result = [];
for i=1:N
result = [result nextPart(i)];
end
but that has a serious speed impact and there must be a smarter way to do concatMap.
Long Version
I'm trying to write a MATLAB function that returns the counterdiagonals of a block. For example, if you have the block:
1 2 4
3 5 7
6 8 9
then counterDiagonals(block) should return [1 2 3 4 5 6 7 8 9].
I have a function that will find a single counter diagonal of a block. i.e. counterDiagonal(x, 3) will return [4 5 6].
Therefore, counterDiagonals should be as simple as concatMap counterDiagonal(x, i) (1:N) where N is (2*length(block)-1). How can I do this in MATLAB in an efficient way?
One problem with the accepted answer: if the matrix A had zeros, they will be incorrectly removed from the result.. Instead you should work on the indices of the elements:
A = [0 2 4; 3 5 7; 6 8 9]; %# Sample matrix (contains zeros)
ind = reshape(1:numel(A), size(A)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals (or use ROT90)
ind(ind==0) = []; %# keep non-zero indices
result = A(ind); %# get elements in desired order
This is very similar to this answer I gave in a previous question (the difference was that the anti-digaonals were in reverse order).
I believe what you want to do can be accomplished using the functions ROT90 and SPDIAGS:
A = [1 2 4; 3 5 7; 6 8 9]; %# Sample matrix
result = rot90(A); %# Rotate the matrix counter-clockwise
result = spdiags(result); %# Find all the diagonals
result = result(result ~= 0).'; %'# Remove zero padding and format the results
%# into a row vector
And you should end up with result = [1 2 3 4 5 6 7 8 9].
EDIT: As Amro mentions in a comment, the above code assumes that there are no zeroes in the original matrix A. If there are zeroes in the original matrix, one solution is to replace them with a non-zero flag value that you know doesn't appear in the original matrix (like, for example, NaN), run the above code, then replace the flag values in the result:
A = [0 2 4; 3 0 7; 6 8 0]; %# Sample matrix
result = rot90(A); %# Rotate the matrix counter-clockwise
result(result == 0) = nan; %# Replace zeroes with NaN
result = spdiags(result); %# Find all the diagonals
result = result(result ~= 0).'; %'# Remove zero padding and format the results
%# into a row vector
result(isnan(result)) = 0; %# Put the original zeroes back
Short version:
If you preassign your result array, everything will be a lot faster.
result = zeros(1,knownLengthOfResultsArray); %# such as "numel(block)"
ct = 1;
for i=1:N
tmp = nextPart(i);
nTmp = length(tmp);
result(ct:ct+nTmp-1) = tmp;
ct = ct + nTmp;
end
Long version:
However, it may be even more efficient to rewrite your algorithm. See e.g. the answers to this question (use fliplr on your array first), or #gnovice's answer.