Correct usage of arrayfun/bsxfun in Matlab - simple example - arrays

Assume we have a 1-d Matrix, with random length:
M = [102,4,12,6,8,3,4,65,23,43,111,4]
Moreover I have a vector with values that are linked to the index of M:
V = [1,5]
What i want is a simple code of:
counter = 1;
NewM = zeros(length(V)*3,1);
for i = 1:length(V)
NewM(counter:(counter+2)) = M(V(i):(V(i)+2))
counter = counter+3;
end
So, the result will be
NewM = [102,4,12,8,3,4]
In other words, I want the V to V+2 values from M in a new array.
I'm pretty sure this can be done easier, but I'm struggeling with how to implement it in arrayfun /bsxfun...
bsxfun(#(x,y) x(y:(y+2)),M,V)

With bsxfun it's about getting the Vi on one dimension and the +0, +1 +2 on the other:
M = [102,4,12,6,8,3,4,65,23,43,111,4];
V = [1,5];
NewM = M( bsxfun(#plus, V(:), 0:2) )
NewM =
102 4 12
8 3 4
Or if you want a line:
NewM = reshape(NewM.', 1, [])
NewM =
102 4 12 8 3 4

Using arrayfun (note that M is used as an "external" matrix entity in this scope, whereas the arrayfun anonymous function parameter x correspond to the elements in V)
NewM = cell2mat(arrayfun(#(x) M(x:x+2), V, 'UniformOutput', false));
With result
NewM =
102 4 12 8 3 4

One fully vectorized solution is to expand V to create the correct indexing vector. In your case:
[1,2,3,5,6,7]
You can expand V to replicate each of it's elements n times and then add a repeating vector of 0:(n-1):
n = 3;
idx = kron(V, ones(1,n)) + mod(0:numel(V)*n-1,n)
So here kron(V, ones(1,n)) will return [1,1,1,5,5,5] and mod(0:numel(V)*n-1,n) will return [0,1,2,0,1,2] which add up to the required indexing vector [1,2,3,5,6,7]
and now it's just
M(idx)
Note a slightly faster alternative to kron is reshape(repmat(V,n,1),1,[])

Related

How to obtain the index of a line specific value [ a b c d] within a double matrix?

I have made a table composed of four variables[v1, v2, v3, v4], with near 26000 rows. I need to search the values of an specific line e.g. [1 1 2 2016] within the table (26000 x 4), and return the index of the line in which the search is located.
Example of what I would like to search:
want_1 = [1 1 3 2016];
want_2 = [1 1 5 2016];
And would like to obtain the number of the line in which it is located.
If you have a matrix M (which you could get from table2array(T) on your table) you should be able to use implicit expansion* and all to get your result
srch = [1 1 3 2016]; % Row to search for
res = find( all( M == srch, 2 ) );
The find converts the logical array returned by all into the row numbers where it is true.
The implicit expansion here is basically the same as repeating the srch array for the entire height of the matrix M and then doing an element-wise == operation. The all then ensures that every comparison in a given row was true (i.e. a match for every element of srch).
*Implicit expansion relies on having MATLAB R2016b or newer... for older versions you can achieve the same using bsxfun.
Just as an exercise in alternatives, you could use splitapply instead to apply the all and == operators to each row in turn, this is probably slower...
res = find( splitapply( #(x)all(x==srch), M, (1:size(M,1)).' ) );
Or you could even use rowfun, which is a bit of a loop-in-disguise, but would work on your table T without having to first convert it to a matrix:
res = find( rowfun( #(varargin)all([varargin{:}]==srch), T, 'OutputFormat', 'uniform' ) );
For a matrix, you can use ismember with the 'rows' option:
M = [1 2 3 4; 1 1 3 2016; 5 6 7 8]; % example data matrix
wanted = [1 1 3 2016]; % example wanted row
result = find(ismember(M, wanted, 'rows'));
This also works with a table, as long as the wanted row is a table (of one row) with the same variable names:
M = table;
M.hour = [1; 2; 3]; M.day = [4; 5; 6]; M.month = [7; 8; 9]; M.year = [10; 11; 12];
wanted = table;
wanted.hour = 2; wanted.day = 5; wanted.month = 8; wanted.year = 11;
result = find(ismember(M, wanted, 'rows'));

what is the matlab way without loop to multiply a matrix with a vector (extending to the 3rd dim)?

A simple example to illustrate all elements of a matrix multiplying each element of a vector to generate a 3D array.
M = reshape(1:12,4,3);
V = 1:2;
n = length(V);
A = nan([size(M),n]);
for ii = 1 : n
A(:,:,ii) = M * V(ii);
end
then
A(:,:,1) =
1 5 9
2 6 10
3 7 11
4 8 12
A(:,:,2) =
2 10 18
4 12 20
6 14 22
8 16 24
Or by repmat both M and V to the size of [4,3,2],
A = repmat(M,1,1,n) * reshape(V(ones(size(M)),:),[size(M),n])
It creates two 3D array by repmat besides the resulting 3d array A.
How to make it efficiently WITHOUT for loop and save the memory use?
According to the answer by #Lincoln,
A = bsxfun(#times, repmat(M,1,1,n), reshape(1:n, 1, 1, n));
repmat the vector V to 3d is not necessary.
Is it possible to create NO 3d array if the final result wanted is 2d, the sum of A along the 3rd dim? By for loop, the code would be
M = reshape(1:12,4,3);
V = 1:2;
n = length(V);
A = 0;
for ii = 1 : n
A = A + M * V(ii);
end
Try replacing the for.. loop with:
M_rep = repmat(M,1,1,n) %//repeat M in the 3rd dimension n times
v = reshape(1:n, 1, 1, n) %//create a vector [1 2 .. n] pointing in the third dimension
A = bsxfun(#times, M_rep, v) %//vector multiply them in the third dimension
In your above example, n=2.
EDIT (to your added question): To sum without allocating A:
B = sum(bsxfun(#times, M_rep, v),3);

Matlab Convert Vector to Binary Matrix [duplicate]

This question already has answers here:
Create a zero-filled 2D array with ones at positions indexed by a vector
(4 answers)
Closed 6 years ago.
I have a vector v of size (m,1) whose elements are integers picked from 1:n. I want to create a matrix M of size (m,n) whose elements M(i,j) are 1 if v(i) = j, and are 0 otherwise. I do not want to use loops, and would like to implement this as a simple vector-matrix manipulation only.
So I thought first, to create a matrix with repeated elements
M = v * ones(1,n) % this is a (m,n) matrix of repeated v
For example v=[1,1,3,2]'
m = 4 and n = 3
M =
1 1 1
1 1 1
3 3 3
2 2 2
then I need to create a comparison vector c of size (1,n)
c = 1:n
1 2 3
Then I need to perform a series of logical comparisons
M(1,:)==c % this results in [1,0,0]
.
M(4,:)==c % this results in [0,1,0]
However, I thought it should be possible to perform the last steps of going through each single row in compact matrix notation, but I'm stumped and not knowledgeable enough about indexing.
The end result should be
M =
1 0 0
1 0 0
0 0 1
0 1 0
A very simple call to bsxfun will do the trick:
>> n = 3;
>> v = [1,1,3,2].';
>> M = bsxfun(#eq, v, 1:n)
M =
1 0 0
1 0 0
0 0 1
0 1 0
How the code works is actually quite simple. bsxfun is what is known as the Binary Singleton EXpansion function. What this does is that you provide two arrays / matrices of any size, as long as they are broadcastable. This means that they need to be able to expand in size so that both of them equal in size. In this case, v is your vector of interest and is the first parameter - note that it's transposed. The second parameter is a vector from 1 up to n. What will happen now is the column vector v gets replicated / expands for as many values as there are n and the second vector gets replicated for as many rows as there are in v. We then do an eq / equals operator between these two arrays. This expanded matrix in effect has all 1s in the first column, all 2s in the second column, up until n. By doing an eq between these two matrices, you are in effect determining which values in v are equal to the respective column index.
Here is a detailed time test and breakdown of each function. I placed each implementation into a separate function and I also let n=max(v) so that Luis's first code will work. I used timeit to time each function:
function timing_binary
n = 10000;
v = randi(1000,n,1);
m = numel(v);
function luis_func()
M1 = full(sparse(1:m,v,1));
end
function luis_func2()
%m = numel(v);
%n = 3; %// or compute n automatically as n = max(v);
M2 = zeros(m, n);
M2((1:m).' + (v-1)*m) = 1;
end
function ray_func()
M3 = bsxfun(#eq, v, 1:n);
end
function op_func()
M4= ones(1,m)'*[1:n] == v * ones(1,n);
end
t1 = timeit(#luis_func);
t2 = timeit(#luis_func2);
t3 = timeit(#ray_func);
t4 = timeit(#op_func);
fprintf('Luis Mendo - Sparse: %f\n', t1);
fprintf('Luis Mendo - Indexing: %f\n', t2);
fprintf('rayryeng - bsxfun: %f\n', t3);
fprintf('OP: %f\n', t4);
end
This test assumes n = 10000 and the vector v is a 10000 x 1 vector of randomly distributed integers from 1 up to 1000. BTW, I had to modify Luis's second function so that the indexing will work as the addition requires vectors of compatible dimensions.
Running this code, we get:
>> timing_binary
Luis Mendo - Sparse: 0.015086
Luis Mendo - Indexing: 0.327993
rayryeng - bsxfun: 0.040672
OP: 0.841827
Luis Mendo's sparse code wins (as I expected), followed by bsxfun, followed by indexing and followed by your proposed approach using matrix operations. The timings are in seconds.
Assuming n equals max(v), you can use sparse:
v = [1,1,3,2];
M = full(sparse(1:numel(v),v,1));
What sparse does is build a sparse matrix using the first argument as row indices, the second as column indices, and the third as matrix values. This is then converted into a full matrix with full.
Another approach is to define the matrix containing initially zeros and then use linear indexing to fill in the ones:
v = [1,1,3,2];
m = numel(v);
n = 3; %// or compute n automatically as n = max(v);
M = zeros(m, n);
M((1:m) + (v-1)*m) = 1;
I think I've also found a way to do it, and it would be nice if somebody could tell me which of the methods shown is faster for very large vectors and matrices. The additional method I thought of is the following
M= ones(1,m)'*[1:n] == v * ones(1,n)

Selecting elements from an array in MATLAB

I know that in MATLAB, in the 1D case, you can select elements with indexing such as a([1 5 3]), to return the 1st, 5th, and 3rd elements of a. I have a 2D array, and would like to select out individual elements according to a set of tuples I have. So I may want to get a(1,3), a(1,4), a(2,5) and so on. Currently the best I have is diag(a(tuples(:,1), tuples(:,2)), but this requires a prohibitive amount of memory for larger a and/or tuples. Do I have to convert these tuples into linear indices, or is there a cleaner way of accomplishing what I want without taking so much memory?
Converting to linear indices seems like a legitimate way to go:
indices = tuples(:, 1) + size(a,1)*(tuples(:,2)-1);
selection = a(indices);
Note that this is also implement in the Matlab built-in solution sub2ind, as in nate'2 answer:
a(sub2ind(size(a), tuples(:,1),tuples(:,2)))
however,
a = rand(50);
tuples = [1,1; 1,4; 2,5];
start = tic;
for ii = 1:1e4
indices = tuples(:,1) + size(a,1)*(tuples(:,2)-1); end
time1 = toc(start);
start = tic;
for ii = 1:1e4
sub2ind(size(a),tuples(:,1),tuples(:,2)); end
time2 = toc(start);
round(time2/time1)
which gives
ans =
38
so although sub2ind is easier on the eyes, it's also ~40 times slower. If you have to do this operation often, choose the method above. Otherwise, use sub2ind to improve readability.
if x and y are vectors of the x y values of matrix a, then sub2und should solve your problem:
a(sub2ind(size(a),x,y))
For example
a=magic(3)
a =
8 1 6
3 5 7
4 9 2
x = [3 1];
y = [1 2];
a(sub2ind(size(a),x,y))
ans =
4 1
you can reference the 2D matlab position with a 1D number as in:
a = [3 4 5;
6 7 8;
9 10 11;];
a(1) = 3;
a(2) = 6;
a(6) = 10;
So if you can get the positions in a matrix like this:
a([(col1-1)*(rowMax)+row1, (col2-1)*(rowMax)+row2, (col3-1)*(rowMax)+row3])
note: rowmax is 3 in this case
will give you a list of the elements at col1/row1 col2/row2 and col3/row3.
so if
row1 = col1 = 1
row2 = col2 = 2
row3 = col3 = 3
you will get:
[3, 7, 11]
back.

Element-wise array replication in Matlab

Let's say I have a one-dimensional array:
a = [1, 2, 3];
Is there a built-in Matlab function that takes an array and an integer n and replicates each
element of the array n times?
For example calling replicate(a, 3) should return [1,1,1,2,2,2,3,3,3].
Note that this is not at all the same as repmat. I can certainly implement replicate by doing repmat on each element and concatenating the result, but I am wondering if there is a built in function that is more efficient.
I'm a fan of the KRON function:
>> a = 1:3;
>> N = 3;
>> b = kron(a,ones(1,N))
b =
1 1 1 2 2 2 3 3 3
You can also look at this related question (which dealt with replicating elements of 2-D matrices) to see some of the other solutions involving matrix indexing. Here's one such solution (inspired by Edric's answer):
>> b = a(ceil((1:N*numel(a))/N))
b =
1 1 1 2 2 2 3 3 3
a = [1 2 3];
N = 3;
b = reshape(repmat(a,N,1), 1, [])
As of R2015a, there is a built-in and documented function to do this, repelem:
repelem Replicate elements of an array.
W = repelem(V,N), with vector V and scalar N, creates a vector W where each element of V is repeated N times.
The second argument can also be a vector of the same length as V to specify the number of replications for each element. For 2D replication:
B = repelem(A,N1,N2)
No need for kron or other tricks anymore!
UPDATE: For a performance comparison with other speedy methods, please see the Q&A Repeat copies of array elements: Run-length decoding in MATLAB.
>> n=3;
>> a(floor((0:size(a,2)*n-1)/n)+1)
ans =
1 1 1 2 2 2 3 3 3
Some exotic alternatives. Admittedly more funny than useful:
Assign the (first) result of meshgrid to a vector:
b = NaN(1,numel(a)*n); %// pre-shape result
b(:) = meshgrid(a,1:n);
Build a matrix that multiplied by a gives the result:
b = a * fliplr(sortrows(repmat(eye(numel(a)),n,1))).';
Use ind2sub to generate the indices:
[~, ind] = ind2sub([n 1],1:numel(a)*n);
b = a(ind);
If you have the image processing toolbox, there is another alternative:
N = 3;
imresize(a, [1 N*numel(a)],'nearest')
% To get b = [1 1 1 2 2 2 3 3 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a,1,numel(temp_a));
% To get b = [1 2 3 1 2 3 1 2 3]
N = 3;
a = [1 2 3];
temp_a = a(ones(N,1),:);
b = reshape(temp_a',1,numel(temp_a));

Resources