Skip to content

Commit 2a748b1

Browse files
committed
Refactor correlation accumulators to use CovarianceAccumulator for covariance calculations
1 parent c2f1afe commit 2a748b1

8 files changed

Lines changed: 762 additions & 125 deletions

File tree

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AccumulatorFactory.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ public static Accumulator createBuiltinMultiInputAccumulator(
9696
CorrelationAccumulator.CorrelationType.CORR);
9797
case COVAR_POP:
9898
checkState(inputDataTypes.size() == 2, "Wrong inputDataTypes size.");
99-
return new CorrelationAccumulator(
99+
return new CovarianceAccumulator(
100100
new TSDataType[] {inputDataTypes.get(0), inputDataTypes.get(1)},
101-
CorrelationAccumulator.CorrelationType.COVAR_POP);
101+
CovarianceAccumulator.CovarianceType.COVAR_POP);
102102
case COVAR_SAMP:
103103
checkState(inputDataTypes.size() == 2, "Wrong inputDataTypes size.");
104-
return new CorrelationAccumulator(
104+
return new CovarianceAccumulator(
105105
new TSDataType[] {inputDataTypes.get(0), inputDataTypes.get(1)},
106-
CorrelationAccumulator.CorrelationType.COVAR_SAMP);
106+
CovarianceAccumulator.CovarianceType.COVAR_SAMP);
107107
case REGR_SLOPE:
108108
checkState(inputDataTypes.size() == 2, "Wrong inputDataTypes size.");
109109
return new RegressionAccumulator(

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CorrelationAccumulator.java

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@
3333
public class CorrelationAccumulator implements Accumulator {
3434

3535
public enum CorrelationType {
36-
CORR,
37-
COVAR_POP,
38-
COVAR_SAMP
36+
CORR
3937
}
4038

4139
private final TSDataType[] seriesDataTypes;
@@ -90,15 +88,16 @@ private double getDoubleValue(Column column, int position, TSDataType dataType)
9088

9189
private void update(double x, double y) {
9290
long newCount = count + 1;
93-
double deltaX = x - meanX;
94-
double deltaY = y - meanY;
9591

96-
meanX += deltaX / newCount;
97-
meanY += deltaY / newCount;
92+
double oldMeanX = meanX;
93+
double oldMeanY = meanY;
9894

99-
c2 += deltaX * (y - meanY);
100-
m2X += deltaX * (x - meanX);
101-
m2Y += deltaY * (y - meanY);
95+
meanX = oldMeanX + (x - oldMeanX) / newCount;
96+
meanY = oldMeanY + (y - oldMeanY) / newCount;
97+
98+
c2 += (x - oldMeanX) * (y - meanY);
99+
m2X += (x - oldMeanX) * (x - meanX);
100+
m2Y += (y - oldMeanY) * (y - meanY);
102101

103102
count = newCount;
104103
}
@@ -173,34 +172,16 @@ public void outputIntermediate(ColumnBuilder[] columnBuilders) {
173172

174173
@Override
175174
public void outputFinal(ColumnBuilder columnBuilder) {
176-
switch (correlationType) {
177-
case CORR:
178-
if (count < 2) {
179-
180-
columnBuilder.appendNull();
181-
} else if (m2X == 0 || m2Y == 0) {
182-
183-
columnBuilder.appendNull();
184-
} else {
185-
columnBuilder.writeDouble(c2 / Math.sqrt(m2X * m2Y));
186-
}
187-
break;
188-
case COVAR_POP:
189-
if (count == 0) {
190-
columnBuilder.appendNull();
191-
} else {
192-
columnBuilder.writeDouble(c2 / count);
193-
}
194-
break;
195-
case COVAR_SAMP:
196-
if (count < 2) {
197-
columnBuilder.appendNull();
198-
} else {
199-
columnBuilder.writeDouble(c2 / (count - 1));
200-
}
201-
break;
202-
default:
203-
throw new UnsupportedOperationException("Unknown type: " + correlationType);
175+
if (correlationType != CorrelationType.CORR) {
176+
throw new UnsupportedOperationException("Unknown type: " + correlationType);
177+
}
178+
179+
if (count < 2) {
180+
columnBuilder.appendNull();
181+
} else if (m2X == 0 || m2Y == 0) {
182+
columnBuilder.appendNull();
183+
} else {
184+
columnBuilder.writeDouble(c2 / Math.sqrt(m2X * m2Y));
204185
}
205186
}
206187

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.db.queryengine.execution.aggregation;
21+
22+
import org.apache.tsfile.block.column.Column;
23+
import org.apache.tsfile.block.column.ColumnBuilder;
24+
import org.apache.tsfile.enums.TSDataType;
25+
import org.apache.tsfile.file.metadata.statistics.Statistics;
26+
import org.apache.tsfile.utils.Binary;
27+
import org.apache.tsfile.utils.BitMap;
28+
29+
import java.nio.ByteBuffer;
30+
31+
import static com.google.common.base.Preconditions.checkArgument;
32+
33+
public class CovarianceAccumulator implements Accumulator {
34+
35+
public enum CovarianceType {
36+
COVAR_POP,
37+
COVAR_SAMP
38+
}
39+
40+
private final TSDataType[] seriesDataTypes;
41+
private final CovarianceType covarianceType;
42+
43+
private long count;
44+
private double meanX;
45+
private double meanY;
46+
private double c2;
47+
48+
public CovarianceAccumulator(TSDataType[] seriesDataTypes, CovarianceType covarianceType) {
49+
this.seriesDataTypes = seriesDataTypes;
50+
this.covarianceType = covarianceType;
51+
}
52+
53+
@Override
54+
public void addInput(Column[] columns, BitMap bitMap) {
55+
int size = columns[0].getPositionCount();
56+
for (int i = 0; i < size; i++) {
57+
if (bitMap != null && !bitMap.isMarked(i)) {
58+
continue;
59+
}
60+
if (columns[1].isNull(i) || columns[2].isNull(i)) {
61+
continue;
62+
}
63+
64+
double x = getDoubleValue(columns[1], i, seriesDataTypes[0]);
65+
double y = getDoubleValue(columns[2], i, seriesDataTypes[1]);
66+
update(x, y);
67+
}
68+
}
69+
70+
private double getDoubleValue(Column column, int position, TSDataType dataType) {
71+
switch (dataType) {
72+
case INT32:
73+
return column.getInt(position);
74+
case INT64:
75+
case TIMESTAMP:
76+
return column.getLong(position);
77+
case FLOAT:
78+
return column.getFloat(position);
79+
case DOUBLE:
80+
return column.getDouble(position);
81+
default:
82+
throw new IllegalArgumentException("Unsupported data type: " + dataType);
83+
}
84+
}
85+
86+
private void update(double x, double y) {
87+
long newCount = count + 1;
88+
double oldMeanX = meanX;
89+
meanX = oldMeanX + (x - oldMeanX) / newCount;
90+
double oldMeanY = meanY;
91+
double newMeanY = oldMeanY + (y - oldMeanY) / newCount;
92+
meanY = newMeanY;
93+
c2 += (x - oldMeanX) * (y - newMeanY);
94+
count = newCount;
95+
}
96+
97+
@Override
98+
public void addIntermediate(Column[] partialResult) {
99+
checkArgument(partialResult.length == 1, "partialResult of Covariance should be 1");
100+
if (partialResult[0].isNull(0)) {
101+
return;
102+
}
103+
104+
byte[] bytes = partialResult[0].getBinary(0).getValues();
105+
ByteBuffer buffer = ByteBuffer.wrap(bytes);
106+
107+
long otherCount = buffer.getLong();
108+
double otherMeanX = buffer.getDouble();
109+
double otherMeanY = buffer.getDouble();
110+
double otherC2 = buffer.getDouble();
111+
112+
merge(otherCount, otherMeanX, otherMeanY, otherC2);
113+
}
114+
115+
private void merge(long otherCount, double otherMeanX, double otherMeanY, double otherC2) {
116+
if (otherCount == 0) {
117+
return;
118+
}
119+
if (count == 0) {
120+
count = otherCount;
121+
meanX = otherMeanX;
122+
meanY = otherMeanY;
123+
c2 = otherC2;
124+
return;
125+
}
126+
127+
long newCount = count + otherCount;
128+
double deltaX = otherMeanX - meanX;
129+
double deltaY = otherMeanY - meanY;
130+
131+
c2 += otherC2 + deltaX * deltaY * count * otherCount / newCount;
132+
meanX += deltaX * otherCount / newCount;
133+
meanY += deltaY * otherCount / newCount;
134+
count = newCount;
135+
}
136+
137+
@Override
138+
public void outputIntermediate(ColumnBuilder[] columnBuilders) {
139+
checkArgument(columnBuilders.length == 1, "partialResult of Covariance should be 1");
140+
if (count == 0) {
141+
columnBuilders[0].appendNull();
142+
return;
143+
}
144+
145+
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES + Double.BYTES * 3);
146+
buffer.putLong(count);
147+
buffer.putDouble(meanX);
148+
buffer.putDouble(meanY);
149+
buffer.putDouble(c2);
150+
columnBuilders[0].writeBinary(new Binary(buffer.array()));
151+
}
152+
153+
@Override
154+
public void outputFinal(ColumnBuilder columnBuilder) {
155+
switch (covarianceType) {
156+
case COVAR_POP:
157+
if (count == 0) {
158+
columnBuilder.appendNull();
159+
} else {
160+
columnBuilder.writeDouble(c2 / count);
161+
}
162+
break;
163+
case COVAR_SAMP:
164+
if (count < 2) {
165+
columnBuilder.appendNull();
166+
} else {
167+
columnBuilder.writeDouble(c2 / (count - 1));
168+
}
169+
break;
170+
default:
171+
throw new UnsupportedOperationException("Unknown type: " + covarianceType);
172+
}
173+
}
174+
175+
@Override
176+
public void removeIntermediate(Column[] input) {
177+
throw new UnsupportedOperationException("Remove not implemented for Covariance");
178+
}
179+
180+
@Override
181+
public void addStatistics(Statistics statistics) {
182+
throw new UnsupportedOperationException(getClass().getName());
183+
}
184+
185+
@Override
186+
public void setFinal(Column finalResult) {}
187+
188+
@Override
189+
public void reset() {
190+
count = 0;
191+
meanX = 0;
192+
meanY = 0;
193+
c2 = 0;
194+
}
195+
196+
@Override
197+
public boolean hasFinalResult() {
198+
return false;
199+
}
200+
201+
@Override
202+
public TSDataType[] getIntermediateType() {
203+
return new TSDataType[] {TSDataType.TEXT};
204+
}
205+
206+
@Override
207+
public TSDataType getFinalType() {
208+
return TSDataType.DOUBLE;
209+
}
210+
}

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/AccumulatorFactory.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
2424
import org.apache.iotdb.db.queryengine.execution.aggregation.CentralMomentAccumulator;
2525
import org.apache.iotdb.db.queryengine.execution.aggregation.CorrelationAccumulator;
26+
import org.apache.iotdb.db.queryengine.execution.aggregation.CovarianceAccumulator;
2627
import org.apache.iotdb.db.queryengine.execution.aggregation.RegressionAccumulator;
2728
import org.apache.iotdb.db.queryengine.execution.aggregation.VarianceAccumulator;
2829
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.BinaryGroupedApproxMostFrequentAccumulator;
@@ -38,6 +39,7 @@
3839
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedCountAccumulator;
3940
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedCountAllAccumulator;
4041
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedCountIfAccumulator;
42+
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedCovarianceAccumulator;
4143
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedExtremeAccumulator;
4244
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedFirstAccumulator;
4345
import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.grouped.GroupedFirstByAccumulator;
@@ -268,15 +270,15 @@ private static GroupedAccumulator createBuiltinGroupedAccumulator(
268270
inputDataTypes.get(1),
269271
CorrelationAccumulator.CorrelationType.CORR);
270272
case COVAR_POP:
271-
return new GroupedCorrelationAccumulator(
273+
return new GroupedCovarianceAccumulator(
272274
inputDataTypes.get(0),
273275
inputDataTypes.get(1),
274-
CorrelationAccumulator.CorrelationType.COVAR_POP);
276+
CovarianceAccumulator.CovarianceType.COVAR_POP);
275277
case COVAR_SAMP:
276-
return new GroupedCorrelationAccumulator(
278+
return new GroupedCovarianceAccumulator(
277279
inputDataTypes.get(0),
278280
inputDataTypes.get(1),
279-
CorrelationAccumulator.CorrelationType.COVAR_SAMP);
281+
CovarianceAccumulator.CovarianceType.COVAR_SAMP);
280282
case REGR_SLOPE:
281283
return new GroupedRegressionAccumulator(
282284
inputDataTypes.get(0),
@@ -368,15 +370,15 @@ public static TableAccumulator createBuiltinAccumulator(
368370
inputDataTypes.get(1),
369371
CorrelationAccumulator.CorrelationType.CORR);
370372
case COVAR_POP:
371-
return new TableCorrelationAccumulator(
373+
return new TableCovarianceAccumulator(
372374
inputDataTypes.get(0),
373375
inputDataTypes.get(1),
374-
CorrelationAccumulator.CorrelationType.COVAR_POP);
376+
CovarianceAccumulator.CovarianceType.COVAR_POP);
375377
case COVAR_SAMP:
376-
return new TableCorrelationAccumulator(
378+
return new TableCovarianceAccumulator(
377379
inputDataTypes.get(0),
378380
inputDataTypes.get(1),
379-
CorrelationAccumulator.CorrelationType.COVAR_SAMP);
381+
CovarianceAccumulator.CovarianceType.COVAR_SAMP);
380382
case REGR_SLOPE:
381383
return new TableRegressionAccumulator(
382384
inputDataTypes.get(0),

0 commit comments

Comments
 (0)