use vector to select columns of a matlab array [duplicate] - matlab

This question already has answers here:
Get different column in each row
(2 answers)
Closed 8 years ago.
This is an issue i commonly find myself trying to solve. I have the following:
A = [1 2;
3 4;
5 6;
7 8;
9 10];
B = [1,2,1,2,2];
On each row (i) of A, i want to return the value of the column specified in B(i). I currently solve the problem using a loop:
result = zeros(size(B));
for i=1:length(B)
result(i) = A(i,B(i));
end
Where result = [1 4 5 8 10]
But this seems inelegant to me. Is there a one-liner?

You could get the correct linear indices using sub2ind:
rows = (1:numel(B))'
cols = B(:);
ind = sub2ind(size(A), rows, cols);
A(ind)
or in a one-liner
A(sub2ind(size(A), (1:numel(B))', B(:)))
or a more elegant method (taken from the 2nd answer to the duplicate question)
diag(A(:,B))
I can't tell you about performance though...

Related

Finding value that is similar and available in another vector [duplicate]

This question already has an answer here:
Matlab, finding common values in two array
(1 answer)
Closed 5 years ago.
Let say we have 2 vectors of A and B,
A=[1;2;5;6;7;9]; B=[1;3;4;7];
How to find value C that are available in both A and B? The expected value should be
C=[1;7]
Since the title of your question says "similar", I assume you want to compare with a given tolerance. For that you can use ismembertol:
tol = 1e-3;
A = [1; 2 ; 5 ; 6 ; 7 ; 9];
B = [1.0001; 3.0001; 4.0001; 7.0001];
ind = ismembertol(A, B, tol);
C = A(ind);
Very simple:
A=[1;2;5;6;7;9];
B=[1;3;4;7];
C=intersect(A,B)

matlab: filling matrix diagonalwise [duplicate]

This question already has answers here:
adding values to diagonals of matrix using element-wise addition in matlab
(3 answers)
Closed 7 years ago.
I have an (2n-1)-by-1 vector with certain values and I want to obtain an n-n matrix with the diagonals filled using the same value.
Eg. if I have
a = [1; 2; 3; 4; 5];
I want to obtain
A = [[3 4 5];[2 3 4];[1 2 3]]
= 3 4 5
2 3 4
1 2 3
My matrix dimensions are a lot bigger so I'd want this as efficient as possible. I already found following solutions:
n = 3;
A = toeplitz(a);
A = A(1:n,end-n+1:end)
and
A = a(n)*eye(n);
for j=1:n-1
A(1+j:n+1:end-j*n) = a(n-j);
A(j*n+1:n+1:end) = a(n+j);
end
I wonder if there are more efficient ways to obtain this result, keeping in mind that I am working with huge matrices and really need the speed.
ix=bsxfun(#plus,[1:n],[n-1:-1:0]'); %generate indices
A=a(ix);
or
A=hankel(a) %might be faster than toeplitz because half the matrix is zero
A(n:-1:1,1:n)
here is what hankel does internally (at least in ML R2013a), adapted to this problem:
c=[1:n];
r=[n-1:-1:0]';
idx=c(ones(n,1),:)+r(:,ones(n,1));
A=a(ix);
I guess the bsxfun solution and what thewaywewalk supposed is the fastest (it's basically the same)
Go with:
n = (numel(a)+1)/2;
A = a(bsxfun(#minus, n+1:n+n, (1:n).'));

Matlab vectorization of vector creation [duplicate]

This question already has answers here:
Element-wise array replication according to a count [duplicate]
(4 answers)
Closed 8 years ago.
I would like to vectorize the creation of the following vector:
For example-
Let A be a vector [5 3 2 1]
And let B be a vector [1 2 3 4]
I would like C to be the vector [1 1 1 1 1 2 2 2 3 3 4]
Meaning- each element i in B is duplicated A(i) times in C.
I haven't found a way to vectorize the creation of this, any ideas?
Thanks in advance!
Ronen
Approach #1
Here's one approach if B doesn't have any zeros -
C = nonzeros(bsxfun(#times,bsxfun(#le,[1:max(A)]',A),B))
Approach #2
A general case solution -
mask = bsxfun(#le,[1:max(A)]',A) %//'
B_ext = bsxfun(#times,mask,B)
C = B_ext(mask)
Approach #3
cumsum based approach and must be pretty efficient one -
idx = [1 cumsum(A(1:end-1))+1] %// indices where each new B values start
C = zeros(sum(A),1) %// storage for output
C(idx) = diff([0 B]) %// put those values, but offseted
C = cumsum(C) %// finally get the output

FOR loop over column vector vs row vector [duplicate]

This question already has answers here:
The for loop doesn't iterate
(2 answers)
Closed 9 years ago.
I was just writing a "kinda-foreach" loop in Matlab and encountered this strange behavior:
I have the matrix A:
A = [
3
9
5
0];
And I want to use a foreach loop (as explained here) on the A.
If I write this:
for i = A
disp('for')
i
end
The result will be:
for
i =
3
9
5
0
But when I use the transpose, the result will change:
for i = A'
disp('for')
i
end
Result:
for
i =
3
for
i =
9
for
i =
5
for
i =
0
Which is the result I want.
Can anybody explain what's going on here? What's the difference between these two cases?
when you type
A = [
3
9
5
0];
you create a column vector. Because Matlab iterates over columns you get one answer (the first column). By transposing it you get a row vector with 4 columns and therefore 4 answers each with one column.
In Matlab, the for loop iterates over columns. http://www.mathworks.es/es/help/matlab/ref/for.html

Extract fixed amounts of data from array based on a list of indices

I have the array
a=1:20
and a series of indices which indicate where I want to start pulling data out:
i=[4,12]
For each index i, I want that index and the next four (well, x, really) elements in a column or row. I'll avoid getting to close to the end of the array, so that special case can be disregarded.
If I was hard-coding this, I could use:
a([4:8;12:16])
and this would achieve my result.
But i may have many different values.
Any thoughts on how I can transform a list of indices into a matrix of ranges, or other ways to solve this problem?
EDIT
I am using Matlab 2007; it would be preferable if the solution relied solely on Matlab's internals and toolboxes. bsxfun is not present until 2007a.
Let i be your indicesx and x the number of elements you want in addition to the elements in i, then you can use
i = [4 6 8];
x = 4;
bsxfun(#plus, 0:x, i(:))
to get a matrix of indices:
ans =
4 5 6 7 8
6 7 8 9 10
8 9 10 11 12
If you do not have access to bsxfun you can use repmat instead:
i = [4 6 8];
x = 4;
repmat(i(:), 1, x+1) + repmat(0:x, length(i), 1)
Here is a solution without bsxfun but with repmat inspired by the previous answer.
i = [4 6 8];
x = 4;
p = repmat(1:x,length(i),1);
q = repmat(i',1,x);
p+q