From fc086aba47a5e45005b3aedd08589cab1d43a913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Walter?= Date: Tue, 5 Nov 2013 22:11:18 +0000 Subject: [PATCH] Bug 55724: implementation of Excel PERCENTILE function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1539154 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/eval/FunctionEval.java | 3 +- .../formula/functions/AggregateFunction.java | 69 ++++++++++++++++++ test-data/spreadsheet/FormulaEvalTestData.xls | Bin 175616 -> 176640 bytes 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java index a343d759b..92f6ec8c9 100644 --- a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java @@ -224,7 +224,8 @@ public final class FunctionEval { retval[325] = AggregateFunction.LARGE; retval[326] = AggregateFunction.SMALL; - + retval[328] = AggregateFunction.PERCENTILE; + retval[330] = new Mode(); retval[336] = TextFunction.CONCATENATE; diff --git a/src/java/org/apache/poi/ss/formula/functions/AggregateFunction.java b/src/java/org/apache/poi/ss/formula/functions/AggregateFunction.java index 52f12210f..084355d30 100644 --- a/src/java/org/apache/poi/ss/formula/functions/AggregateFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/AggregateFunction.java @@ -67,6 +67,72 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return new NumberEval(result); } } + + /** + * Returns the k-th percentile of values in a range. You can use this function to establish a threshold of + * acceptance. For example, you can decide to examine candidates who score above the 90th percentile. + * + * PERCENTILE(array,k) + * Array is the array or range of data that defines relative standing. + * K is the percentile value in the range 0..1, inclusive. + * + * Remarks + * + */ + private static final class Percentile extends Fixed2ArgFunction { + + protected Percentile() { + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) { + double dn; + try { + ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex); + dn = OperandResolver.coerceValueToDouble(ve1); + } catch (EvaluationException e1) { + // all errors in the second arg translate to #VALUE! + return ErrorEval.VALUE_INVALID; + } + if (dn < 0 || dn > 1) { // has to be percentage + return ErrorEval.NUM_ERROR; + } + + double result; + try { + double[] ds = ValueCollector.collectValues(arg0); + int N = ds.length; + + if (N == 0 || N > 8191) { + return ErrorEval.NUM_ERROR; + } + + double n = (N - 1) * dn + 1; + if (n == 1d) { + result = StatsLib.kthSmallest(ds, 1); + } else if (n == N) { + result = StatsLib.kthLargest(ds, 1); + } else { + int k = (int) n; + double d = n - k; + result = StatsLib.kthSmallest(ds, k) + d + * (StatsLib.kthSmallest(ds, k + 1) - StatsLib.kthSmallest(ds, k)); + } + + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return new NumberEval(result); + } + } + static final class ValueCollector extends MultiOperandNumericFunction { private static final ValueCollector instance = new ValueCollector(); public ValueCollector() { @@ -148,6 +214,9 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return values.length > 0 ? MathX.min(values) : 0; } }; + + public static final Function PERCENTILE = new Percentile(); + public static final Function PRODUCT = new AggregateFunction() { protected double evaluate(double[] values) { return MathX.product(values); diff --git a/test-data/spreadsheet/FormulaEvalTestData.xls b/test-data/spreadsheet/FormulaEvalTestData.xls index 29924ae6d0cb0f6a0ac44bfd00eda53e19dc9665..dfa87f7cb7f0a09b5daca3769e18b3933f0bb1be 100644 GIT binary patch delta 4627 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 delta 3658 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 IZdt7TFG^KKaR2}S