Browse Source

[bug-66105] support CORREL function

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901670 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_5_2_3
PJ Fanning 2 years ago
parent
commit
3cd83e1b9c

+ 1
- 1
poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java View File

@@ -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

+ 135
- 0
poi/src/main/java/org/apache/poi/ss/formula/functions/Correl.java View File

@@ -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.
* <p>
* <b>Syntax</b>:<br> <b>CORREL </b>(<b>array1</b>, <b>array2</b>)<br>
* </p>
* <p>
* 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.
* </p>
* <p>
* See https://support.microsoft.com/en-us/office/correl-function-995dcef7-0c0a-4bed-a3fb-239d7b68ca92
* </p>
*/
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() + ")");
}

}

+ 32
- 0
poi/src/main/java/org/apache/poi/ss/formula/functions/DoubleList.java View File

@@ -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++;
}
}

+ 0
- 31
poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java View File

@@ -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) {

+ 1
- 1
poi/src/main/java/org/apache/poi/ss/formula/functions/Sqrtpi.java View File

@@ -27,7 +27,7 @@ import org.apache.poi.ss.util.NumberToTextConverter;
/**
* Implementation for Excel SQRTPI() function.
* <p>
* <b>Syntax</b>:<br> <b>SQRT </b>(<b>number</b>)<br>
* <b>Syntax</b>:<br> <b>SQRTPI </b>(<b>number</b>)<br>
* </p>
* <p>
* Returns the square root of (number * pi).

+ 60
- 0
poi/src/test/java/org/apache/poi/ss/formula/functions/TestCorrel.java View File

@@ -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;
}
}

Loading…
Cancel
Save