1

我正在使用 Apache POI 3.9,我需要使用 Excel 公式 PERCENTILE 并从 Java 中评估公式。

问题是不支持 PERCENTILE。

我收到一个org.apache.poi.ss.formula.eval.NotImplementedException:错误

我有 3 种可能的解决方案来解决我的问题。

  1. 在 java 中编写我自己的百分位函数并将其贡献给 Apache POI 库(参见http://poi.apache.org/spreadsheet/eval-devguide.html

  2. 将 Excel 工作表中的相关单元格放入 java 中,并使用诸如百分比计算中的函数进行计算

  3. 将 PERCENTILE 转换为 SMALL 函数,例如代替=PERCENTILE(A1:A10,95%) then=(SMALL(A1:A10,9)+SMALL(A1:A10,10))/2

在这个时间点,我需要一个快速的解决方案。对我来说,第三个将是最好的,但它并没有给出完全相同的结果。

在我退回到有点混乱的选项 2 之前,有没有人有任何想法?

4

1 回答 1

1

Thank you Gagravarr for your encouragement and also for your post Apache POI - How to register a function which was extremely helpful

I implemented the PERCENTILE function in the library in the following way:

  1. I downloaded the source code for Apache POI 3.9
  2. I checked for the ID number of the PERCENTILE function in src/resources/main/org/apache/poi/ss/formula/function/functionMetadata.txt (or online) and it was 328
  3. I thus added retval[328] = AggregateFunction.PERCENTILE; to the file org.apache.poi.ss.formula.eval.FunctionEval.java
  4. I added public static final Function PERCENTILE = new Percentile(); to the file org.apache.poi.ss.formula.functions.AggregateFunction.java in the appropriate place.
  5. I added to the same file the following code (which I based on the LargeSmall function; I had looked for the existing code/type of function which was most similar to what I wanted to do)

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

    }

  6. I then uploaded these two files individually and could then use the PERCENTILE function normally.

于 2013-10-29T17:37:28.180 回答