Reverse lookup in MATLAB cell array of indices - matlab

I have hit a brick wall trying to solve this:
Given a 5x1 cell array of vectors of indices to an array of n elements I need to find the reverse mapping.
What I have is the relation "In group 2, there are elements 15, 16, 17,...."
What I want to have is "Element 15 is member of group 2,4,5."
This is the structure of my cell array
myCellArray =
[1x228 double]
[1x79 double]
[1x136 double]
[1x93 double]
[1x81 double]
This is part of the contents of my index vector
myCellArray{2}(1:5) =
15 16 17 18 19
What I want is a cell array of n cells containing the indices of group membership for each element.
help?

You can do this with a combination of cellfun and arrayfun. First create a cell array:
>> mycellarray = { [1 2], [4 5], [3 4], [1 2 3 4 5] };
To get the elements of the cell array that contain a particular number (say 1) you can use cellfun:
>> find( cellfun(#(s)ismember(1, s), mycellarray) )
ans =
1 4
Which tells you that 1 is in the 1st and 4th elements of mycellarray. Now you can just map this over the list of all possible indexes using arrayfun. The arrays that are produced might be of different length, so we need to set 'UniformOutput' to false.
>> n = 5;
>> result = arrayfun(#(i)find(cellfun(#(s)ismember(i,s), mycellarray)), 1:n, ...
'UniformOutput', false);
The elements are the index vectors that you want --
>> result{1}
ans =
1 4 # since 1 is in the 1st and 4th array
>> result{3}
ans =
3 4 # since 3 is in the 3rd and 4th array

Do you have to use cell arrays to save space?
Otherwise can you change your current matrix to an MxN normal matrix where N is n as you have defined and M is the number of groups. And then just pad the end of each row with zeros. So it holds the same information but it makes your reverse query easy to do using find.
so if n = [1 2 3 4 5 6 7]
and we have 3 groups such that group 1 is [1 4 5], group 2 is [3] and group 3 is [1 2 6 7] your current matrix would be
M = 3; N = numel(n);
m = zeros(M,N);
m(1, 1:3) = [1 4 5];
m(2, 1) = 3;
m(3, 1:4) = [1 2 6 7];
now you want to know which group does the number i belong to. It's as simple as (updated based on Chris Taylor's observation)
find(any(m == i, 2))

Related

how to find the order of elements in array in Matlab?

If I have an array of A = [10 1 5 20] I want a program to find indexes of the elements. In this case Idx = [3 1 2 4]. I am using [~,Idx]=sort([10 1 5 20]) and get the following:
Idx =
2 3 1 4
It's totally not what I expected. I even don't understand how the program got those numbers.
It is simple:
A = [10 1 5 20];
[~, Idx] = sort(A);
[~, orders] = sort(Idx);
% orders
% [3 1 2 4]
orders is your answer. You need to get the indices of sorted Idx.
Note that Idx(i) represents the index of i-th element in the original array A.

Extract values from 2d array with indices from another array (without loops)

I have an array [2; 3] and a matrix [ 1 3 4 5; 2 4 9 2].
Now I would like to extract the second element from the first row and the third element from the second row and thus obtain [3 ; 9]. I managed it to do it with a loop, but since I'm working with much larger arrays, I would like to avoid these.
You can use sub2ind to convert each of the column subscripts (along with their row subscripts) into a linear index and then use that to index into your matrix.
A = [1 3 4 5; 2 4 9 2];
cols = [2; 3];
% Compute the linear index using sub2ind
inds = sub2ind(size(A), (1:numel(cols)).', cols);
B = A(inds)
% 3
% 9
Alternately, you could compute the linear indices yourself which is going to be more performant than sub2ind
B = A((cols - 1) * size(A, 1) + (1:numel(cols)).');
% 3
% 9
By exploiting the diag function, you can obtain an elegant one-line solution:
A = [1 3 4 5; 2 4 9 2];
cols = [2; 3];
B = diag(A(:,cols))
% 3
% 9
Here is what diag(A(:,cols)) does:
A(:,cols) selects the columns cols of A, with column k of A(:,cols) corresponding to the column cols(k) of A, giving [3 4; 4 9];
diag returns the diagonal entries of this matrix, thus returning at position k the k-th diagonal element of A(:,cols), which is A(k,cols(k)).

Create Non Zero elements of Matrix in vector form in Matlab

I have a Matrix of size M by N in which each row has some zero entries. I want to create M row vectors such that each of the vector contains the non zero elements of each row. For example if I have the following Matrix
A=[0 0 0 5;0 0 4 6;0 1 2 3;9 10 2 3]
I want four different row vectors of the following form
[5]
[4 6]
[1 2 3]
[9 10 2 3]
This can be done with accumarray using an anonymous function as fourth input argument. To make sure that the results are in the same order as in A, the grouping values used as first input should be sorted. This requires using (a linearized version of) A transposed as second input.
ind = repmat((1:size(A,2)).',1,size(A,2)).';
B = A.';
result = accumarray(ind(:), B(:), [], #(x){nonzeros(x).'});
With A = [0 0 0 5; 0 0 4 6; 0 1 2 3; 9 10 2 3]; this gives
result{1} =
5
result{2} =
4 6
result{3} =
1 2 3
result{4} =
9 10 2 3
Since Matlab doesn't support non-rectangular double arrays, you'll want to settle on a cell array. One quick way to get the desired output is to combine arrayfun with logical indexing:
nonZeroVectors = arrayfun(#(k) A(k,A(k,:)~=0),1:size(A,1),'UniformOutput',false);
I used the ('UniformOutput',false) name-value pair for the reasons indicated in the documentation (I'll note that the pair ('uni',0) also works, but I prefer verbosity). This input produces a cell array with the entries
>> nonZerosVectors{:}
ans =
5
ans =
4 6
ans =
1 2 3
ans =
9 10 2 3

Transfering elements of 2D array into cell array

Is there a way to instantly transfer elements of an n x n numerical array into an n x n cell array and vice versa so that each cell in the cell array has element's row, column, and value? E.g.,
Input:
A=[8 7 8 4 5;
7 0 7 4 4;
4 3 3 8 6;
7 0 10 8 7;
2 1 0 2 8;];
B=cell(5,5);
Output:
B{1}=[1 1 8];
B{2}=[2 1 7];
B{3}=[3 1 4];
B{4}=[4 1 7];
B{5}=[5 1 2];
B{6}=[1 2 7];
and so on...
Here's one approach:
dim=length(A); %//square matrix
cols = repmat(1:dim,dim,1);
rows = cols';
B=reshape(num2cell([rows(:) cols(:) A(:)],2),dim,dim);
You could wrap this piece of code in a function if you are going to use it often, to "instantly" transfer the elements of the matrix to the cell array.
Maybe using arrayfun
[row, col] = ndgrid(1:size(A,1));
B=arrayfun(#(x,y,z) [x y z], row(:), col(:), A(:), 'uni', 0);
Interesting point is that if you use
B=arrayfun(#(x,y,z) [x y z], row, col, A, 'uni', 0);
you get cell array B with the same size as A where each element is in its corresponding element in A.

Matlab - find function between matrix and cell array

I’ve a matrix A = (4*4) and a cell array B {4,1}. I’d like to find all the values of B in A, searching row by row and after I’d like to delete the correspondent column associated to this particular value. I’ve a problem using bsxfun o cellfun and find function with a matrix and a cell array. I‘ve tried to convert the cell array into a matrix but I don’t have more the exact correspondence.
For example:
A =
1 5 10 23
2 4 2 18
3 3 5 14
1 9 10 4
B =
1
2 4
3 3 14
1
To obtain:
C =
10
2
5
10
Thanks in advance,
L.
Here's how:
C = cellfun(#(x, y){sparse(1,find(ismember(x,y),numel(y)),true,1,size(A,2))}, ...
mat2cell(A, ones(size(A, 1), 1), size(A, 2)), B(:));
C = A(:, all(~vertcat(C{:})));
The cellfun is fed with two cell arrays: the first one contains the rows of A and second one is B. The anonymous function is the tricky part; it operates on a pair of two corresponding rows as follows:
It employs ismember to check which columns in A contain any of the elements in B.
It uses find to pick only the first N ones, with respect to the number of elements in the B.
It uses sparse as a fancy way of zeroing out the rest of the elements.
For your example it would look like this:
A = [1 5 10 23; 2 4 2 18; 3 3 5 14; 1 9 10 4];
B = {1; [2 4]; [3 3 14]; 1};
C = cellfun(#(x, y){sparse(1,find(ismember(x,y),numel(y)),true,1,size(A,2))}, ...
mat2cell(A, ones(size(A, 1), 1), size(A, 2)), B(:));
which yields:
C =
{
[1 0 0 0]
[1 1 0 0]
[1 1 0 1]
[1 0 0 0]
}
After that, it's a simple matter of logical indexing to pick the resulting columns:
C = A(:, all(~vertcat(C{:})));
which in this case should be:
C =
10
2
5
10