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

25 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!
    

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.

  • Jun: I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
  • Loren: Wow folks- Always lots of interest when there’s a quickie to try out! I will only make 2 general...
  • Loren: Jun- ismember is your friend here: >> [aa,ind] = ismember(Array2,Arra y1) aa = 1 1 1 1 1 1 1 ind = 1 2 1 4 4 3...
  • Dan: I like the first way better than the second way. Combining the arrays into one and running any is nice, although...
  • James Myatt: How about I = (a == 0 | b == 0); a(I) = []; b(I) = [];
  • Tunc: Hello Loren, love your blog because of such inspiring and challenging comments to such ’small’...
  • Pekka Kumpulainen: Here is my tradeoff. I usually want to keep the original variables as they are most probably...
  • Iain: Followup: Of course, to allow NaNs (counting them as non-zero): mask = (a~=0) & (b~=0); The mask says “a...
  • Matt Fig: I would usually go with something like this: y = a&b; x = a(y); y = b(y); But I was surprised to find...
  • kk: c=all([a;b]) a(c) a(b)

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