Bug 62373: Support for FREQUENCY function
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844238 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
290e9c4087
commit
c7111f562c
@ -243,6 +243,7 @@ public final class FunctionEval {
|
|||||||
|
|
||||||
// 247: DB
|
// 247: DB
|
||||||
// 252: FEQUENCY
|
// 252: FEQUENCY
|
||||||
|
retval[252] = Frequency.instance;
|
||||||
|
|
||||||
retval[FunctionID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeRefFunction, nominally 255
|
retval[FunctionID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeRefFunction, nominally 255
|
||||||
|
|
||||||
|
81
src/java/org/apache/poi/ss/formula/functions/Frequency.java
Normal file
81
src/java/org/apache/poi/ss/formula/functions/Frequency.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.ss.formula.CacheAreaEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
|
import org.apache.poi.ss.formula.eval.NumberEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Excel 'Analysis ToolPak' function FREQUENCY()<br>
|
||||||
|
* Returns a frequency distribution as a vertical array<p>
|
||||||
|
* <p>
|
||||||
|
* <b>Syntax</b><br>
|
||||||
|
* <b>FREQUENCY</b>(<b>data_array</b>, <b>bins_array</b>)<p>
|
||||||
|
* <p>
|
||||||
|
* <b>data_array</b> Required. An array of or reference to a set of values for which you want to count frequencies.
|
||||||
|
* If data_array contains no values, FREQUENCY returns an array of zeros.<br>
|
||||||
|
* <b>bins_array</b> Required. An array of or reference to intervals into which you want to group the values in data_array.
|
||||||
|
* If bins_array contains no values, FREQUENCY returns the number of elements in data_array.<br>
|
||||||
|
*
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
|
public class Frequency extends Fixed2ArgFunction {
|
||||||
|
public static final Function instance = new Frequency();
|
||||||
|
|
||||||
|
private Frequency() {
|
||||||
|
// enforce singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||||
|
MatrixFunction.MutableValueCollector collector = new MatrixFunction.MutableValueCollector(false, false);
|
||||||
|
|
||||||
|
double[] values;
|
||||||
|
double[] bins;
|
||||||
|
try {
|
||||||
|
values = collector.collectValues(arg0);
|
||||||
|
bins = collector.collectValues(arg1);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
|
||||||
|
// can bins be not sorted?
|
||||||
|
//bins = Arrays.stream(bins).sorted().distinct().toArray();
|
||||||
|
|
||||||
|
int[] histogram = histogram(values, bins);
|
||||||
|
NumberEval[] result = Arrays.stream(histogram).boxed().map(NumberEval::new).toArray(NumberEval[]::new);
|
||||||
|
return new CacheAreaEval(srcRowIndex, srcColumnIndex,
|
||||||
|
srcRowIndex + result.length - 1, srcColumnIndex, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int findBin(double value, double[] bins) {
|
||||||
|
int idx = Arrays.binarySearch(bins, value);
|
||||||
|
return idx >= 0 ? idx + 1 : -idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int[] histogram(double[] values, double[] bins) {
|
||||||
|
int[] histogram = new int[bins.length + 1];
|
||||||
|
for (double val : values) {
|
||||||
|
histogram[findBin(val, bins) - 1]++;
|
||||||
|
}
|
||||||
|
return histogram;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.apache.poi.ss.formula.functions.Frequency.histogram;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testcase for the function FREQUENCY(data, bins)
|
||||||
|
*
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
|
public class TestFrequency {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistogram() {
|
||||||
|
assertArrayEquals(new int[]{3, 2, 2, 0, 1, 1},
|
||||||
|
histogram(
|
||||||
|
new double[]{11, 12, 13, 21, 29, 36, 40, 58, 69},
|
||||||
|
new double[]{20, 30, 40, 50, 60})
|
||||||
|
);
|
||||||
|
|
||||||
|
assertArrayEquals(new int[]{1, 1, 1, 1, 1, 0},
|
||||||
|
histogram(
|
||||||
|
new double[]{20, 30, 40, 50, 60},
|
||||||
|
new double[]{20, 30, 40, 50, 60})
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
assertArrayEquals(new int[]{2, 3},
|
||||||
|
histogram(
|
||||||
|
new double[]{20, 30, 40, 50, 60},
|
||||||
|
new double[]{30})
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEvaluate() {
|
||||||
|
Workbook wb = new HSSFWorkbook();
|
||||||
|
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
|
||||||
|
|
||||||
|
int[] data = {1, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 11, 3, 5, 8};
|
||||||
|
int[] bins = {3, 6, 9};
|
||||||
|
Sheet sheet = wb.createSheet();
|
||||||
|
Row dataRow = sheet.createRow(0); // A1:O1
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
dataRow.createCell(i).setCellValue(data[i]);
|
||||||
|
}
|
||||||
|
Row binsRow = sheet.createRow(1);
|
||||||
|
for (int i = 0; i < bins.length; i++) { // A2:C2
|
||||||
|
binsRow.createCell(i).setCellValue(bins[i]);
|
||||||
|
}
|
||||||
|
Row fmlaRow = sheet.createRow(2);
|
||||||
|
CellRange<? extends Cell> arrayFmla = sheet.setArrayFormula("FREQUENCY(A1:O1,A2:C2)", CellRangeAddress.valueOf("A3:A6"));
|
||||||
|
Cell b3 = fmlaRow.createCell(1); // B3
|
||||||
|
b3.setCellFormula("COUNT(FREQUENCY(A1:O1,A2:C2))"); // frequency returns a vertical array of bins+1
|
||||||
|
|
||||||
|
Cell c3 = fmlaRow.createCell(2);
|
||||||
|
c3.setCellFormula("SUM(FREQUENCY(A1:O1,A2:C2))"); // sum of the frequency bins should add up to the number of data values
|
||||||
|
|
||||||
|
assertEquals(5, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[0]).getNumberValue());
|
||||||
|
assertEquals(4, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[1]).getNumberValue());
|
||||||
|
assertEquals(5, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[2]).getNumberValue());
|
||||||
|
assertEquals(1, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[3]).getNumberValue());
|
||||||
|
|
||||||
|
assertEquals(4, (int) evaluator.evaluate(b3).getNumberValue());
|
||||||
|
assertEquals(15, (int) evaluator.evaluate(c3).getNumberValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user