]> source.dussan.org Git - poi.git/commitdiff
add AVERAGEA function
authorPJ Fanning <fanningpj@apache.org>
Thu, 26 May 2022 22:23:48 +0000 (22:23 +0000)
committerPJ Fanning <fanningpj@apache.org>
Thu, 26 May 2022 22:23:48 +0000 (22:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901299 13f79535-47bb-0310-9956-ffa450edef68

poi/src/main/java/org/apache/poi/ss/formula/eval/FunctionEval.java
poi/src/main/java/org/apache/poi/ss/formula/functions/AggregateFunction.java
poi/src/main/java/org/apache/poi/ss/formula/functions/MultiOperandNumericFunction.java
poi/src/test/java/org/apache/poi/ss/formula/functions/TestAverageA.java [new file with mode: 0644]

index aa03d7e4f29e2f04581cff9bca56887913ed844e..741a7921d0ff2e8e5cca52587fc9c9c3ee212c22 100644 (file)
@@ -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];
index 6ad53f887fb09a4513e70c2a531aa376eafc84d4..02dd83c93ef67fd4153e200fb9795e203975c49a 100644 (file)
@@ -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 {
index 5e3670c784d18f9e91e096cdeecb1ce56494ab0b..23213caf7c2135bff949ee784c88c30866a81eaa 100644 (file)
@@ -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 (file)
index 0000000..e5940cb
--- /dev/null
@@ -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;
+    }
+}