Calculate mean of columns for a unique value of column variable?

7 views (last 30 days)
I have a matrix of 38767*31. I want to calculate mean of columns for unique value of variable in columns (in first row).Let me clarify my problem.
Let
A=[1 2 1 2 1 1 2 1 1
3 2 1 2 3 4 5 6 2
2 1 2 1 3 5 4 2 6]
for unique value for 1st row of A, I have to calculate mean of all corresponding columns. I mean I should get as follows,
B=[1 2; 3.66 3; 3.33 2];
for unique of 1st row (=1), mean of corresponding values (3+1+3+4+6+1)/6=3.166 for 2nd row and (2+2+3+5+2+6)/3=3.33 for 3rd row. for value of 1st row (=2), mean of corresponding values (2+2+5)/3=3 for 2nd row and (1+1+4)/3=2 for 3rd row.Hope I am understood. I tried using accumarray but not able to do. my code is,
A=importdata('abc.dat');
y=accumarray(unique(A(1,:)),A(1,:),[],@mean));
It shows following error, Second input VAL must be a vector with one element for each row in SUBS, or a scalar.? Help. Thanks in advance.

Accepted Answer

the cyclist
the cyclist on 18 Jun 2017
This will do what you want with a for loop:
A = [1 2 1 2 1 1 2 1 1
3 2 1 2 3 4 5 6 2
2 1 2 1 3 5 4 2 6];
[uniqueA1,~,k] = unique(A(1,:));
numberUniqueA1 = numel(uniqueA1);
meanArray = zeros(size(A,1),numberUniqueA1);
for nu = 1:numberUniqueA1
indexToThisUniqueValue = (nu==k)';
meanArray(:,nu) = mean(A(:,indexToThisUniqueValue),2);
end

More Answers (2)

dpb
dpb on 18 Jun 2017
Edited: dpb on 18 Jun 2017
>> [~,~,ib]=unique(A(1,:));
>> C=A(2,:);
>> accumarray(ib,C,[],@mean)
ans =
3.1667
3.0000
>>
Unfortunately, accumarray only works on a vector val input, not columns in an array (it's convoluted-enough already without that complication so that's understandable although would be the cat's meow here, indeed.)
Let's see if there's another way without looping over every column...hmmm....well, let's try
>> ix=A(1,:);
>> u=unique(ix);
>> for i=1:length(u),mean(A(2:end,ix==u(i)),2),end
ans =
3.1667
3.3333
ans =
3
2
>>
You can mull over how to best code it for your situation...
ADDENDUM:
May not be any better speedwise than the loop, but for the record--
>> arrayfun(@(ix) accumarray(ib,A(ix,:).',[],@mean),2:size(A,1),'uniformoutput',0)
ans =
[2x1 double] [2x1 double]
>> ans{:}
ans =
3.1667
3.0000
ans =
3.3333
2.0000
>>
ib is the previous index vector from unique above, of course...
Try to figure out what that does two years down the road when you come back to it! :)

Andrei Bobrov
Andrei Bobrov on 18 Jun 2017
Edited: Andrei Bobrov on 18 Jun 2017
[g,ii] = findgroups(A(1,:)');
out = [ii,splitapply(@mean,A(2:3,:)',g)]';
or
[ii,jj] = ndgrid(A(1,:),1:2);
out = [unique(A(1,:)'), accumarray([ii(:),jj(:)],reshape(A(2:3,:)',[],1),[],@mean)]';
  2 Comments
the cyclist
the cyclist on 18 Jun 2017
The second solution can be simplified to
[ii,jj] = ndgrid(A(1,:),1:3);
out = [accumarray([ii(:),jj(:)],reshape(A',[],1),[],@mean)]';
Depending on the depth of one's understanding of accumarray, I feel like this solution is both more elegant and more obfuscated than my for-loop solution. Either way, I would be sure to add some comments so that future you remembers what is going on in this code.
In limited testing, the for-loop solution is the fastest of these.
dpb
dpb on 18 Jun 2017
Edited: dpb on 18 Jun 2017
Less memory-intensive, too, if A gets really large...I hadn't thought of the 2D subscripting array into the full 1D vector, though...had pondered the thought of generating the 1D vector to go with it and that didn't seem attractive.

Sign in to comment.

Categories

Find more on Elementary Math in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!