5

I have a question regarding the cellfun function in MATLAB.

When / why should I use it, and when may I just as well drop it?

A simple example: Let's say I have a cell a, and I want to find the average of all values in a.

a{1} = [1 2;3 4];
a{2} = [1 2 3;4 5 6; 7 8 9];

My approach would be something like:

mean([a{1}(:); a{2}(:)])

What will the appropriate cellfun version of this be, and is it any better?

I've tried doing something like this, (obviously not working):

mean_a = mean(cellfun(@mean, a,'UniformOutput',0))

Thanks!

Stewie Griffin
  • 14,889
  • 11
  • 39
  • 70
  • 1
    relevant question: http://stackoverflow.com/questions/16143314/matlab-arrayfun-cellfun-spfun-and-structfun-vs-simple-for-loop – Shai Jun 02 '13 at 12:20

4 Answers4

4

It of course depends in what you want to do. cellfun is intended to act separately on each cell in your cell array. If you want to take a global mean of your cell array values, and insist on using cellfun then this should work:

mean_a = mean(cell2mat(cellfun(@mean, a,'UniformOutput',0)))
4

cellfun is just like looping through the cell matrix and
executing specified function separately on each cell.
It's usually faster than doing the same thing explicitly
in loop, but the basic difference is that its easy to
write and read - its immediately clear what the call is
doing. But you could just as well write the loop yourself.

In your specific case you could use cellfun this way:

mean_a = mean(cellfun(@(x) mean(x(:)), a));

If you have thousands of cells and you want to do something
to each of them you either use a loop or a cellfun
BTW: @(x) means that you want the content of each cell
to be understood as x so that mean(x(:)) gives you what
you want - mean of the whole matrix content of the cell.

mmagnuski
  • 1,249
  • 1
  • 11
  • 23
3

Based on your attempted solution, I imagine that the answers given will solve your problem. However, it appears to me your solution will weight some values more than others and may not be valuable to all readers.
Using your matrices,

mean([a{1}(:); a{2}(:)]) ~= mean([mean(a{1}(:)) mean(a{2}(:))])
                  4.2308 ~= 3.75

This is the case where numel(a{1}) ~= numel(a{2}).

The accepted solution is equivalent to the right side of the above equation, but the original implementation is (obviously) equal to the left side. Either could be correct given your needs.

For balance, I offer one (of many) ways to accomplish a non-weighted mean of all the elements of your cell by reshaping each matrix to a column array and concatenating them:

b = cellfun(@(x) reshape(x, 1, []), a, 'UniformOutput', false);
mean_a = mean([b{:}])
  • Welcome to SO! Please edit the answer so that it is clear what attempted solution/answer you are talking about. The order in which they are shown will not always be the same. Once yours is positioned between other answers, the readers will wonder what you are talking about. (I can not make any statement on the contents of your or others' anwers) – Stefan Hegny Jul 11 '16 at 20:57
1

I love using cellfun for plotting operations instead of looping, for example if I have multiple sets of sensor data and each set has multiple columns (because of multiple sensors per set) it's very convenient using

numOfSensors = 5;
numOfSets = 6;

%% sample data preparation
x = 1:100;
y = rand(length(x), numOfSets*numOfSensors);
yCell = mat2cell(y, 100, numOfSensors*ones(1,numOfSets)); % this is my sensor data
scaleCell = num2cell(fliplr(cumsum(1:numOfSets)));
yCell = cellfun(@(x, scale)x.*scale, yCell, scaleCell, 'unif', false);

%% plot preparation
nameCell = arrayfun(@(x)['sensor set ' num2str(x)], 1:numOfSets, 'unif', false);
colorCell = num2cell(lines(numOfSets), 2)';

%% plot
figure, hold all,
set(gca, 'ColorOrder', [0 0 0], 'LineStyleOrder', {'-','--','-*','-.',':'})
h = cellfun(@(y, name, c)plot(x, y, 'linewidth', 1.5, 'displayName', name, 'color', c), yCell, nameCell, colorCell, 'unif', false);
hh = cellfun(@(x)x(1), h, 'unif', false);
legend([hh{:}])

instead of looping. This example plots all datasets, each dataset in it's own color and each sensor per dataset with an other linestyle. The legend is displayed for each data set only (note: this could also be done by using hggroups).

Or a simpler using case - I have a cell array of data again and want to have a short view at it:

figure, hold all, cellfun(@plot,dataCell)

That's it, one line, very fast in the command line.

Another great use case is compressing higher dimensional data numerical data by using mean(), max(), min(), std() and so on, but you've mentioned this already. This gets even more important if the data is not of uniform size.

Jan
  • 310
  • 1
  • 7