From: PJ Fanning Date: Sun, 5 Jun 2022 13:27:01 +0000 (+0000) Subject: [bug-66105] support CORREL function X-Git-Tag: REL_5_2_3~247 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3cd83e1b9cc369415cdcdcc656b317124d9eb95e;p=poi.git [bug-66105] support CORREL function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901670 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java b/poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java index eb322007fd..e08989ef27 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java @@ -287,7 +287,7 @@ public final class FunctionEval { retval[304] = new Sumx2my2(); retval[305] = new Sumx2py2(); // 306: CHITEST - // 307: CORREL + retval[307] = Correl.instance; // 308: COVAR // 309: FORECAST // 310: FTEST diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/Correl.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/Correl.java new file mode 100644 index 0000000000..34c871bdb3 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/Correl.java @@ -0,0 +1,135 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.ss.formula.functions; + +import org.apache.commons.math3.stat.correlation.PearsonsCorrelation; +import org.apache.poi.ss.formula.ThreeDEval; +import org.apache.poi.ss.formula.TwoDEval; +import org.apache.poi.ss.formula.eval.BlankEval; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.NumberEval; +import org.apache.poi.ss.formula.eval.NumericValueEval; +import org.apache.poi.ss.formula.eval.OperandResolver; +import org.apache.poi.ss.formula.eval.RefEval; +import org.apache.poi.ss.formula.eval.StringValueEval; +import org.apache.poi.ss.formula.eval.ValueEval; + +/** + * Implementation for Excel CORREL() function. + *

+ * Syntax:
CORREL (array1, array2)
+ *

+ *

+ * The CORREL function returns the correlation coefficient of two cell ranges. + * Use the correlation coefficient to determine the relationship between two properties. + * For example, you can examine the relationship between a location's average temperature and the use of air conditioners. + *

+ *

+ * See https://support.microsoft.com/en-us/office/correl-function-995dcef7-0c0a-4bed-a3fb-239d7b68ca92 + *

+ */ +public class Correl extends Fixed2ArgFunction { + + public static final Correl instance = new Correl(); + + @Override + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + try { + final PearsonsCorrelation pc = new PearsonsCorrelation(); + final double correl = pc.correlation( + getNumberArray(arg0), getNumberArray(arg1)); + return new NumberEval(correl); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } + + private double[] getNumberArray(ValueEval operand) throws EvaluationException { + DoubleList retval = new DoubleList(); + collectValues(operand, retval); + return retval.toArray(); + } + + private void collectValues(ValueEval operand, DoubleList temp) throws EvaluationException { + if (operand instanceof ThreeDEval) { + ThreeDEval ae = (ThreeDEval) operand; + for (int sIx = ae.getFirstSheetIndex(); sIx <= ae.getLastSheetIndex(); sIx++) { + int width = ae.getWidth(); + int height = ae.getHeight(); + for (int rrIx = 0; rrIx < height; rrIx++) { + for (int rcIx = 0; rcIx < width; rcIx++) { + ValueEval ve = ae.getValue(sIx, rrIx, rcIx); + collectValue(ve, temp); + } + } + } + return; + } + if (operand instanceof TwoDEval) { + TwoDEval ae = (TwoDEval) operand; + int width = ae.getWidth(); + int height = ae.getHeight(); + for (int rrIx = 0; rrIx < height; rrIx++) { + for (int rcIx = 0; rcIx < width; rcIx++) { + ValueEval ve = ae.getValue(rrIx, rcIx); + collectValue(ve, temp); + } + } + return; + } + if (operand instanceof RefEval) { + RefEval re = (RefEval) operand; + for (int sIx = re.getFirstSheetIndex(); sIx <= re.getLastSheetIndex(); sIx++) { + collectValue(re.getInnerValueEval(sIx), temp); + } + return; + } + collectValue(operand, temp); + } + + private void collectValue(ValueEval ve, DoubleList temp) throws EvaluationException { + if (ve == null) { + throw new IllegalArgumentException("ve must not be null"); + } + if (ve instanceof NumericValueEval) { + NumericValueEval ne = (NumericValueEval) ve; + temp.add(ne.getNumberValue()); + return; + } + if (ve instanceof StringValueEval) { + String s = ((StringValueEval) ve).getStringValue().trim(); + Double d = OperandResolver.parseDouble(s); + if (d == null) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } else { + temp.add(d.doubleValue()); + } + return; + } + if (ve instanceof ErrorEval) { + throw new EvaluationException((ErrorEval) ve); + } + if (ve == BlankEval.instance) { + temp.add(0.0); + return; + } + throw new RuntimeException("Invalid ValueEval type passed for conversion: (" + + ve.getClass() + ")"); + } + +} diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/DoubleList.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/DoubleList.java new file mode 100644 index 0000000000..e257bab2f6 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/DoubleList.java @@ -0,0 +1,32 @@ +package org.apache.poi.ss.formula.functions; + +import java.util.Arrays; + +final class DoubleList { + static final double[] EMPTY_DOUBLE_ARRAY = {}; + + private double[] _array; + private int _count; + + public DoubleList() { + _array = new double[8]; + _count = 0; + } + + public double[] toArray() { + return _count < 1 ? EMPTY_DOUBLE_ARRAY : Arrays.copyOf(_array, _count); + } + + private void ensureCapacity(int reqSize) { + if (reqSize > _array.length) { + int newSize = reqSize * 3 / 2; // grow with 50% extra + _array = Arrays.copyOf(_array, newSize); + } + } + + public void add(double value) { + ensureCapacity(_count + 1); + _array[_count] = value; + _count++; + } +} diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java index 86a2741fff..adbddd8ba4 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java @@ -17,8 +17,6 @@ package org.apache.poi.ss.formula.functions; -import java.util.Arrays; - import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.ThreeDEval; import org.apache.poi.ss.formula.TwoDEval; @@ -57,35 +55,6 @@ public abstract class MultiOperandNumericFunction implements Function { blankConsumer = ConsumerFactory.createForBlank(isBlankCounted ? Policy.COERCE : Policy.SKIP); } - static final double[] EMPTY_DOUBLE_ARRAY = {}; - - private static class DoubleList { - private double[] _array; - private int _count; - - public DoubleList() { - _array = new double[8]; - _count = 0; - } - - public double[] toArray() { - return _count < 1 ? EMPTY_DOUBLE_ARRAY : Arrays.copyOf(_array, _count); - } - - private void ensureCapacity(int reqSize) { - if (reqSize > _array.length) { - int newSize = reqSize * 3 / 2; // grow with 50% extra - _array = Arrays.copyOf(_array, newSize); - } - } - - public void add(double value) { - ensureCapacity(_count + 1); - _array[_count] = value; - _count++; - } - } - private static final int DEFAULT_MAX_NUM_OPERANDS = SpreadsheetVersion.EXCEL2007.getMaxFunctionArgs(); public void setMissingArgPolicy(Policy policy) { diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/Sqrtpi.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/Sqrtpi.java index dbd02b3c3b..9c51cc967a 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/functions/Sqrtpi.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/Sqrtpi.java @@ -27,7 +27,7 @@ import org.apache.poi.ss.util.NumberToTextConverter; /** * Implementation for Excel SQRTPI() function. *

- * Syntax:
SQRT (number)
+ * Syntax:
SQRTPI (number)
*

*

* Returns the square root of (number * pi). diff --git a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestCorrel.java b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestCorrel.java new file mode 100644 index 0000000000..dc18477a39 --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestCorrel.java @@ -0,0 +1,60 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.apache.poi.ss.util.Utils.addRow; +import static org.apache.poi.ss.util.Utils.assertDouble; + +/** + * Tests for {@link Correl} + */ +final class TestCorrel { + + //https://support.microsoft.com/en-us/office/correl-function-995dcef7-0c0a-4bed-a3fb-239d7b68ca92 + @Test + void testMicrosoftExample() throws IOException { + try (HSSFWorkbook wb = initWorkbook1()) { + HSSFSheet sheet = wb.getSheetAt(0); + HSSFRow row = sheet.getRow(0); + HSSFCell cell = row.createCell(100); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + assertDouble(fe, cell, "CORREL(A2:A6,B2:B6)", 0.997054486, 0.0000000005); + } + } + + private HSSFWorkbook initWorkbook1() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + addRow(sheet, 0, "Data1", "Data2"); + addRow(sheet, 1, 3, 9); + addRow(sheet, 2, 2, 7); + addRow(sheet, 3, 4, 12); + addRow(sheet, 4, 5, 15); + addRow(sheet, 5, 6, 17); + return wb; + } +}