Search code examples
arraysmatlabloopsvectorizationcell-array

Fast way to store different length Matrix rows into Cell


I have three matrices:

Values = [200x7] doubles
numOfStrings = [82    78    75    73    72    71    70] %(for example)
numOfColumns = [1 4] 

The numOfColumns may contain any set of distinct values from 1 to 7. For example [1 2 3] or [4 7] or [1 5 6 7]. Obviously, biggest numOfColumns that can be is [1 2 3 4 5 6 7].

numOfColumns show the columns I want to get. numOfStrings shows the rows of that columns I need. I.e. in my example I want to get columns 1 and 4. So from the 1 column I want to get the 82 first rows and from the 4th get the 73 first rows.

i.e. if numOfColumns = [1 4] then

myCell{1} = Values( 1:82,1); % Values(numOfStrings(numOfColumn(1)), numOfColumn(1))
myCell{2} = Values( 1:73,4); % Values(numOfStrings(numOfColumn(2)), numOfColumn(2))

P.S. That's not necessary to save it into cell array. If you can offer any another solution Ill be grateful to you.


I'm looking for the fastest way to do this, which is likely to be by avoiding for loops and using vectorization.

I think a lot about sub2ind function. But I can't figure out how to return arrays of the different size! Because myCell{1} - [82x1] and myCell{2} - [73x1]. I suppose I can't use bsxfun or arrayfun.


RESULTS:

  1. Using for loops alike @Will 's answer:

    for jj = 1:numel(numOfColumns)
        myCell{rowNumber(numOfColumns(jj)),numOfColumns(jj)} = Values( 1:numOfStrings(numOfColumns(jj)),numOfColumns(jj));
    end
    

    Elapsed time is 157 seconds

  2. Using arrayfun alike @Yishai E 's answer:

    myCell(sub2ind(size(myCell),rowNumber(numOfColumns),numOfColumns)) = arrayfun( @(nOC) Values( 1:numOfStrings(nOC),nOC), numOfColumns, 'UniformOutput', false);
    

    Elapsed time is 179 seconds

  3. Using bsxfun alike @rahnema1 's answer:

    idx = bsxfun(@ge,numOfStrings , (1:200).');
    extracted_values = Values (idx);
    tempCell = mat2cell(extracted_values,numOfStrings);
    myCell(sub2ind(size(myCell),rowNumber(numOfColumns),numOfColumns)) = myCell(numOfColumns)';
    

    Elapsed time is 204 seconds

SO, I got a lot of working answers, and some of them are vectorized as I asked, but for loops still fastest!


Solution

  • % Get number of elements in NUMOFCOLUMNS
    n = numel(numOfColumns);
    
    % Set up output
    myCell = cell(1,n);    
    
    % Loop through all NUMOFCOLUMNS values, storing to cell
    for i = 1:n
    
        myCell{i} = Values(1:numOfStrings(numOfColumns(i)), numOfColumns(i));
    
    end 
    

    Which for your example gives output

    myCell = 
    
    [82x1 double]    [73x1 double]