Как кэшировать ячейку формулы, используя Apache POI - PullRequest
0 голосов
/ 04 марта 2020

Я создал код, как показано ниже, для создания файла Excel, вычисления и получения значения ячейки после этого, потому что мне нужно использовать функцию в Excel. (Эта функция не поддерживается apache poi, поэтому я думаю, что мне нужно читать cached ./ форматированное значение)

package main;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class WorkbookEvaluator
{
    public static void pop_mean()
    {
        System.out.println ("Test population mean in two different populations are same or not");
        System.out.println ("This program works only for two-tailed ");

        Scanner in = new Scanner(System.in);
        System.out.println ("What is population mean?:");
        double m = in.nextDouble();

        System.out.println ("How many samples are taken from population?:");
        double n = in.nextDouble();

        System.out.println ("What is Sample mean?:");
        double X = in.nextDouble();

        System.out.println ("What is unbiased variance for population:");
        double U = in.nextDouble();

        System.out.println ("What is Level of Significance (Type with %-value)");
        double L = in.nextDouble();
        double l = L/100;

            double a = n-1;
            double b = X-m;
            double c = Math.sqrt(n);
            double d = Math.sqrt(U);
            double f = d/c;
            double T = b/f;
            System.out.println ("Degree of freedom is " + a);
            System.out.println ("Test statistic is " + T);

            Workbook wb = new XSSFWorkbook();
            Sheet sheet = wb.createSheet();
            Row row1 = sheet.createRow(1);
            Row row2 = sheet.createRow(2);

            Cell cell1_1 = row1.createCell(1);
            Cell cell1_2 = row1.createCell(2);
            Cell cell1_3 = row1.createCell(3);
            Cell cell2_3 = row2.createCell(3);
            Cell cell2_4 = row2.createCell(4);

            cell1_1.setCellValue(l);
            cell1_2.setCellValue(a);
            cell2_3.setCellFormula("_xlfn.T.INV.2T(" + l +"," + a + ")");
            cell2_4.setCellFormula("SUM(" + l +"," + a + ")");

            FileOutputStream out = null;
            try {
                out = new FileOutputStream("T-inverse.xlsx");
                wb.write(out);
            } catch(IOException e) {
                System.out.println(e.toString());
            } finally {
                try {
                    out.close();
                } catch(IOException e) {
                    System.out.println(e.toString());
                }
            }
    }

    public static void read_excel() throws IOException
    {
        for (int q=3;q<5;q++)
        {
            XSSFWorkbook book = new XSSFWorkbook("C:\\Users\\shump\\Java\\Population mean Test\\T-inverse.xlsx");
            book.setForceFormulaRecalculation(true);
            XSSFSheet sheet = book.getSheetAt(0);
            sheet.setForceFormulaRecalculation(true);
            XSSFRow row = sheet.getRow(2);
            final DataFormatter dataFormatter = new DataFormatter(); 
            final double formtatedValue = row.getCell((short) q).getNumericCellValue(); 
            System.out.println(formtatedValue);
        }
    }

    public static void main(String[] args) throws IOException
    {    
        pop_mean();
        read_excel();
    }
}

Как видно из приведенной ниже попытки, она успешно работает в Excel. Тем не менее, утешенный вывод будет отображать 0.0. Я думал, что это из-за того, что функция не поддерживается, поэтому я попробовал то же самое с помощью функции sum, которая является одной из основных функций c, и эта функция поддерживается. Однако это не сработало. На рисунке D3 я использовал функцию T.INV.2T.

enter image description here

Test population mean in two different populations are same or not
This program works only for two-tailed 
What is population mean?:
171.4
How many samples are taken from population?:
9
What is Sample mean?:
172.8
What is unbiased variance for population:
4
What is Level of Significance (Type with %-value)
5
Degree of freedom is 8.0
Test statistic is 2.1000000000000085
0.0
0.0

После нескольких исследований я нашел статью, в которой говорится, когда формула значение не кэшируется, вывод будет 0. Итак, скажите, пожалуйста, как я могу кэшировать значение или есть какой-нибудь другой способ, которым я могу воспользоваться, чтобы получить значение неподдерживаемой функции.

1 Ответ

1 голос
/ 04 марта 2020

Вы, кажется, неправильно поняли, для чего apache poi создан. С точки зрения Excel это сделано для создания Excel файлов. Это создает файлы, которые Excel может открыть. Когда он открывает файлы книги, он открывает их как apache poi Workbook только для возможности добавления содержимого. Apache poi считается не таким, как Excel, и не взаимодействует ни с одним приложением Excel.

Таким образом, setForceFormulaRecalculation - это не настройка apache poi, а настройка для Excel когда Excel открывает рабочую книгу. Если для setForceFormulaRecalculation установлено значение true, то Excel пересчитывает все формулы, когда Excel открывает файл книги. Это не означает, что apache poi пересчитывает все формулы.

Для принудительного apache poi вычисления формул необходимо использовать FormulaEvaluator.

Например, если вы расширяете свой код следующим образом :

...
import org.apache.poi.ss.usermodel.FormulaEvaluator;
...
            Workbook wb = new XSSFWorkbook();
            FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
...
            cell2_4.setCellFormula("SUM(" + l +"," + a + ")");
            try {
                evaluator.evaluateFormulaCell(cell2_4);
            } catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
                notImplEx.printStackTrace();
            }
...

затем вычисляется формула SUM, а cell2_4 содержит вычисленное число чисел c, дополнительное к формуле.

Но, конечно,

...
            cell2_3.setCellFormula("TINV(" + l +"," + a + ")");
            try {
                evaluator.evaluateFormulaCell(cell2_3);
            } catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
                notImplEx.printStackTrace();
            }
...

приведет к NotImplementedException, поскольку TINV еще не реализовано в apache poi.

Так что нам нужно сделать то, что показано в Разработка формулы . С точки зрения функции TINV это будет:

...
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.functions.*;
import org.apache.commons.math3.distribution.TDistribution;
...
    static Function TINV = new Fixed2ArgFunction() {
        public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) {
            try {
                ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
                double dArg1 = OperandResolver.coerceValueToDouble(ve1);
                ValueEval ve2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
                double dArg2 = OperandResolver.coerceValueToDouble(ve2);

                TDistribution t = new TDistribution(dArg2);
                double result = t.inverseCumulativeProbability(1d - dArg1/2d);

                if (Double.isNaN(result) || Double.isInfinite(result)) {
                    throw new EvaluationException(ErrorEval.NUM_ERROR);
                }

                return new NumberEval(result);

            } catch (EvaluationException e) {
                return e.getErrorEval();
            }
        }
    };
...

, а затем

...
        WorkbookEvaluator.registerFunction("TINV", TINV);
...

Обратите внимание, я реализовал TINV вместо _xlfn.T.INV.2T, так как последний не является может быть реализован таким образом из-за его странного названия. Все известные мне Excel версии поддерживают TINV вместо _xlfn.T.INV.2T.

Полный пример расширения вашего кода:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.functions.*;
import org.apache.commons.math3.distribution.TDistribution;

public class WorkbookEvaluatorTest {

    static Function TINV = new Fixed2ArgFunction() {
        public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) {
            try {
                ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
                double dArg1 = OperandResolver.coerceValueToDouble(ve1);
                ValueEval ve2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
                double dArg2 = OperandResolver.coerceValueToDouble(ve2);

                TDistribution t = new TDistribution(dArg2);
                double result = t.inverseCumulativeProbability(1d - dArg1/2d);

                if (Double.isNaN(result) || Double.isInfinite(result)) {
                    throw new EvaluationException(ErrorEval.NUM_ERROR);
                }

                return new NumberEval(result);

            } catch (EvaluationException e) {
                return e.getErrorEval();
            }
        }
    };

    public static void pop_mean() {

        WorkbookEvaluator.registerFunction("TINV", TINV);

        System.out.println ("Test population mean in two different populations are same or not");
        System.out.println ("This program works only for two-tailed ");

        Scanner in = new Scanner(System.in);
        System.out.println ("What is population mean?:");
        double m = in.nextDouble();

        System.out.println ("How many samples are taken from population?:");
        double n = in.nextDouble();

        System.out.println ("What is Sample mean?:");
        double X = in.nextDouble();

        System.out.println ("What is unbiased variance for population:");
        double U = in.nextDouble();

        System.out.println ("What is Level of Significance (Type with %-value)");
        double L = in.nextDouble();
        double l = L/100;

        double a = n-1;
        double b = X-m;
        double c = Math.sqrt(n);
        double d = Math.sqrt(U);
        double f = d/c;
        double T = b/f;
        System.out.println ("Degree of freedom is " + a);
        System.out.println ("Test statistic is " + T);

        Workbook wb = new XSSFWorkbook();
        FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();

        Sheet sheet = wb.createSheet();
        Row row1 = sheet.createRow(1);
        Row row2 = sheet.createRow(2);

        Cell cell1_1 = row1.createCell(1);
        Cell cell1_2 = row1.createCell(2);
        Cell cell1_3 = row1.createCell(3);
        Cell cell2_3 = row2.createCell(3);
        Cell cell2_4 = row2.createCell(4);

        cell1_1.setCellValue(l);
        cell1_2.setCellValue(a);
        cell2_3.setCellFormula("TINV(" + l +"," + a + ")");
        try {
            evaluator.evaluateFormulaCell(cell2_3);
        } catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
            notImplEx.printStackTrace();
        }

        cell2_4.setCellFormula("SUM(" + l +"," + a + ")");
        try {
            evaluator.evaluateFormulaCell(cell2_4);
        } catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
            notImplEx.printStackTrace();
        }

        FileOutputStream out = null;
        try {
            out = new FileOutputStream("T-inverse.xlsx");
            wb.write(out);
        } catch(IOException e) {
            System.out.println("Write: " + e.toString());
        } finally {
            try {
                out.close();
                wb.close();
            } catch(IOException e) {
                System.out.println("Close: " + e.toString());
            }
        }
    }

    public static void read_excel() throws IOException {
        for (int q=3;q<5;q++) {
            XSSFWorkbook book = new XSSFWorkbook("T-inverse.xlsx");
            //book.setForceFormulaRecalculation(true);
            XSSFSheet sheet = book.getSheetAt(0);
            //sheet.setForceFormulaRecalculation(true);
            XSSFRow row = sheet.getRow(2);
            final DataFormatter dataFormatter = new DataFormatter(); 
            final double formtatedValue = row.getCell((short) q).getNumericCellValue(); 
            System.out.println(formtatedValue);
        }
    }

    public static void main(String[] args) throws IOException {    
        pop_mean();
        read_excel();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...