Intersection of multiple arrays without for loop in MATLAB - arrays

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

Related

How to find a particular array matching certain pattern in cell array?

I want to know the index of row of cell consisting of particular array in particular column...
Example:
C{1,1} = [1 2 3];
C{1,2} = [4 5 6];
C{2,1} = [11 12 13];
C{2,2} = [14 15 16];
I want to get index as 1 when I search for [1 2 3] in column 1 (or) 2 when I search for [14 15 16] in column 2. I tried using
index = find([C{:,1}] == [1 2 3])
But didn't get. Please help
Use cellfun in combination with strfind and isempty or isequal directly.
pattern = [1 2 3];
out = cellfun(#(x) ~isempty(strfind(x,pattern)),C)
%// or as suggested by Luis Mendo
out = cellfun(#(x) isequal(x,pattern),C)
ind = find(out)
if the order within the arrays does not matter, also the following using
ismember and all is possible:
out = cellfun(#(x) all(ismember(x,pattern)),C)
out =
1 0
0 0
ind =
1
Do all the arrays have the same length n? Then you could use a more vectorized approach with an optional if-condition to see if your result is valid. It may is not necessary, depending how sure you are about your pattern input.
n = 3;
pos = strfind([C{:}],pattern)
ind = [];
if mod(pos,n) == 1, ind = (pos - 1)/n + 1, end
Both variants give you the linear index, means for pattern = [14 15 16]; it would return 4. To get the row indices you need an additional step:
[~,row_ind] = ind2sub(size(C),ind)
Another approach Using pdist2 instead of ismember and all from the other answer
pattern = [1 2 3];
out = cellfun(#(x) pdist2(x,pattern)==0,C);
ind = find(out)
Gives the same result as the other answer.

Array filter based on multiple columns

Suppose I have a 4 x n array:
A = [1 2 3 4; ...
2 4 8 9; ...
6 7 9 4; ...
1 8 3 4];
I want to filter the whole array based on the content of the first two columns.
For example, if I want to return array rows which contain a 2 in the first two columns, the answer I'm looking for isL
R = [1 2 3 4;...
2 4 8 9];
Or, if I want to return rows containing a 1 in the first two columns, the answer I'm looking for is...
A = [1 2 3 4;...
1 8 3 4];
I'm sure it's obvious but how can I do this in MATLAB? Filtering the whole array based on find or evaluation commands (e.g. A == 2) is totally fine. It's the filtering based on multiple columns in any order I can't figure out.
To check for a given number, just apply any along 2nd dimension restricted to the desired columns, and use that as a logical index to select the desired rows:
cols = [1 2]; %// columns to look at
val = 1; %// value to look for
R = A(any(A(:, cols)==val, 2), :);
If you want to look for several values, for example, select all rows that contain either 2 or 3 in columns 1 or 2: use ismember instead of ==:
cols = [1 2]; %// columns to look at
vals = [2 3]; %// values to look for
R = A(any(ismember(A(:, cols), vals), 2), :);
If you want to check if the numbers are within a range:
cols = [1 2]; %// columns to look at
v1 = 6; %// numbers should be greater or equal to this...
v2 = 8; %// ...and less than this
R = A(any(A(:, cols)>=v1, 2) & any(A(:, cols)<v2, 2), :);

Create all possible Mx1 vectors from an Nx1 vector in MATLAB

I am trying to create all possible 1xM vectors (word) from a 1xN vector (alphabet) in MATLAB. N is > M. For example, I want to create all possible 2x1 "words" from a 4x1 "alphabet" alphabet = [1 2 3 4];
I expect a result like:
[1 1]
[1 2]
[1 3]
[1 4]
[2 1]
[2 2]
...
I want to make M an input to my routine and I do not know it beforehand. Otherwise, I could easily do this using nested for-loops. Anyway to do this?
Try
[d1 d2] = ndgrid(alphabet);
[d2(:) d1(:)]
To parameterize on M:
d = cell(M, 1);
[d{:}] = ndgrid(alphabet);
for i = 1:M
d{i} = d{i}(:);
end
[d{end:-1:1}]
In general, and in languages that don't have ndgrid in their library, the way to parameterize for-loop nesting is using recursion.
[result] = function cartesian(alphabet, M)
if M <= 1
result = alphabet;
else
recursed = cartesian(alphabet, M-1)
N = size(recursed,1);
result = zeros(M, N * numel(alphabet));
for i=1:numel(alphabet)
result(1,1+(i-1)*N:i*N) = alphabet(i);
result(2:M,1+(i-1)*N:i*N) = recursed; % in MATLAB, this line can be vectorized with repmat... but in MATLAB you'd use ndgrid anyway
end
end
end
To get all k-letter combinations from an arbitrary alphabet, use
n = length(alphabet);
aux = dec2base(0:n^k-1,n)
aux2 = aux-'A';
ind = aux2<0;
aux2(ind) = aux(ind)-'0'
aux2(~ind) = aux2(~ind)+10;
words = alphabet(aux2+1)
The alphabet may consist of up to 36 elements (as per dec2base). Those elements may be numbers or characters.
How this works:
The numbers 0, 1, ... , n^k-1 when expressed in base n give all groups of k numbers taken from 0,...,n-1. dec2base does the conversion to base n, but gives the result in form of strings, so need to convert to the corresponding number (that's part with aux and aux2). We then add 1 to make the numbers 1,..., n. Finally, we index alphabet with that to use the real letters of numbers of the alphabet.
Example with letters:
>> alphabet = 'abc';
>> k = 2;
>> words
words =
aa
ab
ac
ba
bb
bc
ca
cb
cc
Example with numbers:
>> alphabet = [1 3 5 7];
>> k = 2;
>> words
words =
1 1
1 3
1 5
1 7
3 1
3 3
3 5
3 7
5 1
5 3
5 5
5 7
7 1
7 3
7 5
7 7
use ndgrid function in Matlab
[a,b] = ndgrid(alphabet)

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