Repository: sfu-db/dataprep
Issue: Cryptic error message when a column contains only NaN

**Describe the bug**
When a column of the dataframe passed to plot contains only NaNs, a cryptic error message is shown.

**To Reproduce**
```
nan_value = float("NaN")
>>> df = pd.DataFrame(5 * [[1, nan_value]]
... )
>>> df
   0   1
0  1 NaN
1  1 NaN
2  1 NaN
3  1 NaN
4  1 NaN
>>> rep = plot(df)
C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\numpy\\lib\\histograms.py:433: RuntimeWarning: invalid value encountered in greater
  if np.any(bin_edges[:-1] > bin_edges[1:]):
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\dataprep\\eda\\distribution\\__init__.py", line 165, in plot
    figure = render(intermediate, yscale=yscale, tile_size=tile_size)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\dataprep\\eda\\distribution\\render.py", line 1920, in render
    visual_elem = render_distribution_grid(
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\dataprep\\eda\\distribution\\render.py", line 1565, in render_distribution_grid
    fig = hist_viz(data, nrows, col, yscale, plot_width, plot_height, False)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\dataprep\\eda\\distribution\\render.py", line 441, in hist_viz
    fig = Figure(
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\plotting\\figure.py", line 155, in __init__
    super().__init__(*arg, **kw)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\model.py", line 234, in __init__
    super().__init__(**kwargs)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\has_props.py", line 247, in __init__
    setattr(self, name, value)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\has_props.py", line 274, in __setattr__
    super().__setattr__(name, value)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\property\\descriptors.py", line 539, in __set__
    self._internal_set(obj, value, setter=setter)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\property\\descriptors.py", line 760, in _internal_set
    value = self.property.prepare_value(obj, self.name, value)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\property\\bases.py", line 331, in prepare_value
    raise e
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\property\\bases.py", line 324, in prepare_value
    self.validate(value)
  File "C:\\Users\\Lukas\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\bokeh\\core\\property\\instance.py", line 112, in validate
    raise ValueError(msg)
ValueError: expected an instance of type Title, got 0 of type int
```

**Expected behavior**
A clear and concise description of the error is shown.


**Desktop (please complete the following information):**
 - OS: [e.g. Windows]
 - Platform Powershell
 - Python Version Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:37:02) [MSC v.1924 64 bit (AMD64)] on win32
 - Dataprep Version dataprep-0.2.11-py3-none-any.whl

**Additional context**
invalid value encountered in bug report


Changed files:
dataprep/eda/distribution/render.py, dataprep/eda/distribution/compute/__init__.py

diff --git a/dataprep/eda/distribution/compute/__init__.py b/dataprep/eda/distribution/compute/__init__.py
index 997a0708..40e7ddf3 100644
--- a/dataprep/eda/distribution/compute/__init__.py
+++ b/dataprep/eda/distribution/compute/__init__.py
@@ -94,6 +94,7 @@ def compute(
         or dtype = Continuous() or dtype = "Continuous" or dtype = Continuous()
     """  # pylint: disable=too-many-locals
 
+    df.columns = df.columns.astype(str)
     df = to_dask(df)
 
     if not any((x, y, z)):
diff --git a/dataprep/eda/distribution/render.py b/dataprep/eda/distribution/render.py
index adc7e52d..47f5dd70 100644
--- a/dataprep/eda/distribution/render.py
+++ b/dataprep/eda/distribution/render.py
@@ -255,6 +255,27 @@ def _sci_notation_superscript(value: str) -> str:
     return value
 
 
+def _empty_figure(title: str, plot_height: int, plot_width: int) -> Figure:
+    # If no data to render in the heatmap, i.e. no missing values
+    # we render a blank heatmap
+    fig = Figure(
+        x_range=[],
+        y_range=[],
+        plot_height=plot_height,
+        plot_width=plot_width,
+        title=title,
+        x_axis_location="below",
+        tools="hover",
+        toolbar_location=None,
+        background_fill_color="#fafafa",
+    )
+
+    # Add at least one renderer to fig, otherwise bokeh
+    # gives us error -1000 (MISSING_RENDERERS): Plot has no renderers
+    fig.rect(x=0, y=0, width=0, height=0)
+    return fig
+
+
 def wordcloud_viz(word_cnts: pd.Series, plot_width: int, plot_height: int,) -> Panel:
     """
     Visualize the word cloud
@@ -262,13 +283,16 @@ def wordcloud_viz(word_cnts: pd.Series, plot_width: int, plot_height: int,) -> P
     ellipse_mask = np.array(
         Image.open(f"{Path(__file__).parent.parent.parent}/assets/ellipse.jpg")
     )
-    wordcloud = WordCloud(
-        background_color="white", mask=ellipse_mask, width=800, height=400
-    )
+    wordcloud = WordCloud(background_color="white", mask=ellipse_mask)
     wordcloud.generate_from_frequencies(word_cnts)
-    wcimg = wordcloud.to_array().astype(np.uint8)
-    alpha = np.full([*wcimg.shape[:2], 1], 255, dtype=np.uint8)
-    wcimg = np.concatenate([wcimg, alpha], axis=2)[::-1, :]
+    wcarr = wordcloud.to_array().astype(np.uint8)
+
+    # use image_rgba following this example
+    # https://docs.bokeh.org/en/latest/docs/gallery/image_rgba.html
+    img = np.empty(wcarr.shape[:2], dtype=np.uint32)
+    view = img.view(dtype=np.uint8).reshape((*wcarr.shape[:2], 4))
+    alpha = np.full((*wcarr.shape[:2], 1), 255, dtype=np.uint8)
+    view[:] = np.concatenate([wcarr, alpha], axis=2)[::-1]
 
     fig = figure(
         plot_width=plot_width,
@@ -278,7 +302,7 @@ def wordcloud_viz(word_cnts: pd.Series, plot_width: int, plot_height: int,) -> P
         y_range=(0, 1),
         toolbar_location=None,
     )
-    fig.image_rgba(image=[wcimg], x=0, y=0, dh=1, dw=1)
+    fig.image_rgba(image=[img], x=0, y=0, dw=1, dh=1)
 
     fig.axis.visible = False
     fig.grid.visible = False
@@ -368,7 +392,7 @@ def pie_viz(
     if nrows > npresent:
         df = df.append(pd.DataFrame({col: [nrows - npresent]}, index=["Others"]))
     df["pct"] = df[col] / nrows * 100
-    df["angle"] = df[col] / npresent * 2 * np.pi
+    df["angle"] = df[col] / nrows * 2 * np.pi
 
     tooltips = [(col, "@index"), ("Count", f"@{col}"), ("Percent", "@pct{0.2f}%")]
     fig = Figure(
@@ -417,6 +441,8 @@ def hist_viz(
     """
     # pylint: disable=too-many-arguments,too-many-locals
     counts, bins = hist
+    if sum(counts) == 0:
+        return _empty_figure(col, plot_height, plot_width)
     intvls = _format_bin_intervals(bins)
     df = pd.DataFrame(
         {
@@ -451,8 +477,7 @@ def hist_viz(
     fig.add_tools(hover)
     tweak_figure(fig, "hist", show_yticks)
     fig.yaxis.axis_label = "Frequency"
-    if not df.empty:
-        _format_axis(fig, df.iloc[0]["left"], df.iloc[-1]["right"], "x")
+    _format_axis(fig, df.iloc[0]["left"], df.iloc[-1]["right"], "x")
     if show_yticks:
         fig.xaxis.axis_label = col
         if yscale == "linear":