You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2020/09/16 06:35:00 UTC
[incubator-superset] 04/28: fix: support non-string groupbys for
pie chart (#10493)
This is an automated email from the ASF dual-hosted git repository.
villebro pushed a commit to branch 0.37
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
commit ddac4b88386b13d1aeececfb26aac4ad64709a67
Author: Ville Brofeldt <33...@users.noreply.github.com>
AuthorDate: Fri Jul 31 11:19:21 2020 +0300
fix: support non-string groupbys for pie chart (#10493)
* chore: add unit tests to pie chart
* refine logic for floats and nans and add more tests
---
superset/viz.py | 34 +++++++++++++++++++++++++++++++-
tests/viz_tests.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/superset/viz.py b/superset/viz.py
index 3ce9434..77b228b 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -1544,11 +1544,43 @@ class DistributionPieViz(NVD3Viz):
is_timeseries = False
def get_data(self, df: pd.DataFrame) -> VizData:
+ def _label_aggfunc(labels: pd.Series) -> str:
+ """
+ Convert a single or multi column label into a single label, replacing
+ null values with `NULL_STRING` and joining multiple columns together
+ with a comma. Examples:
+
+ >>> _label_aggfunc(pd.Series(["abc"]))
+ 'abc'
+ >>> _label_aggfunc(pd.Series([1]))
+ '1'
+ >>> _label_aggfunc(pd.Series(["abc", "def"]))
+ 'abc, def'
+ >>> # note: integer floats are stripped of decimal digits
+ >>> _label_aggfunc(pd.Series([0.1, 2.0, 0.3]))
+ '0.1, 2, 0.3'
+ >>> _label_aggfunc(pd.Series([1, None, "abc", 0.8], dtype="object"))
+ '1, <NULL>, abc, 0.8'
+ """
+ label_list: List[str] = []
+ for label in labels:
+ if isinstance(label, str):
+ label_recast = label
+ elif label is None or isinstance(label, float) and math.isnan(label):
+ label_recast = NULL_STRING
+ elif isinstance(label, float) and label.is_integer():
+ label_recast = str(int(label))
+ else:
+ label_recast = str(label)
+ label_list.append(label_recast)
+
+ return ", ".join(label_list)
+
if df.empty:
return None
metric = self.metric_labels[0]
df = pd.DataFrame(
- {"x": df[self.groupby].agg(func=", ".join, axis=1), "y": df[metric]}
+ {"x": df[self.groupby].agg(func=_label_aggfunc, axis=1), "y": df[metric]}
)
df.sort_values(by="y", ascending=False, inplace=True)
return df.to_dict(orient="records")
diff --git a/tests/viz_tests.py b/tests/viz_tests.py
index 17e43d8..b76c95c 100644
--- a/tests/viz_tests.py
+++ b/tests/viz_tests.py
@@ -20,6 +20,7 @@ from datetime import datetime
import logging
from math import nan
from unittest.mock import Mock, patch
+from typing import Any, Dict, List, Set
import numpy as np
import pandas as pd
@@ -1322,3 +1323,60 @@ class TestPivotTableViz(SupersetTestCase):
viz.PivotTableViz.get_aggfunc("strcol", self.df, {"pandas_aggfunc": "min"})
== "min"
)
+
+
+class TestDistributionPieViz(SupersetTestCase):
+ base_df = pd.DataFrame(
+ data={
+ "intcol": [1, 2, 3, 4, None],
+ "floatcol": [1.0, 0.2, 0.3, 0.4, None],
+ "strcol_a": ["a", "a", "a", "a", None],
+ "strcol": ["a", "b", "c", None, "d"],
+ }
+ )
+
+ @staticmethod
+ def get_cols(data: List[Dict[str, Any]]) -> Set[str]:
+ return set([row["x"] for row in data])
+
+ def test_bool_groupby(self):
+ datasource = self.get_datasource_mock()
+ df = pd.DataFrame(data={"intcol": [1, 2, None], "boolcol": [True, None, False]})
+
+ pie_viz = viz.DistributionPieViz(
+ datasource, {"metrics": ["intcol"], "groupby": ["boolcol"]},
+ )
+ data = pie_viz.get_data(df)
+ assert self.get_cols(data) == {"True", "False", "<NULL>"}
+
+ def test_string_groupby(self):
+ datasource = self.get_datasource_mock()
+ pie_viz = viz.DistributionPieViz(
+ datasource, {"metrics": ["floatcol"], "groupby": ["strcol"]},
+ )
+ data = pie_viz.get_data(self.base_df)
+ assert self.get_cols(data) == {"<NULL>", "a", "b", "c", "d"}
+
+ def test_int_groupby(self):
+ datasource = self.get_datasource_mock()
+ pie_viz = viz.DistributionPieViz(
+ datasource, {"metrics": ["floatcol"], "groupby": ["intcol"]},
+ )
+ data = pie_viz.get_data(self.base_df)
+ assert self.get_cols(data) == {"<NULL>", "1", "2", "3", "4"}
+
+ def test_float_groupby(self):
+ datasource = self.get_datasource_mock()
+ pie_viz = viz.DistributionPieViz(
+ datasource, {"metrics": ["intcol"], "groupby": ["floatcol"]},
+ )
+ data = pie_viz.get_data(self.base_df)
+ assert self.get_cols(data) == {"<NULL>", "1", "0.2", "0.3", "0.4"}
+
+ def test_multi_groupby(self):
+ datasource = self.get_datasource_mock()
+ pie_viz = viz.DistributionPieViz(
+ datasource, {"metrics": ["floatcol"], "groupby": ["intcol", "strcol"]},
+ )
+ data = pie_viz.get_data(self.base_df)
+ assert self.get_cols(data) == {"1, a", "2, b", "3, c", "4, <NULL>", "<NULL>, d"}