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.
Related
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'));
I've always been told that almost all for loops can be omitted in MATLAB and that they in general slow down the process. So is there a way to do so here?:
I have a cell-array (tsCell). tsCell stores time-arrays with varying length. I want to find an intersecting time-array for all time-arrays (InterSection):
InterSection = tsCell{1}.time
for i = 2:length{tsCell};
InterSection = intersect(InterSection,tsCell{i}.time);
end
Here's another way. This also assumes there are no duplicates within each original vector.
tsCell_time = {[1 6 4 5] [4 7 1] [1 4 3] [4 3 1 7]}; %// example data (from Divakar)
t = [tsCell_time{:}]; %// concat into a single vector
u = unique(t); %// get unique elements
ind = sum(bsxfun(#eq, t(:), u), 1)==numel(tsCell_time); %// indices of unique elements
%// that appear maximum number of times
result = u(ind); %// output those elements
Here's a vectorized approach using unique and accumarray, assuming there are no duplicates within each cell of the input cell array -
[~,~,idx] = unique([tsCell_time{:}],'stable')
out = tsCell_time{1}(accumarray(idx,1) == length(tsCell_time))
Sample run -
>> tsCell_time = {[1 6 4 5],[4 7 1],[1 4 3],[4 3 1 7]};
>> InterSection = tsCell_time{1};
for i = 2:length(tsCell_time)
InterSection = intersect(InterSection,tsCell_time{i});
end
>> InterSection
InterSection =
1 4
>> [~,~,idx] = unique([tsCell_time{:}],'stable');
out = tsCell_time{1}(accumarray(idx,1) == length(tsCell_time));
>> out
out =
1 4
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,[])
I am trying to vectorize, or make the following code more efficient:
[Y,k] = min(abs(dxcp-X));
X = dxcp(k);
The objective of the code is to compare a value X to an array of accepted values for x (dxcp) and assign X to the closest value in the array dxcp. For example:
X is equal to 9 and the dxcp array is: [ 1, 2, 3, 6, 10, 20]. The second line would change X to be equal to 10.
I am trying to change my script so that X can be inputted as an array of numbers and was wondering what would be the most efficient way to go about making the above code work for this case. Of course I could use:
for i = 1:numel(X)
[Y,k] = min(abs(dxcp-X(i)));
X(i) = dxcp(k);
end
But I have the feeling that there must be a way to accomplish this more efficiently. Cheers, nzbru.
You need to use bsxfun to extend your case to a vector case.
Code
dxcp = [1 2 3 6 10 20];
X = [2 5 9 18]
abs_diff_vals = abs(bsxfun(#minus,dxcp,X')); %%//'
[~,k] = min(abs_diff_vals,[],2);
X = dxcp(k)
Output
X =
2 5 9 18
X =
2 6 10 20
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));