Repository: pandas-dev/pandas
Pull request: ENH: Add built-in function for Styler to format the text displayed for missing values
As described in the issues, user who wants to control how NA values are printed
while applying styles to the output will have to implement their own formatter.
(so that the underlying data will not change and can be used for styling)

Since the behavior is common in styling (for reports etc.), suggest to add this
shortcut function to enable users format their NA values as something like '--'
or 'Not Available' easily.

EDIT:

- Change implementation to integrate with the original `.format()` using `na_rep` argument
- Add a new table-wise default `na_rep` setting, which can be set through `.set_na_rep()`
- Minor fix: formatter should be wrapped outside of locs-loop 
- Added a few user guide examples and test cases

example usage: `df.style.highlight_max().format(None, na_rep="-")`

- [x] closes #21527 and #28358
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
ENH: Styler display of NaN / null Values
The `Styler` allows for null cells to be highlighted using `.style.highlight_null()` but a `nan` text string will also be displayed.

If a column is coloured using `.background_gradient()` it must be a numeric type otherwise numerical comparisons fail. This means that using a construction of the form `..fillna('')` on the column will create an error as the column is cast to an `object` type.

`nan` values can be removed in the rendered table using a hack of the form `.render().replace('nan','')` but it would be cleaner to use something along the lines of `.style.replace_null(value)` that could be used to tidy up the display of `nan` cells.

Hints from issue discussion:
Please be sure to checkout out the contributing guide on how to open bug reports:

https://pandas.pydata.org/pandas-docs/stable/contributing.html#bug-reports-and-enhancement-requests

This is missing version information and a minimally reproducible example. You can find more information on how to construct one of those here:

http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports

It's difficult to infer from what you have here alone exactly what the problem is and what you are looking for, so try going through the above to see if we can get to something more explicit
In the following example (pandas 0.23.0), a columns is rendered with a gradient styler, and `nan` cells are displayed with `nan` text dispayed explicity. 

```
import pandas as pd
import numpy as np

df=pd.DataFrame({'x1':list(np.random.randint(-10,10,size=10))+[None] })

df.style.background_gradient()
```

It would be useful to be able to display such cells as empty cells. If the NA values are filled with an empty string, the column is cast to an object type and the background gradient is not displayed:

```
df.fillna('').style.background_gradient()
```

In a Jupyter notebook, a hacky workaround is to render the styled table and then replace `nan` strings with an empty string:

```
from IPython.core.display import HTML 
HTML(df.style.background_gradient().render().replace('nan',''))
```

It would be neater to be able to say something like:

```
df.style.replace_null('')

#simpler - use empty string as default and call:
df.style.replace_null()
```

The empty cell can then be highlighted as such using `.highlight_null()`:

```
HTML(df.style.background_gradient().highlight_null().render().replace('nan',''))
```

However, it might be more appropriate if a fill colour for null cells could be passed along with a replacement string: `df.style.replace_null(color='lightgrey')`

Commit message: Add built-in funcion for Styler to format the text displayed for missing values

As described in GH #28358, user who wants to control how NA values are printed
while applying styles to the output will have to implement their own formatter.
(so that the underlying data will not change and can be used for styling)

Since the behavior is common in styling (for reports etc.), suggest to add this
shortcut function to enable users format their NA values as something like '--'
or 'Not Available' easily.

example usage: `df.style.highlight_max().format_null('--')`

Patch to review:
diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst
index 48c1173a372a7..96b4ce59f958e 100644
--- a/doc/source/whatsnew/v1.0.0.rst
+++ b/doc/source/whatsnew/v1.0.0.rst
@@ -110,6 +110,7 @@ Other enhancements
 - :meth:`DataFrame.to_json` now accepts an ``indent`` integer argument to enable pretty printing of JSON output (:issue:`12004`)
 - :meth:`read_stata` can read Stata 119 dta files. (:issue:`28250`)
 - Added ``encoding`` argument to :func:`DataFrame.to_html` for non-ascii text (:issue:`28663`)
+- :meth:`Styler.format_null` is now added into the built-in functions to help formatting missing values (:issue:`28358`)
 
 Build Changes
 ^^^^^^^^^^^^^
diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py
index 6b98eaca9dacc..f93923f91ba8d 100644
--- a/pandas/io/formats/style.py
+++ b/pandas/io/formats/style.py
@@ -930,6 +930,25 @@ def hide_columns(self, subset):
     # A collection of "builtin" styles
     # -----------------------------------------------------------------------
 
+    def format_null(self, na_rep="-"):
+        """
+        Format the text displayed for missing values.
+
+        .. versionadded:: 1.0.0
+
+        Parameters
+        ----------
+        na_rep : str
+
+        Returns
+        -------
+        self : Styler
+        """
+        self.format(
+            lambda x: na_rep if pd.isna(x) else self._display_funcs.default_factory()(x)
+        )
+        return self
+
     @staticmethod
     def _highlight_null(v, null_color):
         return (
diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py
index 61c163d2cdaac..5f96372e153c9 100644
--- a/pandas/tests/io/formats/test_style.py
+++ b/pandas/tests/io/formats/test_style.py
@@ -990,6 +990,14 @@ def test_bar_bad_align_raises(self):
         with pytest.raises(ValueError):
             df.style.bar(align="poorly", color=["#d65f5f", "#5fba7d"])
 
+    def test_format_null(self, na_rep="-"):
+        # GH 28358
+        df = pd.DataFrame({"A": [0, np.nan]})
+        ctx = df.style.format_null()._translate()
+        result = ctx["body"][1][1]["display_value"]
+        expected = "-"
+        assert result == expected
+
     def test_highlight_null(self, null_color="red"):
         df = pd.DataFrame({"A": [0, np.nan]})
         result = df.style.highlight_null()._compute().ctx


Code review comments:
diff_hunk: @@ -110,6 +110,7 @@ Other enhancements
 - :meth:`DataFrame.to_json` now accepts an ``indent`` integer argument to enable pretty printing of JSON output (:issue:`12004`)
 - :meth:`read_stata` can read Stata 119 dta files. (:issue:`28250`)
 - Added ``encoding`` argument to :func:`DataFrame.to_html` for non-ascii text (:issue:`28663`)
+- :meth:`Styler.format_null` is now added into the built-in functions to help formatting missing values (:issue:`28358`)
path: doc/source/whatsnew/v1.0.0.rst
text: @user1:
can u add this into the user guide as well

@user2:
I'd like the name ``format_nans`` better, to be similar to ``fillna``, ``hasnans`` etc.

@user1?


@author:
Sure, I'll be glad to!