{
counts.ensureCapacity(groupIdsBlock.getGroupCount());
means.ensureCapacity(groupIdsBlock.getGroupCount());
m2s.ensureCapacity(groupIdsBlock.getGroupCount());
BlockCursor values = valuesBlock.cursor();
for (int position = 0; position < groupIdsBlock.getPositionCount(); position++) {
checkState(values.advanceNextPosition());
if (!values.isNull()) {
long groupId = groupIdsBlock.getGroupId(position);
Slice slice = values.getSlice();
long inputCount = getCount(slice);
double inputMean = getMean(slice);
double inputM2 = getM2(slice);
long currentCount = counts.get(groupId);
double currentMean = means.get(groupId);
double currentM2 = m2s.get(groupId);
// Use numerically stable variant
long newCount = currentCount + inputCount;
double newMean = ((currentCount * currentMean) + (inputCount * inputMean)) / newCount;
double delta = inputMean - currentMean;
double newM2 = currentM2 + inputM2 + ((delta * delta) * (currentCount * inputCount)) / newCount;
counts.set(groupId, newCount);
means.set(groupId, newMean);
m2s.set(groupId, newM2);
}
}
checkState(!values.advanceNextPosition());
}