Repository: sfu-db/dataprep
Issue: Category column throws TypeError: Categorical cannot perform the operation all


The dataset has a datetime index and is all floats and ints apart from one categorical column:

```
 23  rh_diff             84 non-null     float64 
 24  we_count_diff           84 non-null     float64 
 25  ae_count_diff           84 non-null     float64 
 26  month                   85 non-null     category
```

which is generated:

    df['month'] = df.index.strftime('%Y/%m %b').astype('category')


**To Reproduce**
Running in Jupyter notebook inside Pycharm:

        create_report(df)

Gives error trace below.  Remove this column or create it as:

    df['month'] = df.index.strftime('%Y/%m %b').astype('object')

and it works fine.  

```python
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-2915-463fb2fdfb17> in <module>
----> 1 create_report(df)

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/__init__.py in create_report(df, config, display, title, mode, progress)
     66         "resources": INLINE.render(),
     67         "title": title,
---> 68         "components": format_report(df, cfg, mode, progress),
     69     }
     70     template_base = ENV_LOADER.get_template("base.html")

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in format_report(df, cfg, mode, progress)
     71         if mode == "basic":
     72             edaframe = EDAFrame(df)
---> 73             comps = format_basic(edaframe, cfg)
     74         # elif mode == "full":
     75         #     comps = format_full(df)

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in format_basic(df, cfg)
    103     setattr(getattr(cfg, "plot"), "report", True)
    104     if cfg.missingvalues.enable:
--> 105         data, completions = basic_computations(df, cfg)
    106     else:
    107         data = basic_computations(df, cfg)

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in basic_computations(df, cfg)
    281                 data[col] = nom_comps(srs, srs.head(), cfg)
    282             elif isinstance(dtype, Nominal):
--> 283                 data[col] = nom_comps(df.frame[col], head[col], cfg)
    284             elif isinstance(dtype, SmallCardNum):
    285                 srs = df.get_col_as_str(col, na_as_str=False)

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/distribution/compute/univariate.py in nom_comps(srs, head, cfg)
    143 
    144     if cfg.stats.enable or cfg.wordlen.enable:
--> 145         if not head.apply(lambda x: isinstance(x, str)).all():
    146             srs = srs.astype(str)  # srs must be a string to compute the value lengths
    147     if cfg.stats.enable or cfg.wordcloud.enable or cfg.wordfreq.enable:

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/generic.py in logical_func(self, axis, bool_only, skipna, level, **kwargs)
  11363 
  11364     # ----------------------------------------------------------------------
> 11365     # Misc methods
  11366 
  11367     @final

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/series.py in _reduce(self, op, name, axis, skipna, numeric_only, filter_type, **kwds)
   3870         --------
   3871         >>> s = pd.Series(['cat', 'dog', np.nan, 'rabbit'])
-> 3872         >>> s
   3873         0      cat
   3874         1      dog

~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/arrays/categorical.py in _reduce(self, name, axis, **kwargs)
   2122         cls: Type[CategoricalT], to_concat: Sequence[CategoricalT], axis: int = 0
   2123     ) -> CategoricalT:
-> 2124         from pandas.core.dtypes.concat import union_categoricals
   2125 
   2126         return union_categoricals(to_concat)

TypeError: Categorical cannot perform the operation all
```

**Desktop (please complete the following information):**
 - OS: OSX
 - Browser: Chrome/Pycharm
 - Platform Jupyter Notebook
 - Platform Version 
      jupyter==1.0.0
      jupyter-client==6.1.11
      jupyter-console==6.2.0
      jupyter-core==4.7.0
      jupyter-dash==0.4.0
      jupyterlab-pygments==0.1.2
      jupyterlab-widgets==1.0.0

 - Python Version 3.7.9
 - Dataprep Version  dataprep-0.3.0



To reproduce, sort of, giving a slightly different error:

        #%%
        import pandas as pd
        from datetime import datetime
        from dataprep.eda import create_report
        from dataprep.eda import plot_missing
        from dataprep.tests.eda.random_data_generator import gen_test_df
        df = gen_test_df()
        #df['month'] = pd.Categorical(datetime.now().strftime('%Y/%m %b')) #ValueError: Length of values (1) does not match length of index (30)
        df['month'] = datetime.now().strftime('%Y/%m %b')
        df['month'] = df.month.astype('category')
        df.info()
        
        #%%
        create_report(df)

Error is now:

TypeError: 'Categorical' does not implement reduction 'all'

        
        ---------------------------------------------------------------------------
        
        TypeError                                 Traceback (most recent call last)
        
        <ipython-input-15-463fb2fdfb17> in <module>
        ----> 1 create_report(df)
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/__init__.py in create_report(df, config, display, title, mode, progress)
             66         "resources": INLINE.render(),
             67         "title": title,
        ---> 68         "components": format_report(df, cfg, mode, progress),
             69     }
             70     template_base = ENV_LOADER.get_template("base.html")
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in format_report(df, cfg, mode, progress)
             71         if mode == "basic":
             72             edaframe = EDAFrame(df)
        ---> 73             comps = format_basic(edaframe, cfg)
             74         # elif mode == "full":
             75         #     comps = format_full(df)
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in format_basic(df, cfg)
            103     setattr(getattr(cfg, "plot"), "report", True)
            104     if cfg.missingvalues.enable:
        --> 105         data, completions = basic_computations(df, cfg)
            106     else:
            107         data = basic_computations(df, cfg)
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/create_report/formatter.py in basic_computations(df, cfg)
            281                 data[col] = nom_comps(srs, srs.head(), cfg)
            282             elif isinstance(dtype, Nominal):
        --> 283                 data[col] = nom_comps(df.frame[col], head[col], cfg)
            284             elif isinstance(dtype, SmallCardNum):
            285                 srs = df.get_col_as_str(col, na_as_str=False)
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/dataprep/eda/distribution/compute/univariate.py in nom_comps(srs, head, cfg)
            143 
            144     if cfg.stats.enable or cfg.wordlen.enable:
        --> 145         if not head.apply(lambda x: isinstance(x, str)).all():
            146             srs = srs.astype(str)  # srs must be a string to compute the value lengths
            147     if cfg.stats.enable or cfg.wordcloud.enable or cfg.wordfreq.enable:
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/generic.py in all(self, axis, bool_only, skipna, level, **kwargs)
          10884         )
          10885         def all(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs):
        > 10886             return NDFrame.all(self, axis, bool_only, skipna, level, **kwargs)
          10887 
          10888         # pandas\\core\\generic.py:10719: error: Cannot assign to a method
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/generic.py in all(self, axis, bool_only, skipna, level, **kwargs)
          10598     def all(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs):
          10599         return self._logical_func(
        > 10600             "all", nanops.nanall, axis, bool_only, skipna, level, **kwargs
          10601         )
          10602 
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/generic.py in _logical_func(self, name, func, axis, bool_only, skipna, level, **kwargs)
          10588             skipna=skipna,
          10589             numeric_only=bool_only,
        > 10590             filter_type="bool",
          10591         )
          10592 
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/series.py in _reduce(self, op, name, axis, skipna, numeric_only, filter_type, **kwds)
           4171         if isinstance(delegate, ExtensionArray):
           4172             # dispatch to ExtensionArray interface
        -> 4173             return delegate._reduce(name, skipna=skipna, **kwds)
           4174 
           4175         else:
        
        ~/PycharmProjects/.venv/tinycloud379/lib/python3.7/site-packages/pandas/core/arrays/_mixins.py in _reduce(self, name, skipna, **kwargs)
            274         else:
            275             msg = f"'{type(self).__name__}' does not implement reduction '{name}'"
        --> 276             raise TypeError(msg)
            277 
            278     def _wrap_reduction_result(self, axis: Optional[int], result):
        
        TypeError: 'Categorical' does not implement reduction 'all'



Changed files:
dataprep/eda/create_report/formatter.py, dataprep/eda/distribution/compute/univariate.py, dataprep/eda/distribution/compute/overview.py, dataprep/eda/distribution/compute/__init__.py, dataprep/eda/eda_frame.py, dataprep/tests/eda/random_data_generator.py

diff --git a/dataprep/eda/create_report/formatter.py b/dataprep/eda/create_report/formatter.py
index 0334df52..b733b8bc 100644
--- a/dataprep/eda/create_report/formatter.py
+++ b/dataprep/eda/create_report/formatter.py
@@ -1,6 +1,7 @@
 """This module implements the formatting
 for create_report(df) function."""  # pylint: disable=line-too-long,
 
+import sys
 from typing import Any, Dict, List, Optional, Tuple, Union
 from warnings import catch_warnings, filterwarnings
 
@@ -80,68 +81,17 @@ def format_report(
     return comps
 
 
-def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
-    """
-    Format basic version.
-
-    Parameters
-    ----------
-    df
-        The DataFrame for which data are calculated.
-    cfg
-        The config dict user passed in. E.g. config =  {"hist.bins": 20}
-        Without user's specifications, the default is "auto"
-    Returns
-    -------
-    Dict[str, Any]
-        A dictionary in which formatted data is stored.
-        This variable acts like an API in passing data to the template engine.
-    """
-    # pylint: disable=too-many-locals,too-many-statements,too-many-branches
-    # aggregate all computations
-
-    setattr(getattr(cfg, "plot"), "report", True)
-    if cfg.missingvalues.enable:
-        data, completions = basic_computations(df, cfg)
-    else:
-        data = basic_computations(df, cfg)
-    with catch_warnings():
-        filterwarnings(
-            "ignore",
-            "invalid value encountered in true_divide",
-            category=RuntimeWarning,
-        )
-        filterwarnings(
-            "ignore",
-            "overflow encountered in long_scalars",
-            category=RuntimeWarning,
-        )
-        (data,) = dask.compute(data)
-    # results dictionary
+def _format_variables(df: EDAFrame, cfg: Config, data: Dict[str, Any]) -> Dict[str, Any]:
     res: Dict[str, Any] = {}
-    # overview
-    if cfg.overview.enable:
-        # insight
-        all_ins = _format_ov_ins(data["ov"], cfg)
-        for col, dtp, dat in data["insights"]:
-            if isinstance(dtp, Continuous):
-                ins = _format_cont_ins(col, dat, data["ov"]["nrows"], cfg)[1]
-            elif type(dtp) in [Nominal, SmallCardNum, GeoGraphy, GeoPoint]:
-                ins = _format_nom_ins(col, dat, data["ov"]["nrows"], cfg)[1]
-            else:
-                continue
-            all_ins += ins
-        res["overview_insights"] = _insight_pagination(all_ins)
-        res["overview"] = format_ov_stats(data["ov"])
-        res["has_overview"] = True
-    else:
-        res["has_overview"] = False
-
     # variables
-    if cfg.variables.enable:
-        res["variables"] = {}
-        res["has_variables"] = True
-        for col in df.columns:
+    if not cfg.variables.enable:
+        res["has_variables"] = False
+        return res
+
+    res["variables"] = {}
+    res["has_variables"] = True
+    for col in df.columns:
+        try:
             stats: Any = None  # needed for pylint
             dtp = df.get_eda_dtype(col)
             if isinstance(dtp, Continuous):
@@ -183,9 +133,17 @@ def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
                 "plots_tab": zip(comp[1][1:], rndrd["meta"][1:], insight_keys),
                 "insights_tab": rndrd["insights"],
             }
-    else:
-        res["has_variables"] = False
 
+        except:
+            print(f"error happended in column:{col}", file=sys.stderr)
+            raise
+
+    return res
+
+
+def _format_interaction(data: Dict[str, Any], cfg: Config) -> Dict[str, Any]:
+    """Format of Interaction section"""
+    res: Dict[str, Any] = {}
     if len(data["num_cols"]) > 0:
         # interactions
         if cfg.interactions.enable:
@@ -194,7 +152,15 @@ def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
             rndrd = render_correlation(itmdt, cfg)
             rndrd.sizing_mode = "stretch_width"
             res["interactions"] = components(rndrd)
+        else:
+            res["has_interaction"] = False
+    return res
+
 
+def _format_correlation(data: Dict[str, Any], cfg: Config) -> Dict[str, Any]:
+    """Format of Correlation section"""
+    res: Dict[str, Any] = {}
+    if len(data["num_cols"]) > 0:
         # correlations
         if cfg.correlations.enable:
             res["has_correlation"] = True
@@ -224,10 +190,18 @@ def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
             res["correlations"] = components(figs_corr)
 
     else:
-        res["has_interaction"], res["has_correlation"] = False, False
+        res["has_correlation"] = False
 
+    return res
+
+
+def _format_missing(
+    data: Dict[str, Any], cfg: Config, completions: Optional[Dict[str, Any]], ncols: int
+) -> Dict[str, Any]:
+    """Format of Missing section"""
+    res: Dict[str, Any] = {}
     # missing
-    if cfg.missingvalues.enable:
+    if cfg.missingvalues.enable and completions is not None:
         res["has_missing"] = True
         itmdt = completions["miss"](data["miss"])
 
@@ -239,81 +213,170 @@ def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
         res["missing"] = components(figs_missing)
         res["missing_tabs"] = ["Bar Chart", "Spectrum", "Heat Map"]
         # only display dendrogram when df has more than one column
-        if df.shape[1] > 1:
+        if ncols > 1:
             res["missing_tabs"].append("Dendogram")
+    return res
+
 
+def _format_overview(data: Dict[str, Any], cfg: Config) -> Dict[str, Any]:
+    """Format of Overview section"""
+    # results dictionary
+    res: Dict[str, Any] = {}
+    # overview
+    if cfg.overview.enable:
+        # insight
+        all_ins = _format_ov_ins(data["ov"], cfg)
+        for col, dtp, dat in data["insights"]:
+            if isinstance(dtp, Continuous):
+                ins = _format_cont_ins(col, dat, data["ov"]["nrows"], cfg)[1]
+            elif type(dtp) in [Nominal, SmallCardNum, GeoGraphy, GeoPoint]:
+                ins = _format_nom_ins(col, dat, data["ov"]["nrows"], cfg)[1]
+            else:
+                continue
+            all_ins += ins
+        res["overview_insights"] = _insight_pagination(all_ins)
+        res["overview"] = format_ov_stats(data["ov"])
+        res["has_overview"] = True
+    else:
+        res["has_overview"] = False
     return res
 
 
-def basic_computations(
-    df: EDAFrame, cfg: Config
-) -> Union[Tuple[Dict[str, Any], Dict[str, Any]], Any]:
-    """Computations for the basic version.
+def format_basic(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
+    """
+    Format basic version.
 
     Parameters
     ----------
     df
         The DataFrame for which data are calculated.
-    df_num
-        The DataFrame of numerical column (used for correlation). It is seperated from df since
-        the small distinct value numerical column in df is regarded as categorical column, and
-        will transform to str then used for other plots. But they should be regarded as numerical
-        column in df_num and used in correlation. This is a temporary fix, in the future we should treat
-        those small distinct value numerical columns as ordinary in both correlation plots and other plots.
     cfg
         The config dict user passed in. E.g. config =  {"hist.bins": 20}
         Without user's specifications, the default is "auto"
-    """  # pylint: disable=too-many-branches
-    data: Dict[str, Any] = {}
-    df_num = df.select_num_columns()
-    data["num_cols"] = df_num.columns
-    head: pd.DataFrame = df.head()
+    Returns
+    -------
+    Dict[str, Any]
+        A dictionary in which formatted data is stored.
+        This variable acts like an API in passing data to the template engine.
+    """
+    # pylint: disable=too-many-locals,too-many-statements,too-many-branches
+    # aggregate all computations
 
+    setattr(getattr(cfg, "plot"), "report", True)
+    data, completions = basic_computations(df, cfg)
+    with catch_warnings():
+        filterwarnings(
+            "ignore",
+            "invalid value encountered in true_divide",
+            category=RuntimeWarning,
+        )
+        filterwarnings(
+            "ignore",
+            "overflow encountered in long_scalars",
+            category=RuntimeWarning,
+        )
+        (data,) = dask.compute(data)
+
+    res_overview = _format_overview(data, cfg)
+    res_variables = _format_variables(df, cfg, data)
+    res_interaction = _format_interaction(data, cfg)
+    res_correlations = _format_correlation(data, cfg)
+    res_missing = _format_missing(data, cfg, completions, df.shape[1])
+    res = {**res_overview, **res_variables, **res_interaction, **res_correlations, **res_missing}
+
+    return res
+
+
+def _compute_variables(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
+    """Computation of Variables section."""
+    data: Dict[str, Any] = {}
     # variables
     if cfg.variables.enable:
         for col in df.columns:
-            dtype = df.get_eda_dtype(col)
-            # Since it will throw error if a numerical column is all-nan,
-            # we transform it to categorical column.
-            # We also transform to categorical for small cardinality numerical column.
-            if df.get_missing_cnt(col) == df.shape[0]:
-                srs = df.get_col_as_str(col, na_as_str=True)
-                data[col] = nom_comps(srs, srs.head(), cfg)
-            elif isinstance(dtype, Nominal):
-                data[col] = nom_comps(df.frame[col], head[col], cfg)
-            elif isinstance(dtype, SmallCardNum):
-                srs = df.get_col_as_str(col, na_as_str=False)
-                data[col] = nom_comps(srs, srs.head(), cfg)
-            elif isinstance(dtype, GeoGraphy):
-                data[col] = nom_comps(df.frame[col], head[col], cfg)
-            elif isinstance(dtype, GeoPoint):
-                data[col] = nom_comps(df.frame[col], head[col], cfg)
-            elif isinstance(dtype, Continuous):
-                data[col] = cont_comps(df.frame[col], cfg)
-            elif isinstance(dtype, DateTime):
-                data[col] = {}
-                data[col]["stats"] = calc_stats_dt(df.frame[col])
-                data[col]["line"] = dask.delayed(_calc_line_dt)(df.frame[[col]], "auto")
+            try:
+                dtype = df.get_eda_dtype(col)
+                # Since it will throw error if a numerical column is all-nan,
+                # we transform it to categorical column.
+                # We also transform to categorical for small cardinality numerical column.
+                if df.get_missing_cnt(col) == df.shape[0]:
+                    srs = df.get_col_as_str(col, na_as_str=True)
+                    data[col] = nom_comps(srs, cfg)
+                elif isinstance(dtype, (Nominal, GeoGraphy, GeoPoint)):
+                    data[col] = nom_comps(df.frame[col], cfg)
+                elif isinstance(dtype, SmallCardNum):
+                    srs = df.get_col_as_str(col, na_as_str=False)
+                    data[col] = nom_comps(srs, cfg)
+                elif isinstance(dtype, Continuous):
+                    data[col] = cont_comps(df.frame[col], cfg)
+                elif isinstance(dtype, DateTime):
+                    data[col] = {}
+                    data[col]["stats"] = calc_stats_dt(df.frame[col])
+                    data[col]["line"] = dask.delayed(_calc_line_dt)(df.frame[[col]], "auto")
+                else:
+                    raise ValueError(f"unprocessed type in column{col}:{dtype}")
+            except:
+                print(f"error happended in column:{col}", file=sys.stderr)
+                raise
+    return data
+
+
+def _compute_overview(df: EDAFrame, cfg: Config) -> Dict[str, Any]:
+    """Computation of Overview section."""
+    data: Dict[str, Any] = {}
     # overview
     if cfg.overview.enable:
         data["ov"] = calc_stats(df, cfg)
         data["insights"] = []
         for col in df.columns:
             col_dtype = df.get_eda_dtype(col)
-            if isinstance(col_dtype, Continuous):
+
+            # when srs is full NA, the column type will be float
+            # and need to be transformed into str.
+            if df.get_missing_cnt(col) == df.shape[0]:
+                srs = df.get_col_as_str(col, na_as_str=False).dropna().astype(str)
+                data["insights"].append((col, Nominal(), _nom_calcs(srs, cfg)))
+            elif isinstance(col_dtype, Continuous):
                 data["insights"].append(
                     (col, Continuous(), _cont_calcs(df.frame[col].dropna(), cfg))
                 )
-            elif type(col_dtype) in [Nominal, GeoGraphy, GeoPoint, SmallCardNum]:
+            elif isinstance(col_dtype, (Nominal, GeoGraphy, GeoPoint, SmallCardNum)):
                 srs = df.get_col_as_str(col, na_as_str=False).dropna()
-                data["insights"].append((col, Nominal(), _nom_calcs(srs, head[col], cfg)))
+                data["insights"].append((col, Nominal(), _nom_calcs(srs, cfg)))
             elif isinstance(col_dtype, DateTime):
                 data["insights"].append(
                     (col, DateTime(), dask.delayed(_calc_line_dt)(df.frame[[col]], cfg.line.unit))
                 )
             else:
                 raise RuntimeError(f"unprocessed data type: col:{col}, dtype: {type(col_dtype)}")
+    return data
+
+
+def basic_computations(
+    df: EDAFrame, cfg: Config
+) -> Tuple[Dict[str, Any], Optional[Dict[str, Any]]]:
+    """Computations for the basic version.
 
+    Parameters
+    ----------
+    df
+        The DataFrame for which data are calculated.
+    df_num
+        The DataFrame of numerical column (used for correlation). It is seperated from df since
+        the small distinct value numerical column in df is regarded as categorical column, and
+        will transform to str then used for other plots. But they should be regarded as numerical
+        column in df_num and used in correlation. This is a temporary fix, in the future we should treat
+        those small distinct value numerical columns as ordinary in both correlation plots and other plots.
+    cfg
+        The config dict user passed in. E.g. config =  {"hist.bins": 20}
+        Without user's specifications, the default is "auto"
+    """  # pylint: disable=too-many-branches
+
+    variables_data = _compute_variables(df, cfg)
+    overview_data = _compute_overview(df, cfg)
+    data: Dict[str, Any] = {**variables_data, **overview_data}
+
+    df_num = df.select_num_columns()
+    data["num_cols"] = df_num.columns
     # interactions
     if cfg.interactions.enable:
         data["scat"] = df_num.frame.map_partitions(
@@ -322,7 +385,9 @@ def basic_computations(
     # correlations
     if cfg.correlations.enable:
         data.update(zip(("cordx", "cordy", "corrs"), correlation_nxn(df_num, cfg)))
+
     # missing values
+    completions = None
     if cfg.missingvalues.enable:
         (
             delayed,
@@ -332,30 +397,5 @@ def basic_computations(
         )
         data["miss"] = delayed
         completions = {"miss": completion}
-        return data, completions
 
-    else:
-        return data
-
-
-# def cont_comps_brief(srs: dd.Series, cfg: Config) -> Dict[str, Any]:
-#
-#     """
-#     Computations required for constant column and all-nan column
-#     """
-#     # pylint: disable=too-many-branches
-#     data: Dict[str, Any] = {}
-#
-#     data["nrows"] = srs.shape[0]  # total rows
-#     srs = srs.dropna()
-#     data["npres"] = srs.shape[0]  # number of present (not null) values
-#     data["mean"] = srs.mean()
-#     data["min"] = srs.min()
-#     data["max"] = srs.max()
-#     data["nreals"] = srs.shape[0]
-#     data["nzero"] = (srs == 0).sum()
-#     data["nneg"] = (srs < 0).sum()
-#     data["mem_use"] = srs.memory_usage(deep=True)
-#     data["nuniq"] = srs.nunique_approx()
-#
-#     return data
+    return data, completions
diff --git a/dataprep/eda/distribution/compute/__init__.py b/dataprep/eda/distribution/compute/__init__.py
index b0d02d66..e7fc309b 100644
--- a/dataprep/eda/distribution/compute/__init__.py
+++ b/dataprep/eda/distribution/compute/__init__.py
@@ -100,3 +100,9 @@ def suppress_warnings() -> None:
         "The default value of regex will change from True to False in a future version",
         category=FutureWarning,
     )
+
+    warnings.filterwarnings(
+        "ignore",
+        "invalid value encountered in true_divide",
+        category=RuntimeWarning,
+    )
diff --git a/dataprep/eda/distribution/compute/overview.py b/dataprep/eda/distribution/compute/overview.py
index bfe94815..227db6a7 100644
--- a/dataprep/eda/distribution/compute/overview.py
+++ b/dataprep/eda/distribution/compute/overview.py
@@ -49,7 +49,6 @@ def compute_overview(
     # pylint: disable=too-many-locals
 
     frame = EDAFrame(df, dtype=dtype)
-    head: pd.DataFrame = frame.head()
 
     data: List[Tuple[str, DType, Any]] = []
     for col in frame.columns:
@@ -60,7 +59,11 @@ def compute_overview(
             cfg.bar.enable or cfg.insight.enable
         ):
             srs = frame.get_col_as_str(col).dropna()
-            data.append((col, col_dtype, _nom_calcs(srs, head[col], cfg)))
+            # when srs is full NA, the column type will be float
+            # and need to be transformed into str.
+            if frame.get_missing_cnt(col) == frame.shape[0]:
+                srs = srs.astype(str)
+            data.append((col, col_dtype, _nom_calcs(srs, cfg)))
         elif isinstance(col_dtype, DateTime) and (cfg.line.enable or cfg.insight.enable):
             data.append(
                 (col, col_dtype, dask.delayed(_calc_line_dt)(frame.frame[[col]], cfg.line.unit))
@@ -131,9 +134,9 @@ def _cont_calcs(srs: dd.Series, cfg: Config) -> Dict[str, Any]:
     return data
 
 
-def _nom_calcs(srs: dd.Series, head: pd.Series, cfg: Config) -> Dict[str, Any]:
+def _nom_calcs(srs: dd.Series, cfg: Config) -> Dict[str, Any]:
     """
-    Computations for a nominal column in plot(df)
+    Computations for a nominal column in plot(df). Assume srs is string column.
     """
     # dictionary of data for the bar chart and related insights
     data: Dict[str, Any] = {}
@@ -152,9 +155,8 @@ def _nom_calcs(srs: dd.Series, head: pd.Series, cfg: Config) -> Dict[str, Any]:
         data["chisq"] = chisquare(grps.values)  # chi-squared test for uniformity
         data["nuniq"] = grps.shape[0]  # number of unique values
         data["npres"] = grps.sum()  # number of present (not null) values
-        if not head.apply(lambda x: isinstance(x, str)).all():
-            srs = srs.astype(str)  # srs must be a string to compute the value lengths
-        data["min_len"], data["max_len"] = srs.str.len().min(), srs.str.len().max()
+        data["min_len"] = srs.str.len().min()
+        data["max_len"] = srs.str.len().max()
 
     return data
 
diff --git a/dataprep/eda/distribution/compute/univariate.py b/dataprep/eda/distribution/compute/univariate.py
index ac3afef7..cef777bd 100644
--- a/dataprep/eda/distribution/compute/univariate.py
+++ b/dataprep/eda/distribution/compute/univariate.py
@@ -65,8 +65,7 @@ def compute_univariate(
 
     if isinstance(col_dtype, (Nominal, GeoPoint, SmallCardNum)):
         srs = frame.get_col_as_str(x)
-        head = frame.head()[x]
-        (data,) = dask.compute(nom_comps(srs, head, cfg))
+        (data,) = dask.compute(nom_comps(srs, cfg))
         return Intermediate(col=x, data=data, visual_type="categorical_column")
 
     elif isinstance(col_dtype, Continuous):
@@ -91,16 +90,15 @@ def compute_univariate(
             visual_type="datetime_column",
         )
     elif isinstance(col_dtype, GeoGraphy):
-        head = frame.head()[x]
-        (data,) = dask.compute(nom_comps(frame.frame[x], head, cfg))
+        (data,) = dask.compute(nom_comps(frame.frame[x], cfg))
         return Intermediate(col=x, data=data, visual_type="geography_column")
     else:
         raise ValueError(f"unprocessed type. col:{x}, type:{col_dtype}")
 
 
-def nom_comps(srs: dd.Series, head: pd.Series, cfg: Config) -> Dict[str, Any]:
+def nom_comps(srs: dd.Series, cfg: Config) -> Dict[str, Any]:
     """
-    All computations required for plot(df, Nominal)
+    All computations required for plot(df, Nominal). Assume srs is string column.
     """
     # pylint: disable=too-many-branches
     data: Dict[str, Any] = dict()
@@ -140,14 +138,6 @@ def nom_comps(srs: dd.Series, head: pd.Series, cfg: Config) -> Dict[str, Any]:
             data["chisq"] = chisquare(grps.values)
 
     df = grps.reset_index()  # dataframe with group names and counts
-
-    if cfg.stats.enable or cfg.wordlen.enable:
-        if not head.apply(lambda x: isinstance(x, str)).all():
-            srs = srs.astype(str)  # srs must be a string to compute the value lengths
-    if cfg.stats.enable or cfg.wordcloud.enable or cfg.wordfreq.enable:
-        if not head.apply(lambda x: isinstance(x, str)).all():
-            df[df.columns[0]] = df[df.columns[0]].astype(str)
-
     if cfg.stats.enable:
         data.update(_calc_nom_stats(srs, df, data["nrows"], data["nuniq"]))
     elif cfg.wordfreq.enable and cfg.insight.enable:
diff --git a/dataprep/eda/eda_frame.py b/dataprep/eda/eda_frame.py
index 5a4a8d75..37c1aec4 100644
--- a/dataprep/eda/eda_frame.py
+++ b/dataprep/eda/eda_frame.py
@@ -180,7 +180,7 @@ class EDAFrame:
             return self._str_col_cache[(col, na_as_str)]
 
         # The case for directly return
-        if (isinstance(self._eda_dtypes[col], Nominal)) and (
+        if (isinstance(self._eda_dtypes[col], (Nominal, GeoGraphy))) and (
             (na_as_str and self.get_missing_cnt(col) == 0) or (not na_as_str)
         ):
             return self._ddf[col]
diff --git a/dataprep/tests/eda/random_data_generator.py b/dataprep/tests/eda/random_data_generator.py
index ba6477d1..34574ab8 100644
--- a/dataprep/tests/eda/random_data_generator.py
+++ b/dataprep/tests/eda/random_data_generator.py
@@ -212,7 +212,8 @@ def gen_test_df() -> pd.DataFrame:
     data[11] = gen_random_series(size=nrows, dtype="float", na_ratio=0.1, random_state=rand).rename(
         "num_miss"
     )
-
+    data[12] = pd.Series(["a", "b"] * (nrows // 2), name="category_no_miss", dtype="category")
+    data[13] = pd.Series(["a", np.nan] * (nrows // 2), name="category_miss", dtype="category")
     df = pd.concat(data.values(), axis=1)
     df.index = gen_random_series(df.index.shape[0], na_ratio=0.1, str_max_len=100, random_state=2)
     return df