Loren on the Art of MATLAB

October 21st, 2009

Dealing with Cells

A customer recently asked me this question at the MATLAB Virtual Conference.

Contents

Question about Summing Cell Rows

  I was hoping you would cover cells some day. Here is a particular
  problem I was hoping to have a more elegant solutions for.
  A is a cell that has String (say names) in the first column, Numbers
  (say scores in tests 1 and 2) in the next two columns. Assume that each
  cell has only one value. Is there an easier way to calculate, say the
  sum of the two test scores than a for loop?

Example

Let's make some sample data.

c(1:4,1) = {'Fred' 'Alice' 'Lucy' 'Tom'};
c(1:4,2) = { 90  80  55 102 };
c(1:4,3) = { 43  91  80  44 }
c = 
    'Fred'     [ 90]    [43]
    'Alice'    [ 80]    [91]
    'Lucy'     [ 55]    [80]
    'Tom'      [102]    [44]

Answer

To sum the entries in a row for this cell array, I can simply turn the numeric values into a numeric array via comma-separated list notation, and then sum the rows. Let's see the pieces for clarity. Here's the comma-separated list.

c{:,2:3}
ans =
    90
ans =
    80
ans =
    55
ans =
   102
ans =
    43
ans =
    91
ans =
    80
ans =
    44

Now place the results into a vector.

vec = [c{:,2:3}]
vec =
    90    80    55   102    43    91    80    44

Reshape the vector appropriately into a matrix.

array = reshape(vec,[],2)
array =
    90    43
    80    91
    55    80
   102    44

Now sum the results along the rows.

tot = sum(array,2)
tot =
   133
   171
   135
   146

Or, in one bigger statement, try this.

tot = sum(reshape([c{:,2:3}],[],2),2)
tot =
   133
   171
   135
   146

Cell Array Questions

Do you use cell arrays? Can you do what you want without undo contortions? Let me know here.


Get the MATLAB code

Published with MATLAB® 7.9

30 Responses to “Dealing with Cells”

  1. Tomasz Malisiewicz replied on :

    You can also do:

    sum(cell2mat(c(:,2:3)),2)

    -Tomasz

  2. matt fig replied on :

    This is not as easily generalizable, but maybe simpler for this simple case:

    tot = cellfun(@plus,c(:,2),c(:,3))

    I use cell arrays all the time, and yes, sometimes there is a lot of coding acrobatics that takes place when dealing with them.

  3. us replied on :

    dear loren

    almost everything can be done using CELLFUN - although, this approach does not always yield the fastest solution…

    totc=cellfun(@(x,y) sum([x,y],2),c(:,2),c(:,3));

    also, one should not forget the CAT command, which is slightly more versatile than the […] solution…

    just a thought…
    best
    urs

  4. Loren replied on :

    Thanks for the other solutions. They are nice and work just fine.

    Personal preference: I prefer to not use cellfun when there are other ways that aren’t too tortuous. Likewise with cell2mat, vs. extracting the values syntactically. Maybe it’s because the next thing I often need to do requires more generalization. Or maybe I’m being perverse and the other ways are more readable/maintainable. It’s possible that there might be performance differences though they won’t matter here much if the arrays are small.

    Chacun à son goût,
    –Loren

  5. Dan C. replied on :

    I’ve been working with cells more and more to use the xlswrite function. Is there a quick way to concatenate values from a matrix onto an existing cell? Say, for instance, I wanted to add another column from the values of

    m=[1;2;3;4];

    to the cell array c in your post.

  6. Loren replied on :

    Dan C-

    Try this:

    c(1:4,1) = {'Fred' 'Alice' 'Lucy' 'Tom'};
    c(1:4,2) = { 90  80  55 102 };
    c(1:4,3) = { 43  91  80  44 }
    m=[1;2;3;4]
    cm = mat2cell(m,ones(size(m)),1)
    c(:,end+1) = cm
    

    –loren

  7. OysterEngineer replied on :

    Loren,

    I like this alternate syntax instead of your last line of code to Dan C.

    (I hope I get the

     tags proper)
    
    c = [c, cm];
    

  8. Kelly replied on :

    I’ve always wished that cell2mat had the equivalent syntax as mat2cell or num2cell, i.e. being able to combine cell arrays along only one dimension, or into specifically-sized new cells. For example, take a 3 x 2 cell array, with each cell holding a 4 x 5 array, and concatenate along rows only into a 3 x 1 cell array with 4 x 10 elements.

    I’ve written a little function for the task since I need it so often (you can do it with a few loops pretty easily).

  9. Pekka Kumpulainen replied on :

    I use cells whenever I feel they are necessary/helpful. That is quite often. Sometimes things do get tricky though.
    In order to avoid unnecessary acrobatics I try to store my data so that it is easy to use. The data in the example above I would save in a struct of a cell and a matrix:
    c =
    name: {’Fred’ ‘Alice’ ‘Lucy’ ‘Tom’}
    data: [2×4 double]

    This is actually how I arrange most of my data, sometimes 3-d matrices and additional fields or whatever. But I try to keep the numerical data in matrices, the MATLAB way.

  10. Greg S. replied on :

    Hi,

    I find deal() can normally solve problems like this, leaving code looking cleaner but perhaps a bit obscure, e.g.

    [c{:,end+1}] = deal(m)
    
  11. Loren replied on :

    OysterEngineer-
    Your code works well too.

    Kelly-
    I hope you’ve placed your code on the File Exchange!

    Pekka-
    Good point about the data, but I was given the form of the dataset initially. People should definitely think about how to best store the data early in the development process.

    Greg S.-
    I don’t think deal is doing what you want here. It is placing the entire array m in each cell in the final column of c.

    –Loren

  12. Gary replied on :

    Thank you for addressing cells. I always feel a little lost whenever I try to use them.

  13. Mark Mikofski replied on :

    Loren,

    I’m looking for an elegant, and generalized way to sum each cell in a cell array of different sized matrices, without looping. Surely there must be a way?
    e.g.:

     a = {rand(3,1),rand(7,1),rand(5,1)} 

    creates a 3 cell array with different length matrixes. how can I find the sum of each of theses cells in one command that returns a 3×1 matrix with the sum of each cell as an element in the vector?

  14. Loren replied on :

    Mark-

    Take a look at cellfun and think about using the function sum. Like this…

    >> a = {rand(3,1),rand(7,1),rand(5,1)} ;
    >> cellfun(@sum,a,'UniformOutput',true)
    ans =
        1.8475    4.3911    3.3710
    

    –Loren

  15. Kishore replied on :

    Hi Loren,

    Why does the following not work?

    
    data_classwise = 
    
        [19x121 double]    [19x134 double]    [19x84 double]    [19x107 double]
    
    c = cell2mat(data_classwise);
    
    mat2cell(cc,[19 121],[19 134],[19 84],[19 107])
    ??? Error using ==> mat2cell at 96
    Number of input vector arguments, 4, does not match the input matrix's number of dimensions, 2.
    

    I am looking at converting a cell( 4, 2 dimensional matrixes) to a single matrix, and then covert this back to cells using mat2cell().

    2) If i need to know the size of the individual matrices within the cell can i use cellfun() for this?

    Thanks!

  16. Kishore replied on :

    sorry, in the previous code
    mat2cell(c,[19 121],[19 134],[19 84],[19 107])

  17. Loren replied on :

    Kishore-

    It is not clear to me what you are trying to actually achieve. If you want to concatenate the 4 cells, you can do this.

    mymatrix = cat(2, data_classwise {:});
    whos mymatrix
      Name        Size             Bytes  Class     Attributes
    
      mymatix    19x446            67792  double
    

    If this is not what you want, I suggest you read the error message carefully and look at the help for mat2cell carefully as well.

    –Loren

  18. Sumedh replied on :

    Hi Loren,

    I was wondering if there was a way to pre-allocate cell arrays for iteration. I’ve been playing around with this for a while and haven’t figured out a way to do it.

  19. Loren replied on :

    Sumedh-

    It depends what you want to preallocate the cells to. You can use deal, e.g., to initialize all cells to the same value.

    [c{1:5}] = deal(initialValue);
    

    You can also use deal to initialize the cells to different values, using a cell array as a comma separated list as the input to deal.

    There are other ways. I suggest you read more of the blogs in the category of cell arrays. Variants for initializing the cells are covered in them.

    –Loren

  20. Kishore replied on :

    Thanks Loren, found a way out.

  21. Sumedh replied on :

    Thanks Loren, “deal” is what I was looking for.

  22. Jun replied on :
    Question:
    Array1={'AA','AB','BB','BA'}; It has unique element.
    Array2={'AA','AB','AA','BA','BA','BB','AB'}; All its elements come from Array1.
    
    I want to get a vector whose length is same as Array2,and its contents are the index of elements in Array1, and its every element corresponds to the elements in Array2. 
    
    For the above two arrays, my result vector should be like
    [1 2 1 4 4 3 1].
    
    How can I get it without using loop? Thank you very much!!!
    
  23. Jun replied on :
    
    Sorry,For the above two arrays, my result vector should be like
    [1 2 1 4 4 3 2].
    
  24. Loren replied on :

    Jun-

    ismember is your friend here:

    >> [aa,ind] = ismember(Array2,Array1)
    aa =
         1     1     1     1     1     1     1
    ind =
         1     2     1     4     4     3     2
    

    –Loren

  25. Jun replied on :
    I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
    
  26. abc replied on :

    Hi Loren,
    I am learning to use cells.

    I was wondering if it is possible to write the following small code without using for loop. Thanks a lot.

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % each element of ROI and cor is 128×128
    for i=1:47
    ROI{i} = logical(cor{i}) ;
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  27. abc replied on :

    Sorry for the my previous post, the code was not correct. here is the correct code

    % each element of ROI, tar and cor is 128×128
    for i=1:47*2
    if i<=48
    ROI{i} = logical(cor{i}) ;
    else
    ROI{i} = logical(tar{i-47}) ;
    end
    end

  28. Loren replied on :

    ABC-

    I don’t understand why the data you have is in cells in the first place and what the range of valuesis. Why aren’t they in a numeric array (3-D, i.e., 128×128xP)? I think they’d be much easier to deal with there. Is P 47^2?

    Second, why do you want to write this without a for loop? You can generally write most cell operations using cellfun instead, but it’s not necessarily faster, and sometimes slower at the moment.

    Why not have 2 for loops, one from 1-48, and one from 49-94? That would cover the cases in your if statement, I believe. Or two correctly indexed expressions using cellfun?

    I haven’t tried it, but something like this might work. I think I have the indexing a bit wrong - I didn’t look at that part carefully.

    anon = @(x) logical(x);
    ROI = cellfun(anon,cor(1:48),'UniformOutput',false);
    ROI(49:94) = cellfun(anon, tar(2:47),'UniformOutput',false);
    

    –Loren

  29. Lirave replied on :

    Hello,
    i have a cell array “test” with

    test = {[1,2,3],[5,6],[2,3],[7,6],[2,3],[4,4];
    [1,2,4],[1,1],[2,3],[4,5],[3,4],[];
    [1,3,4],[2,3],[4,4],[4,4],[5,3],[2,3];
    }

    Now i want that each entry in a row is unique and that all empty cells located at the end of a row. In that example it should afterwards look like :

    test = {[1,2,3],[5,6],[2,3],[7,6],[4,4],[];
    [1,2,4],[1,1],[2,3],[4,5],[3,4],[];
    [1,3,4],[2,3],[4,4],[5,3],[],[];
    }

    I have found a lot of usefull commands but it doesn´t work.

    I hope you can help me.

    Lirave

  30. Loren replied on :

    Lirave-

    I would transpose the first test array, run unique on each column and fill the end values or each column with []. I would make sure I preallocated space for the result, and then I would transpose the array back.

    –Loren

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Loren: Dirga- You might consider looking at the function interp2. –loren
  • Dirga: I do not know whether the term I used is correct or not. I have set of measurement data. It is the density of...
  • Loren: Alex- You could overload sort in the same way that you create any class method. That would get you the shorter...
  • Alan Hester: can you put infomation in the interlaced images that the subconscious will pick up on and remember that...
  • Andrew Kraev: Hello, here is simple code to calculate for an optional vector a[i], i=1,..,n the sum by i of products,...
  • Alex: It would be nice to be able to sort an array of objects of a custom class, provided the class implements lt()...
  • Stu: Excellent post. No, I wasn’t aware of that property of for...end structures. Thank you!!
  • Loren: Eleanor- You either need an algorithm to determine which 0s should be replaced by NaN values or you will need...
  • Eleanor: Loren- I have a different sort of problem with NaNs and 0s. My data has zeros where data is missing, but...
  • Aslak Grinsted: Here’s another fun piece of code inspired by cellular automata. This one generates what looks...

These postings are the author's and don't necessarily represent the opinions of The MathWorks.