-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Add support for STDDEV_POP and STDDEV_SAMP in VarianceFn #38871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c7c36a0
32ea658
a51470e
40c6b50
8bd2196
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,8 +75,12 @@ public class VarianceFn<T extends Number> extends Combine.CombineFn<T, VarianceA | |
| private static final boolean SAMPLE = true; | ||
| private static final boolean POP = false; | ||
|
|
||
| private boolean isSample; // flag to determine return value should be Variance Pop or Sample | ||
| private SerializableFunction<BigDecimal, T> decimalConverter; | ||
| private final boolean isSample; // flag to determine return value should be Variance Pop or Sample | ||
| // When true, extractOutput returns the square root of the variance (i.e. standard deviation). | ||
| // Beam's enumerable bridge cannot translate a SQRT call layered on top of a window VAR_SAMP, so | ||
| // STDDEV_SAMP / STDDEV_POP are computed end-to-end inside this combiner instead. | ||
| private final boolean isStddev; | ||
| private final SerializableFunction<BigDecimal, T> decimalConverter; | ||
|
|
||
| public static VarianceFn newPopulation(Schema.TypeName typeName) { | ||
| return newPopulation(BigDecimalConverter.forSqlType(typeName)); | ||
|
|
@@ -85,7 +89,7 @@ public static VarianceFn newPopulation(Schema.TypeName typeName) { | |
| public static <V extends Number> VarianceFn newPopulation( | ||
| SerializableFunction<BigDecimal, V> decimalConverter) { | ||
|
|
||
| return new VarianceFn<>(POP, decimalConverter); | ||
| return new VarianceFn<>(POP, false, decimalConverter); | ||
| } | ||
|
|
||
| public static VarianceFn newSample(Schema.TypeName typeName) { | ||
|
|
@@ -95,11 +99,21 @@ public static VarianceFn newSample(Schema.TypeName typeName) { | |
| public static <V extends Number> VarianceFn newSample( | ||
| SerializableFunction<BigDecimal, V> decimalConverter) { | ||
|
|
||
| return new VarianceFn<>(SAMPLE, decimalConverter); | ||
| return new VarianceFn<>(SAMPLE, false, decimalConverter); | ||
| } | ||
|
|
||
| private VarianceFn(boolean isSample, SerializableFunction<BigDecimal, T> decimalConverter) { | ||
| public static VarianceFn newSampleStddev(Schema.TypeName typeName) { | ||
| return new VarianceFn<>(SAMPLE, true, BigDecimalConverter.forSqlType(typeName)); | ||
| } | ||
|
|
||
| public static VarianceFn newPopulationStddev(Schema.TypeName typeName) { | ||
| return new VarianceFn<>(POP, true, BigDecimalConverter.forSqlType(typeName)); | ||
| } | ||
|
|
||
| private VarianceFn( | ||
| boolean isSample, boolean isStddev, SerializableFunction<BigDecimal, T> decimalConverter) { | ||
| this.isSample = isSample; | ||
| this.isStddev = isStddev; | ||
| this.decimalConverter = decimalConverter; | ||
| } | ||
|
|
||
|
|
@@ -133,7 +147,19 @@ public Coder<VarianceAccumulator> getAccumulatorCoder( | |
|
|
||
| @Override | ||
| public T extractOutput(VarianceAccumulator accumulator) { | ||
| return decimalConverter.apply(getVariance(accumulator)); | ||
| BigDecimal result = getVariance(accumulator); | ||
| if (result != null && isStddev) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another side note, as also said in the desciption, stddev is simple sqrt of variance. One line should do the work, and much of the code here are defensive edge case handling.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, I think these are reasonable cases to protect against, though. |
||
| double doubleVal = result.doubleValue(); | ||
| if (doubleVal < 0.0) { | ||
|
Abacn marked this conversation as resolved.
|
||
| doubleVal = 0.0; // Clamp negative variance due to numerical instability | ||
| } | ||
| double sqrtVal = Math.sqrt(doubleVal); | ||
| if (Double.isInfinite(sqrtVal)) { | ||
| return decimalConverter.apply(result.sqrt(MATH_CTX)); | ||
| } | ||
| result = BigDecimal.valueOf(sqrtVal); | ||
| } | ||
|
damccorm marked this conversation as resolved.
damccorm marked this conversation as resolved.
|
||
| return decimalConverter.apply(result); | ||
|
damccorm marked this conversation as resolved.
|
||
| } | ||
|
damccorm marked this conversation as resolved.
|
||
|
|
||
| private BigDecimal getVariance(VarianceAccumulator variance) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.