You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/01/16 15:39:48 UTC

[groovy] branch master updated: Add built-in `stdevp` function to GINQ

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 799b771  Add built-in `stdevp` function to GINQ
799b771 is described below

commit 799b7719cbba2c6a7536d83ed4274c0706953ca2
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Jan 16 23:38:25 2021 +0800

    Add built-in `stdevp` function to GINQ
---
 .../ginq/provider/collection/GinqAstWalker.groovy  |  5 +++--
 .../provider/collection/runtime/Queryable.java     | 11 +++++++++-
 .../collection/runtime/QueryableCollection.java    | 11 +++++++++-
 .../groovy-ginq/src/spec/doc/ginq-userguide.adoc   | 14 +++++++++++--
 .../test/org/apache/groovy/ginq/GinqTest.groovy    | 24 ++++++++++++++++++++++
 5 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 462d4f5..801deb0 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -1548,8 +1548,9 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
     private static final String FUNCTION_AVG = 'avg'
     private static final String FUNCTION_MEDIAN = 'median'
     private static final String FUNCTION_STDEV = 'stdev'
+    private static final String FUNCTION_STDEVP = 'stdevp'
     private static final String FUNCTION_AGG = 'agg'
-    private static final List<String> AGG_FUNCTION_NAME_LIST = [FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG, FUNCTION_MEDIAN, FUNCTION_STDEV, FUNCTION_AGG]
+    private static final List<String> AGG_FUNCTION_NAME_LIST = [FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG, FUNCTION_MEDIAN, FUNCTION_STDEV, FUNCTION_STDEVP, FUNCTION_AGG]
 
     private static final String FUNCTION_ROW_NUMBER = 'rowNumber'
     private static final String FUNCTION_LEAD = 'lead'
@@ -1558,7 +1559,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
     private static final String FUNCTION_LAST_VALUE = 'lastValue'
     private static final String FUNCTION_RANK = 'rank'
     private static final String FUNCTION_DENSE_RANK = 'denseRank'
-    private static final List<String> WINDOW_FUNCTION_LIST = [FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG, FUNCTION_MEDIAN, FUNCTION_STDEV,
+    private static final List<String> WINDOW_FUNCTION_LIST = [FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG, FUNCTION_MEDIAN, FUNCTION_STDEV, FUNCTION_STDEVP,
                                                               FUNCTION_ROW_NUMBER, FUNCTION_LEAD, FUNCTION_LAG, FUNCTION_FIRST_VALUE, FUNCTION_LAST_VALUE, FUNCTION_RANK, FUNCTION_DENSE_RANK]
 
     private static final String NAMEDRECORD_CLASS_NAME = NamedRecord.class.name
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
index 36e0d6e..b73d995 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
@@ -421,12 +421,21 @@ public interface Queryable<T> {
      * Aggregate function {@code stdev}, similar to SQL's {@code stdev}
      *
      * @param mapper choose the field to calculate the statistical standard deviation
-     * @return statistical standard deviation result
+     * @return statistical standard deviation
      * @since 4.0.0
      */
     BigDecimal stdev(Function<? super T, ? extends Number> mapper);
 
     /**
+     * Aggregate function {@code stdevp}, similar to SQL's {@code stdevp}
+     *
+     * @param mapper choose the field to calculate the statistical standard deviation for the population
+     * @return statistical standard deviation for the population
+     * @since 4.0.0
+     */
+    BigDecimal stdevp(Function<? super T, ? extends Number> mapper);
+
+    /**
      * The most powerful aggregate function in GINQ, it will receive the grouped result({@link Queryable} instance) and apply any processing
      *
      * @param mapper map the grouped result({@link Queryable} instance) to aggregate result
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
index 06c9fec..44d5a49 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
@@ -428,6 +428,15 @@ class QueryableCollection<T> implements Queryable<T>, Serializable {
 
     @Override
     public BigDecimal stdev(Function<? super T, ? extends Number> mapper) {
+        return sd(mapper, 0);
+    }
+
+    @Override
+    public BigDecimal stdevp(Function<? super T, ? extends Number> mapper) {
+        return sd(mapper, 1);
+    }
+
+    private BigDecimal sd(Function<? super T, ? extends Number> mapper, int diff) {
         BigDecimal avg = this.avg(mapper);
         Object[] result = agg(q -> q.stream()
                 .map(mapper)
@@ -440,7 +449,7 @@ class QueryableCollection<T> implements Queryable<T>, Serializable {
                 }, (o1, o2) -> o1));
 
         // `BigDecimal.sqrt` is introduced since Java9, so we can not use it for now.
-        return toBigDecimal(sqrt(((BigDecimal) result[1]).divide(toBigDecimal((Long) result[0]), 16, RoundingMode.HALF_UP).doubleValue()));
+        return toBigDecimal(sqrt(((BigDecimal) result[1]).divide(toBigDecimal((Long) result[0] - diff), 16, RoundingMode.HALF_UP).doubleValue()));
     }
 
     @Override
diff --git a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
index a161b36..3ac9260 100644
--- a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
+++ b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
@@ -284,7 +284,7 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_grouping_09,in
 
 ===== Aggregate Functions
 GINQ provides some built-in aggregate functions, e.g.
-`count`, `min`, `max`, `sum`, `avg`, `median`, `stdev` and the most powerful function `agg`.
+`count`, `min`, `max`, `sum`, `avg`, `median`, `stdev`, `stdevp` and the most powerful function `agg`.
 [NOTE]
 `count(...)`, `min(...)`, `max(...)`, `avg(...)`, `median(...)` and `stdev(...)` just operate on non-`null` values,
 and `count()` is similar to `count(*)` in SQL.
@@ -343,6 +343,11 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_aggfunction_01
 include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_aggfunction_03,indent=0]
 ----
 
+[source, groovy]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_aggfunction_04,indent=0]
+----
+
 ==== Sorting
 `orderby` is equivalent to SQL's `ORDER BY`
 
@@ -587,7 +592,7 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_15
 include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_11,indent=0]
 ----
 
-===== `min`, `max`, `count`, `sum`, `avg`, `median` and `stdev`
+===== `min`, `max`, `count`, `sum`, `avg`, `median`, `stdev` and `stdevp`
 [source, groovy]
 ----
 include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_22,indent=0]
@@ -643,6 +648,11 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_30
 include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_35,indent=0]
 ----
 
+[source, groovy]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_winfunction_36,indent=0]
+----
+
 === GINQ Tips
 ==== Row Number
 `_rn` is the implicit variable representing row number for each record in the result set. It starts with `0`
diff --git a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index e634e28..5bfc10d 100644
--- a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++ b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -4687,6 +4687,18 @@ class GinqTest {
     }
 
     @Test
+    void "testGinq - aggregate function - 14"() {
+        assertGinqScript '''
+// tag::ginq_aggfunction_04[]
+            assert [1] == GQ {
+                from n in [1, 2, 3]
+                select stdevp(n)
+            }.toList()
+// end::ginq_aggfunction_04[]
+        '''
+    }
+
+    @Test
     void "testGinq - parallel - 1"() {
         assertGinqScript '''
 // tag::ginq_tips_08[]
@@ -5710,6 +5722,18 @@ class GinqTest {
     }
 
     @Test
+    void "testGinq - window - 79"() {
+        assertGinqScript '''
+// tag::ginq_winfunction_36[]
+            assert [[1, 1], [2, 1], [3, 1]] == GQ {
+                from n in [1, 2, 3]
+                select n, (stdevp(n) over())
+            }.toList()
+// end::ginq_winfunction_36[]
+        '''
+    }
+
+    @Test
     void "testGinq - shutdown - 0"() {
         assertScript '''
             import org.apache.groovy.ginq.provider.collection.runtime.QueryableHelper