From: PJ Fanning Date: Thu, 26 May 2022 22:23:48 +0000 (+0000) Subject: add AVERAGEA function X-Git-Tag: REL_5_2_3~278 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c84eee1b8eca8dd477486fc24d06d6210ad8932e;p=poi.git add AVERAGEA function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901299 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 aa03d7e4f2..741a7921d0 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 @@ -333,13 +333,13 @@ public final class FunctionEval { // 358: GETPIVOTDATA retval[359] = new Hyperlink(); // 360: PHONETIC - // 361: AVERAGEA + retval[361] = AggregateFunction.AVERAGEA; retval[362] = MinaMaxa.MAXA; retval[363] = MinaMaxa.MINA; // 364: STDEVPA // 365: VARPA - // 366: STDEVA - // 367: VARA + retval[366] = AggregateFunction.STDEVA; + retval[367] = AggregateFunction.VARA; for (int i = 0; i < retval.length; i++) { Function f = retval[i]; diff --git a/poi/src/main/java/org/apache/poi/ss/formula/functions/AggregateFunction.java b/poi/src/main/java/org/apache/poi/ss/formula/functions/AggregateFunction.java index 6ad53f887f..02dd83c93e 100644 --- a/poi/src/main/java/org/apache/poi/ss/formula/functions/AggregateFunction.java +++ b/poi/src/main/java/org/apache/poi/ss/formula/functions/AggregateFunction.java @@ -187,6 +187,21 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { } }; public static final Function AVERAGE = new AggregateFunction() { + @Override + protected double evaluate(double[] values) throws EvaluationException { + if (values.length < 1) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return MathX.average(values); + } + }; + public static final Function AVERAGEA = new AggregateFunction() { + @Override + protected boolean handleLogicalValues() { + return true; + } + + @Override protected double evaluate(double[] values) throws EvaluationException { if (values.length < 1) { throw new EvaluationException(ErrorEval.DIV_ZERO); @@ -236,6 +251,20 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return StatsLib.stdevp(values); } }; + public static final Function STDEVA = new AggregateFunction() { + @Override + protected boolean handleLogicalValues() { + return true; + } + + @Override + protected double evaluate(double[] values) throws EvaluationException { + if (values.length < 1) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return StatsLib.stdev(values); + } + }; public static final Function SUM = new AggregateFunction() { protected double evaluate(double[] values) { return MathX.sum(values); @@ -262,6 +291,20 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return StatsLib.varp(values); } }; + public static final Function VARA = new AggregateFunction() { + @Override + protected boolean handleLogicalValues() { + return true; + } + + @Override + protected double evaluate(double[] values) throws EvaluationException { + if (values.length < 1) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return StatsLib.var(values); + } + }; public static final Function GEOMEAN = new Geomean(); private static class Product extends AggregateFunction { 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 5e3670c784..23213caf7c 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 @@ -96,6 +96,17 @@ public abstract class MultiOperandNumericFunction implements Function { blankConsumer = ConsumerFactory.createForBlank(policy); } + /** + * Functions like AVERAGEA() differ from AVERAGE() in the way they handle non-numeric cells. + * AVERAGEA treats booleans as 1.0 (true) and 0.0 (false). For strings, they should be parsed as numbers. + * When the string is not a number, treat it as 0.0. + * + * @return whether to parse non-numeric cells + */ + protected boolean handleLogicalValues() { + return false; + } + public final ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { try { double[] values = getNumberArray(args); @@ -169,7 +180,7 @@ public abstract class MultiOperandNumericFunction implements Function { ValueEval ve = ae.getValue(sIx, rrIx, rcIx); if (!isSubtotalCounted() && ae.isSubTotal(rrIx, rcIx)) continue; if (!isHiddenRowCounted() && ae.isRowHidden(rrIx)) continue; - collectValue(ve, true, temp); + collectValue(ve, !handleLogicalValues(), temp); } } } @@ -183,7 +194,7 @@ public abstract class MultiOperandNumericFunction implements Function { for (int rcIx = 0; rcIx < width; rcIx++) { ValueEval ve = ae.getValue(rrIx, rcIx); if (!isSubtotalCounted() && ae.isSubTotal(rrIx, rcIx)) continue; - collectValue(ve, true, temp); + collectValue(ve, !handleLogicalValues(), temp); } } return; @@ -191,7 +202,7 @@ public abstract class MultiOperandNumericFunction implements Function { if (operand instanceof RefEval) { RefEval re = (RefEval) operand; for (int sIx = re.getFirstSheetIndex(); sIx <= re.getLastSheetIndex(); sIx++) { - collectValue(re.getInnerValueEval(sIx), true, temp); + collectValue(re.getInnerValueEval(sIx), !handleLogicalValues(), temp); } return; } @@ -221,12 +232,17 @@ public abstract class MultiOperandNumericFunction implements Function { // ignore all ref strings return; } - String s = ((StringValueEval) ve).getStringValue(); + String s = ((StringValueEval) ve).getStringValue().trim(); Double d = OperandResolver.parseDouble(s); if (d == null) { - throw new EvaluationException(ErrorEval.VALUE_INVALID); + if (handleLogicalValues()) { + temp.add(0.0); + } else { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + } else { + temp.add(d.doubleValue()); } - temp.add(d.doubleValue()); return; } if (ve instanceof ErrorEval) { diff --git a/poi/src/test/java/org/apache/poi/ss/formula/functions/TestAverageA.java b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestAverageA.java new file mode 100644 index 0000000000..e5940cb7e5 --- /dev/null +++ b/poi/src/test/java/org/apache/poi/ss/formula/functions/TestAverageA.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.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; + +/** + * Testcase for AVERAGEA() functions + */ +public class TestAverageA { + + //https://support.microsoft.com/en-us/office/averagea-function-f5f84098-d453-4f4c-bbba-3d2c66356091 + @Test + void testMicrosoftExample1() throws IOException { + try (HSSFWorkbook wb = initWorkbook1()) { + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + HSSFCell cell = wb.getSheetAt(0).getRow(0).createCell(12); + assertDouble(fe, cell, "AVERAGEA(A2:A6)", 5.6, 0.00000000001); + assertDouble(fe, cell, "AVERAGEA(A2:A5,A7)", 5.6, 0.00000000001); + assertDouble(fe, cell, "AVERAGE(A2:A6)", 7, 0.00000000001); + } + } + + private HSSFWorkbook initWorkbook1() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + addRow(sheet, 0, "Data"); + addRow(sheet, 1, 10); + addRow(sheet, 2, 7); + addRow(sheet, 3, 9); + addRow(sheet, 4, 2); + addRow(sheet, 5, "Not available"); + addRow(sheet, 6, "Formula"); + return wb; + } +}