You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2026/08/17 16:14:44 UTC

(superset) 01/01: fix(pandas_postprocessing): map bare numpy callable aggregators to string names for GroupBy.agg

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

eschutho pushed a commit to branch fix-groupby-agg-median-callable-warning
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 05bb34ae78ad02768053f44f57af90fbf6c2cc4c
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Mon Aug 17 16:10:50 2026 +0000

    fix(pandas_postprocessing): map bare numpy callable aggregators to string names for GroupBy.agg
    
    A bare numpy callable operator (e.g. np.median) passed through to
    GroupBy.agg triggers a pandas FutureWarning:
    
      FutureWarning: The provided callable <function median ...> is currently
      using SeriesGroupBy.median. In a future version of pandas, the provided
      callable will be used directly. To keep current behavior pass the string
      "median" instead.
    
    The existing _PANDAS_STRING_AGGREGATORS fix only covered string operators;
    the callable-operator branch of _get_aggregate_funcs still forwarded the raw
    numpy callable. Add a reverse lookup (reusing NUMPY_FUNCTIONS and
    _PANDAS_STRING_AGGREGATORS) and swap such callables for their string name
    before calling .agg(). Non-mapped callables, string operators, and
    partial(func, **options) paths are unchanged, so results and dtypes are
    identical.
    
    Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
 superset/utils/pandas_postprocessing/utils.py       | 15 ++++++++++++++-
 .../pandas_postprocessing/test_aggregate.py         | 21 +++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/superset/utils/pandas_postprocessing/utils.py b/superset/utils/pandas_postprocessing/utils.py
index aa2667ed9d5..3ec1e743749 100644
--- a/superset/utils/pandas_postprocessing/utils.py
+++ b/superset/utils/pandas_postprocessing/utils.py
@@ -61,6 +61,15 @@ _PANDAS_STRING_AGGREGATORS: frozenset[str] = frozenset(
     {"max", "mean", "median", "min", "prod", "std", "sum", "var"}
 )
 
+# Reverse lookup from the numpy callables above to their pandas string name, so a
+# bare callable operator (e.g. np.median) can be swapped for the string form before
+# reaching GroupBy.agg and avoid the same FutureWarning.
+_STRING_AGGREGATOR_BY_CALLABLE: dict[Callable[..., Any], str] = {
+    func: name
+    for name, func in NUMPY_FUNCTIONS.items()
+    if name in _PANDAS_STRING_AGGREGATORS
+}
+
 DENYLIST_ROLLING_FUNCTIONS = (
     "count",
     "corr",
@@ -173,7 +182,11 @@ def _get_aggregate_funcs(
             )
         operator = agg_obj["operator"]
         if callable(operator):
-            aggfunc: str | Callable[..., Any] = operator
+            # A bare numpy callable (e.g. np.median) that pandas maps to its own
+            # GroupBy method triggers a FutureWarning; use the string name instead.
+            aggfunc: str | Callable[..., Any] = _STRING_AGGREGATOR_BY_CALLABLE.get(
+                operator, operator
+            )
         else:
             func = NUMPY_FUNCTIONS.get(operator)
             if not func:
diff --git a/tests/unit_tests/pandas_postprocessing/test_aggregate.py b/tests/unit_tests/pandas_postprocessing/test_aggregate.py
index 10ac70f4349..7a715010b2a 100644
--- a/tests/unit_tests/pandas_postprocessing/test_aggregate.py
+++ b/tests/unit_tests/pandas_postprocessing/test_aggregate.py
@@ -14,6 +14,10 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+import warnings
+
+import numpy as np
+
 from superset.utils.pandas_postprocessing import aggregate
 from tests.unit_tests.fixtures.dataframes import categories_df
 from tests.unit_tests.pandas_postprocessing.utils import series_to_list
@@ -56,6 +60,23 @@ def test_aggregate_string_operators():
     assert series_to_list(df["asc min"])[0] == 0
 
 
+def test_aggregate_callable_operator_avoids_future_warning():
+    """A bare numpy callable operator (e.g. np.median) that pandas maps to its own
+    GroupBy method must be swapped for the string form so GroupBy.agg does not raise
+    a FutureWarning, while producing the same result as the string operator."""
+    aggregates = {
+        "asc median": {"column": "asc_idx", "operator": np.median},
+        "asc mean": {"column": "asc_idx", "operator": np.mean},
+        "asc sum": {"column": "asc_idx", "operator": np.sum},
+    }
+    with warnings.catch_warnings():
+        warnings.simplefilter("error", FutureWarning)
+        df = aggregate(df=categories_df, groupby=["constant"], aggregates=aggregates)
+    assert series_to_list(df["asc median"])[0] == 50.0
+    assert series_to_list(df["asc mean"])[0] == 50.0
+    assert series_to_list(df["asc sum"])[0] == 5050
+
+
 def test_aggregate_count_includes_nulls():
     """'count' operator uses np.ma.count, which counts all rows including NaN.
     It is intentionally excluded from _PANDAS_STRING_AGGREGATORS to preserve this