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

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


The following commit(s) were added to refs/heads/master by this push:
     new 30f49981ef0 [SPARK-39048][PYTHON] Refactor 
GroupBy._reduce_for_stat_function on accepted data types
30f49981ef0 is described below

commit 30f49981ef0d71303b53f5358c021b94c6c4dc76
Author: Xinrong Meng <[email protected]>
AuthorDate: Fri Apr 29 17:56:25 2022 -0700

    [SPARK-39048][PYTHON] Refactor GroupBy._reduce_for_stat_function on 
accepted data types
    
    ### What changes were proposed in this pull request?
    `Groupby._reduce_for_stat_function` is a common helper function leveraged 
by multiple statistical functions of GroupBy objects.
    
    It defines parameters `only_numeric` and `bool_as_numeric` to control 
accepted Spark types.
    
    To be consistent with pandas API, we may also have to introduce 
`str_as_numeric` for `sum` for example.
    
    Instead of introducing parameters designated for each Spark type, the PR is 
proposed to introduce a parameter `accepted_spark_types` to specify accepted 
types of Spark columns to be aggregated.
    
    That makes the helper function more extensible and clearer.
    
    ### Why are the changes needed?
    To improve code extensibility and readability.
    
    ### Does this PR introduce _any_ user-facing change?
    No.
    
    ### How was this patch tested?
    Unit tests.
    
    Closes #36382 from xinrong-databricks/groupby.refactor_param.
    
    Authored-by: Xinrong Meng <[email protected]>
    Signed-off-by: Takuya UESHIN <[email protected]>
---
 python/pyspark/pandas/groupby.py | 62 +++++++++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 26 deletions(-)

diff --git a/python/pyspark/pandas/groupby.py b/python/pyspark/pandas/groupby.py
index 8071f1597c3..386b24c1916 100644
--- a/python/pyspark/pandas/groupby.py
+++ b/python/pyspark/pandas/groupby.py
@@ -37,6 +37,7 @@ from typing import (
     Sequence,
     Set,
     Tuple,
+    Type,
     Union,
     cast,
     TYPE_CHECKING,
@@ -56,6 +57,7 @@ else:
 from pyspark.sql import Column, DataFrame as SparkDataFrame, Window, functions 
as F
 from pyspark.sql.types import (
     BooleanType,
+    DataType,
     NumericType,
     StructField,
     StructType,
@@ -400,7 +402,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         1  2  3
         2  2  2
         """
-        return self._reduce_for_stat_function(F.count, only_numeric=False)
+        return self._reduce_for_stat_function(F.count)
 
     # TODO: We should fix See Also when Series implementation is finished.
     def first(self, numeric_only: Optional[bool] = False) -> FrameLike:
@@ -446,7 +448,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         2  False  3
         """
         return self._reduce_for_stat_function(
-            F.first, only_numeric=numeric_only, bool_as_numeric=True
+            F.first, accepted_spark_types=(NumericType, BooleanType) if 
numeric_only else None
         )
 
     def last(self, numeric_only: Optional[bool] = False) -> FrameLike:
@@ -493,8 +495,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         """
         return self._reduce_for_stat_function(
             lambda col: F.last(col, ignorenulls=True),
-            only_numeric=numeric_only,
-            bool_as_numeric=True,
+            accepted_spark_types=(NumericType, BooleanType) if numeric_only 
else None,
         )
 
     def max(self, numeric_only: Optional[bool] = False) -> FrameLike:
@@ -534,7 +535,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         2  True  4
         """
         return self._reduce_for_stat_function(
-            F.max, only_numeric=numeric_only, bool_as_numeric=True
+            F.max, accepted_spark_types=(NumericType, BooleanType) if 
numeric_only else None
         )
 
     # TODO: examples should be updated.
@@ -567,7 +568,9 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         1  3.0  1.333333  0.333333
         2  4.0  1.500000  1.000000
         """
-        return self._reduce_for_stat_function(F.mean, only_numeric=True, 
bool_to_numeric=True)
+        return self._reduce_for_stat_function(
+            F.mean, accepted_spark_types=(NumericType,), bool_to_numeric=True
+        )
 
     def min(self, numeric_only: Optional[bool] = False) -> FrameLike:
         """
@@ -605,7 +608,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         2  False  4
         """
         return self._reduce_for_stat_function(
-            F.min, only_numeric=numeric_only, bool_as_numeric=True
+            F.min, accepted_spark_types=(NumericType, BooleanType) if 
numeric_only else None
         )
 
     # TODO: sync the doc.
@@ -638,7 +641,9 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         assert ddof in (0, 1)
 
         return self._reduce_for_stat_function(
-            F.stddev_pop if ddof == 0 else F.stddev_samp, only_numeric=True, 
bool_to_numeric=True
+            F.stddev_pop if ddof == 0 else F.stddev_samp,
+            accepted_spark_types=(NumericType,),
+            bool_to_numeric=True,
         )
 
     def sum(self) -> FrameLike:
@@ -661,7 +666,9 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         pyspark.pandas.Series.groupby
         pyspark.pandas.DataFrame.groupby
         """
-        return self._reduce_for_stat_function(F.sum, only_numeric=True, 
bool_to_numeric=True)
+        return self._reduce_for_stat_function(
+            F.sum, accepted_spark_types=(NumericType,), bool_to_numeric=True
+        )
 
     # TODO: sync the doc.
     def var(self, ddof: int = 1) -> FrameLike:
@@ -693,7 +700,9 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         assert ddof in (0, 1)
 
         return self._reduce_for_stat_function(
-            F.var_pop if ddof == 0 else F.var_samp, only_numeric=True, 
bool_to_numeric=True
+            F.var_pop if ddof == 0 else F.var_samp,
+            accepted_spark_types=(NumericType,),
+            bool_to_numeric=True,
         )
 
     # TODO: skipna should be implemented.
@@ -735,7 +744,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         5  False
         """
         return self._reduce_for_stat_function(
-            lambda col: F.min(F.coalesce(col.cast("boolean"), SF.lit(True))), 
only_numeric=False
+            lambda col: F.min(F.coalesce(col.cast("boolean"), SF.lit(True)))
         )
 
     # TODO: skipna should be implemented.
@@ -777,7 +786,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         5  False
         """
         return self._reduce_for_stat_function(
-            lambda col: F.max(F.coalesce(col.cast("boolean"), SF.lit(False))), 
only_numeric=False
+            lambda col: F.max(F.coalesce(col.cast("boolean"), SF.lit(False)))
         )
 
     # TODO: groupby multiply columns should be implemented.
@@ -2528,7 +2537,7 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
                     F.count(F.when(col.isNull(), 1).otherwise(None)) >= 1, 1
                 ).otherwise(0)
 
-        return self._reduce_for_stat_function(stat_function, 
only_numeric=False)
+        return self._reduce_for_stat_function(stat_function)
 
     def rolling(
         self, window: int, min_periods: Optional[int] = None
@@ -2732,14 +2741,15 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
             return F.percentile_approx(col, 0.5, accuracy)
 
         return self._reduce_for_stat_function(
-            stat_function, only_numeric=numeric_only, bool_to_numeric=True
+            stat_function,
+            accepted_spark_types=(NumericType,) if numeric_only else None,
+            bool_to_numeric=True,
         )
 
     def _reduce_for_stat_function(
         self,
         sfun: Callable[[Column], Column],
-        only_numeric: Optional[bool] = None,
-        bool_as_numeric: bool = False,
+        accepted_spark_types: Optional[Tuple[Type[DataType], ...]] = None,
         bool_to_numeric: bool = False,
     ) -> FrameLike:
         """Apply an aggregate function `sfun` per column and reduce to a 
FrameLike.
@@ -2747,23 +2757,23 @@ class GroupBy(Generic[FrameLike], metaclass=ABCMeta):
         Parameters
         ----------
         sfun : The aggregate function to apply per column
-        only_numeric: If True, only numeric columns are involved
-        bool_as_numeric: If True, boolean columns are seen as numeric columns 
(following pandas)
-        bool_to_numeric: If True, boolean columns are converted to numeric 
columns
+        accepted_spark_types: Accepted spark types of columns to be aggregated;
+                              default None means all spark types are accepted
+        bool_to_numeric: If True, boolean columns are converted to numeric 
columns, which
+                         are accepted for all statistical functions regardless 
of
+                         `accepted_spark_types`.
         """
         groupkey_names = [SPARK_INDEX_NAME_FORMAT(i) for i in 
range(len(self._groupkeys))]
         groupkey_scols = [s.alias(name) for s, name in 
zip(self._groupkeys_scols, groupkey_names)]
 
         agg_columns = []
         for psser in self._agg_columns:
-            if (
-                isinstance(psser.spark.data_type, NumericType)
-                or (bool_as_numeric and isinstance(psser.spark.data_type, 
BooleanType))
-                or not only_numeric
+            if bool_to_numeric and isinstance(psser.spark.data_type, 
BooleanType):
+                agg_columns.append(psser.astype(int))
+            elif (accepted_spark_types is None) or isinstance(
+                psser.spark.data_type, accepted_spark_types
             ):
                 agg_columns.append(psser)
-            elif bool_to_numeric and isinstance(psser.spark.data_type, 
BooleanType):
-                agg_columns.append(psser.astype(int))
 
         sdf = self._psdf._internal.spark_frame.select(
             *groupkey_scols, *[psser.spark.column for psser in agg_columns]
@@ -3476,7 +3486,7 @@ class SeriesGroupBy(GroupBy[Series]):
         3    [3, 4]
         Name: b, dtype: object
         """
-        return self._reduce_for_stat_function(F.collect_set, 
only_numeric=False)
+        return self._reduce_for_stat_function(F.collect_set)
 
 
 def is_multi_agg_with_relabel(**kwargs: Any) -> bool:


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to