MATLAB: index a cell array with cell array of arrays and return a cell array - matlab

Say I have a cell array of (n X 1) vectors, A, and a cell array of vectors containing indices into A, called B. I wish to extract a cell array, C, such that C{i} = [A{B{i}}].
In other words, I have a cell array of arrays of indices, and I want to pull out the matrices corresponding to the concatenations of the vectors in A indexed by each of those arrays of indices.
for i = 1:length(B)
%# B{i} is an array of indices, C{i} is a matrix
C{i} = [ A{ B{i} } ];
end
The loop is equivalent to:
C = cellfun(#(x)[A{x}],B,'UniformOutput',false); %# implicit for loop w/ closure
Can I do that using an indexing expression alone? Or at least without the loop?
I think deal() might have to be involved but can't figure it out.

Here are two alternative solutions:
Collect all the indices of B together with the function cell2mat, index the contents of A to make one large matrix, then divide that matrix up using the function mat2cell and the sizes of the index arrays in B:
N = size(A{1}); % Size of an array in A
M = cellfun('prodofsize', B); % Array of sizes of elements in B
C = mat2cell([A{cell2mat(B)}], N, M);
Here's a more compact version of your cellfun-based solution:
C = cellfun(#(x) {[A{x}]}, B);
Ultimately, I would decide what solution to use based on speed and readability, which may actually turn out to be your for-loop-based solution.

Try the following expression:
C = A(cell2mat(B))
You may have a look at Loren's blog post about Cell Arrays and Their Contents

Related

Adding three or more matrices in one command

For example, in Octave I can do the following:
A = randn(2);
B = randn(2);
C = randn(2);
plus(A, B, C)
This does not work in MATLAB however, because the plus function in MATLAB only allows two input arguments.
For context, I have a cell array full of large, sparse matrices and I need to add all of them together. I am looking for an efficient way to do this. For example, in Octave I would do the following:
D = {A, B, C};
plus(D{:})
But this doesn't work in MATLAB for the reason explained above.
If all matrices inside the cell array have the same size then you can concatenate each of them in a separate slice of a 3D array and then sum along the 3rd dimension i.e.
sum(cat(3,D{:}),3)
This is not valid if your cell array is a combination of scalars and matrices, or if you're doing implicit expansion with plus.
If the cell array has combination of matrices of different size then just use a loop. Loops have been significantly improved in the newer versions of MATLAB.
req = 0;
for k = 1:numel(D)
req = req + D{k}; %or bsxfun(#plus, req, D{k}) for < R2016b
end

Random permutation of each cell in a cell array

I have a 1-by-4 cell array, D. Each of the cell elements contains 2-by-2 double matrices. I want to do random permutation over each matrix independently which in result I will have the same size cell array as D but its matrices' elements will be permuted and then the inverse in order to obtain the original D again.
for a single matrix case I have the code and it works well as follows:
A=rand(3,3)
p=randperm(numel(A));
A(:)=A(p)
[p1,ind]=sort(p);
A(:)=A(ind)
but it doesn't work for a cell array.
The simplest solution for you is to use a loop:
nd = numel(D);
D_permuted{1,nd} = [];
D_ind{1,nd} = [];
for d = 1:nd)
A=D{d};
p=randperm(numel(A));
A(:)=A(p)
[~,ind]=sort(p);
D_permuted{d} = A;
D_ind{d} = ind;
end
Assuming your D matrix is just a list of identically sized (e.g. 2-by-2) matrices, then you could avoid the loop by using a 3D double matrix instead of the cell-array.
For example if you hade a D like this:
n = 5;
D = repmat([1,3;2,4],1,1,n)*10 %// Example data
Then you can do the permutation like this
m = 2*2; %// Here m is the product of the dimensions of each matrix you want to shuffle
[~,I] = sort(rand(m,n)); %// This is just a trick to get the equivalent of a vectorized form of randperm as unfortunately randperm only accepts scalars
idx = reshape(I,2,2,n);
D_shuffled = D(idx);

Matlab Mean over same-indexed elements across cells

I have a cell array of 53 different (40,000 x 2000) sparse matrices. I need to take the mean over the third dimension, so that for example element (2,5) is averaged across the 53 cells. This should yield a single (33,000 x 2016) output. I think there ought to be a way to do this with cellfun(), but I am not able to write a function that works across cells on the same within-cell indices.
You can convert from sparse matrix to indices and values of nonzeros entries, and then use sparse to automatically obtain the sum in sparse form:
myCell = {sparse([0 1; 2 0]), sparse([3 0; 4 0])}; %// example
C = numel(myCell);
M = cell(1,C); %// preallocate
N = cell(1,C);
V = cell(1,C);
for c = 1:C
[m n v] = find(myCell{c}); %// rows, columns and values of nonzero entries
M{c} = m.';
N{c} = n.';
V{c} = v.';
end
result = sparse([M{:}],[N{:}],[V{:}])/C; %'// "sparse" sums over repeated indices
This should do the trick, just initialize an empty array and sum over each element of the cell array. I don't see any way around using a for loop without concatenating it into one giant 3D array (which will almost definitely run out of memory)
running_sum=zeros(size(cell_arr{1}))
for i=1:length(cell_arr)
running_sum=running_sum+cell_arr{i};
end
means = running_sum./length(cell_arr);

How to transpose a cell of Mx1 cells and vectors?

I have a 1xN cell, call it X, whose components X{i} (for i in {1, 2, ..., N}) are either Mx1 cells of strings or Mx1 numeric vectors.
NOTE: the fact that X contains both text cells and numeric vectors precludes using cell2mat here:
>> tmp = cell2mat(X);
Error using cell2mat (line 46)
All contents of the input cell array must be of the same data type.
My question is:
What's MATLAB's "idiomatic" way to transpose this data into an array of M 1xN cells?
EDIT: To be clear, the data structure I want to arrive at, let's call it Y, is an M-long array of 1xN cells (each consisting of a mixture of numbers and strings). E.g., if N=2, and if X{1} is an Mx1 cell of strings, and X{2} is an Mx1 vector of doubles, then the desired data structure Y is such that, for any 1 &leq; i &leq; M, Y(i,:) is a 1x2 cell whose first element is the i-th string in X{1} and whose second element is the i-th double in X{2}. I.e. Y(i,:) would be the same as the 1x2 cell Yi defined as follows
xi1 = X(i, 1);
xi2 = X(i, 2);
Yi = {xi1{1} xi2{1}};
(Sorry for the awkwardness! I just can't find a MATLAB expression for Yi directly in terms X and i, without having to create intermediate variables xi1 and xi2.)
First of all, to my knowledge there is no 'idiomatic' way of doing that i Matlab. Remember, we're talking about a complex data structure with nested cells and differing types.
I tried to cook up a solution based on cellfun. It quickly got complicated and I didn't even succeed. So instead I would recommend doing a simple double for loop and a if like this:
for a=1:size(X,2),
for b=1:size(X{a},1),
if iscell(X{a}),
Y{a,b} = X{a}{b};
else
Y{a,b} = X{a}(b);
end
end
end
If X = {{'s1';'s2'} [3; 4]} then this solution will give Y = {'s1' 3 ;'s2' 4}.
As you see I have flattened the cell to one depth. To get your 1xN vectors, do Y{1,:}
Hope it helps
If each cell in X contains a vector of the same size convert it to a matrix, transpose that matrix and then convert it back to a cell array?
tmp = cell2mat(X);
Y = mat2cell(tmp', ...);
Here is the function:
feval(#(y) feval(#(x) cellfun(#(varargin)[varargin],x{:},'un',0), cellfun(#(x) feval(#(varargin) varargin{3-varargin{1}}(), iscell(x),x,num2cell(x)),y,'un',0)), {{'1','2','3'},[4 5 6],{7,8,9}})
Or stored as a function name:
transpose_nest = #(cell_nest) feval(#(y) feval(#(x) cellfun(#(varargin)[varargin],x{:},'un',0), cellfun(#(x) feval(#(varargin) varargin{3-varargin{1}}(),iscell(x),x,num2cell(x)),y,'un',0)), cell_nest);
transpose_nest({{'1','2','3'},[4 5 6],{7,8,9}})
{{'1',4,7},{'2',5,8},{'3',6,9}}
It is based on a Matlab equivalent of Scheme's (apply map list '(("1" "2" "3") (4 5 6) (7 8 9))) that only works on sub cells:
feval(#(x) cellfun(#(varargin)[varargin],x{:},'un',0), {{'1','2','3'},{4,5,6},{7,8,9}})
{{'1',4,7},{'2',5,8},{'3',6,9}}
The extra part:
#(x) feval(#(varargin) varargin{3-varargin{1}}(), iscell(x), x, num2cell(x))
is an if statement within an anonymous function that calls num2cell if any sub element is a vector

How can I extract n elements from each m-by-m window of a matrix without using loops in MATLAB?

I have a matrix and 2 parameters. The first parameter n is the number of elements to be selected. The second is the window size m.
I want to select n number of elements from every m-by-m window from the matrix. As a result we will have a p-by-q cell array where p is matrix_height/m and q is matrix_width/m.
Each element of the cell array contains the n greatest numbers from the corresponding window. It does not necessarily need to be a cell array, it can be anything that will store the necessary data.
One way to do this is by first finding all the unique m-by-m submatrices of your matrix using the function IM2COL, then sorting each column in descending order using the function SORT, and finally extracting the top n rows. If your initial matrix is A and your output matrix is B, this is what it would look like:
B = sort(im2col(A,[m m],'distinct'),1,'descend');
B = B(1:n,:); %# Get the top n values
Note that B will be an n-by-m^2 matrix. If you want to turn this into a p-by-q cell array, you can do this using the functions NUM2CELL and RESHAPE:
nBlocks = ceil(size(A)./m); %# The number of blocks in each dimension
B = reshape(num2cell(B,1),nBlocks(1),nBlocks(2));
EDIT:
If you also want to get the indices of each value with respect to the input matrix A, that is a bit more complicated. You can do it by getting the second output from SORT, which in this case will be the linear indices of the values within each m-by-m submatrix. You can convert these to subscripts using the function IND2SUB, then shift the row and column indices to account for the position of each m-by-m block:
[B,index] = sort(im2col(A,[m m],'distinct'),1,'descend');
B = B(1:n,:); %# Get the top n values
index = index(1:n,:); %# Get the top n values
[r,c] = ind2sub([m m],index); %# Convert linear indices to subscripts
nBlocks = size(A)./m; %# The number of blocks in each dimension
r = r+repmat(0:m:(nBlocks(1)-1)*m,n,nBlocks(2)); %# Shift the row indices
c = c+kron(0:m:(nBlocks(2)-1)*m,ones(n,nBlocks(1))); %# Shift the column indices
And now you can collect the row indices, column indices, and values together into a cell array using the functions MAT2CELL and RESHAPE:
B = mat2cell([r(:) c(:) B(:)],n.*ones(1,size(B,2)));
B = reshape(B,nBlocks(1),nBlocks(2));
Alternatively, you can create a structure array instead of a cell array using the functions NUM2CELL, STRUCT, and RESHAPE:
B = struct('rowIndices',num2cell(r,1),...
'colIndices',num2cell(c,1),...
'values',num2cell(B,1));
B = reshape(B,nBlocks(1),nBlocks(2));
NOTE:
The function IM2COL will pad partial blocks with zeroes in the event that a dimension of A is not an even multiple of m. If any of this zero-padding appears in the top n values for a block, one or both of the corresponding row and column indices will be out of range (i.e. have a value larger than the size of that dimension for the matrix A). Thus by checking that the row and column indices are in range you can make sure you are not including any of the zero-padding in your subsequent analysis.