allocate values avoiding loop

3 views (last 30 days)
Rahel Braun
Rahel Braun on 8 Nov 2018
Commented: Cris LaPierre on 12 Nov 2018
I have the following matrix [t k p]
1.0000 1.0000 -1.1471
1.0000 2.0000 -1.0689
2.0000 1.0000 -0.8095
2.0000 2.0000 -2.9443
3.0000 1.0000 1.4384
3.0000 2.0000 0.3252
and I want an additional column with the mean of p for every t, hence
1.0000 1.0000 -1.1471 -1.1080
1.0000 2.0000 -1.0689 -1.1080
2.0000 1.0000 -0.8095 -1.8769
2.0000 2.0000 -2.9443 -1.8769
3.0000 1.0000 1.4384 0.8818
3.0000 2.0000 0.3252 0.8818
I can do it with the following code
if true
%Calulate the mean
A=[t p_tk];
p_t= accumarray(A(:,[1]), A(:,2), [], @nanmean, NaN);
% allocate it to long form
p_t_long= NaN(size(t));
for d = 1:max(t)
ind= t ==d;
p_t_long(ind)= p_t(d);
end
end
However, I want to avoid loops since I have a big dataset. Can anybody help?

Accepted Answer

Stephen23
Stephen23 on 8 Nov 2018
Edited: Stephen23 on 8 Nov 2018
Some indexing using the first column does what you want, more efficiently than a loop or unique:
>> M = [1,1,-1.1471;1,2,-1.0689;2,1,-0.8095;2,2,-2.9443;3,1,1.4384;3,2,0.3252]
M =
1.00000 1.00000 -1.14710
1.00000 2.00000 -1.06890
2.00000 1.00000 -0.80950
2.00000 2.00000 -2.94430
3.00000 1.00000 1.43840
3.00000 2.00000 0.32520
>> V = accumarray(M(:,1),M(:,3),[],@mean)
V =
-1.10800
-1.87690
0.88180
>> M(:,4) = V(M(:,1))
M =
1.00000 1.00000 -1.14710 -1.10800
1.00000 2.00000 -1.06890 -1.10800
2.00000 1.00000 -0.80950 -1.87690
2.00000 2.00000 -2.94430 -1.87690
3.00000 1.00000 1.43840 0.88180
3.00000 2.00000 0.32520 0.88180
  3 Comments
Stephen23
Stephen23 on 12 Nov 2018
Edited: Stephen23 on 12 Nov 2018
>> M = [1,1,1,0.1435;1,1,2,-5.3137;1,2,1,-6.7921;1,2,2,-8.5640;2,1,1,-2.3356;2,1,2,-17.0264;2,2,1,12.6423;2,2,2,8.2006;3,1,1,2.7997;3,1,2,2.6523;3,2,1,-4.9816;3,2,2,13.1869]
M =
1.00000 1.00000 1.00000 0.14350
1.00000 1.00000 2.00000 -5.31370
1.00000 2.00000 1.00000 -6.79210
1.00000 2.00000 2.00000 -8.56400
2.00000 1.00000 1.00000 -2.33560
2.00000 1.00000 2.00000 -17.02640
2.00000 2.00000 1.00000 12.64230
2.00000 2.00000 2.00000 8.20060
3.00000 1.00000 1.00000 2.79970
3.00000 1.00000 2.00000 2.65230
3.00000 2.00000 1.00000 -4.98160
3.00000 2.00000 2.00000 13.18690
>> [~,~,idx] = unique(M(:,1:end-2),'rows'); % indices of row groups.
>> V = accumarray(idx,M(:,end),[],@mean); % mean of each group.
>> M(:,5) = V(idx)
M =
1.00000 1.00000 1.00000 0.14350 -2.58510
1.00000 1.00000 2.00000 -5.31370 -2.58510
1.00000 2.00000 1.00000 -6.79210 -7.67805
1.00000 2.00000 2.00000 -8.56400 -7.67805
2.00000 1.00000 1.00000 -2.33560 -9.68100
2.00000 1.00000 2.00000 -17.02640 -9.68100
2.00000 2.00000 1.00000 12.64230 10.42145
2.00000 2.00000 2.00000 8.20060 10.42145
3.00000 1.00000 1.00000 2.79970 2.72600
3.00000 1.00000 2.00000 2.65230 2.72600
3.00000 2.00000 1.00000 -4.98160 4.10265
3.00000 2.00000 2.00000 13.18690 4.10265

Sign in to comment.

More Answers (2)

Bruno Luong
Bruno Luong on 8 Nov 2018
A=[...
1.0000 1.0000 -1.1471
1.0000 2.0000 -1.0689
2.0000 1.0000 -0.8095
2.0000 2.0000 -2.9443
3.0000 1.0000 1.4384
3.0000 2.0000 0.3252 ]
[~,~,J] = unique(A(:,1));
p_t= accumarray(J, A(:,3), [], @(x) mean(x,'omitnan'), NaN);
[A p_t(J)]
Result
ans =
1.0000 1.0000 -1.1471 -1.1080
1.0000 2.0000 -1.0689 -1.1080
2.0000 1.0000 -0.8095 -1.8769
2.0000 2.0000 -2.9443 -1.8769
3.0000 1.0000 1.4384 0.8818
3.0000 2.0000 0.3252 0.8818
  5 Comments
Rahel Braun
Rahel Braun on 12 Nov 2018
My problem was that I didn't know how to use unique() properly with 3 groups, and then the accumarray gave me a 3*2 matrix and I couldn't assign that. But thank you anyway.
Bruno Luong
Bruno Luong on 12 Nov 2018
My problem was that I didn't know how to use unique() properly with 3 groups,
Stephen already answered by just add 'ROWS' argument, to have one identification (third output) by for each 1x3 row (your "groups").
BTW, you might not noticed by using
accumarray(...,data) ./ accumarray(...,1)
is always fater than
accumarray(...,data, ..., @mean)
if speed is matter for you.

Sign in to comment.


Cris LaPierre
Cris LaPierre on 8 Nov 2018
Consider using the splitapply function. Assuming you have variable t, k and p already defined:
grpAvg = splitapply(@mean,p,t);
pAvg = grpAvg(t);
[t k p pAvg]
  2 Comments
Cris LaPierre
Cris LaPierre on 8 Nov 2018
Edited: Cris LaPierre on 8 Nov 2018
If your grouping variable is not as clean as it is in this example, you can use the findgroups function to create an index of the unique values in your grouping variable.
Cris LaPierre
Cris LaPierre on 12 Nov 2018
Using your updated matrix from a comment, here is a robust way to achieve what you want using findgroups and splitapply (assuming variable t,k,l, and p exist and represent the columns of M):
M = [t k l p]
G = findgroups(t,k);
grpAvg = splitapply(@mean,p,G);
pAvg = grpAvg(G);
V = [t k l p pAvg]
The original matrix M is
M =
1.0000 1.0000 1.0000 0.1435
1.0000 1.0000 2.0000 -5.3137
1.0000 2.0000 1.0000 -6.7921
1.0000 2.0000 2.0000 -8.5640
2.0000 1.0000 1.0000 -2.3356
2.0000 1.0000 2.0000 -17.0264
2.0000 2.0000 1.0000 12.6423
2.0000 2.0000 2.0000 8.2006
3.0000 1.0000 1.0000 2.7997
3.0000 1.0000 2.0000 2.6523
3.0000 2.0000 1.0000 -4.9816
3.0000 2.0000 2.0000 13.1869
And resulting matrix V is
V =
1.0000 1.0000 1.0000 0.1435 -2.5851
1.0000 1.0000 2.0000 -5.3137 -2.5851
1.0000 2.0000 1.0000 -6.7921 -7.6780
1.0000 2.0000 2.0000 -8.5640 -7.6780
2.0000 1.0000 1.0000 -2.3356 -9.6810
2.0000 1.0000 2.0000 -17.0264 -9.6810
2.0000 2.0000 1.0000 12.6423 10.4215
2.0000 2.0000 2.0000 8.2006 10.4215
3.0000 1.0000 1.0000 2.7997 2.7260
3.0000 1.0000 2.0000 2.6523 2.7260
3.0000 2.0000 1.0000 -4.9816 4.1026
3.0000 2.0000 2.0000 13.1869 4.1026

Sign in to comment.

Categories

Find more on Loops and Conditional Statements 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!