Repository: dask/dask

Issue:
doc: add nunique example to custom aggregation
I was looking up how to do `nunique` on a `dask.dataframe` and https://stackoverflow.com/a/47931009/6046019 seems to be a solution.

I would like to add this to the bottom of https://docs.dask.org/en/latest/dataframe-groupby.html#aggregate as another example of a custom aggregation.

If there's a better way I will be happy to write that.

```
import pandas as pd
import pandas._testing as tm
import dask.dataframe as dd

d = {'col1': [1, 2, 3, 4], 'col2': [5, 6, 7, 8]}
df = pd.DataFrame(data=d)
expected = df.groupby(['col1']).agg({'col2':'nunique'})

d_df = dd.from_pandas(df, npartitions=2)

nunique = dd.Aggregation(
    name="nunique",
    chunk=lambda s : s.apply(lambda x: list(set(x))),
    agg=lambda s0 : s0._selected_obj.groupby(level=list(range(s0._selected_obj.index.nlevels))).sum(),
    finalize=lambda s1 : s1.apply(lambda final: len(set(final))),
    )

actual = d_df.groupby(['col1']).agg({'col2':nunique}).compute()

tm.assert_equal(actual, expected)
```


Discussion:
If you only want to do it for one column, then you can just take advantage of the pre-written `nunique` aggregator for SeriesGroupBy:

```python
d_df.groupby(['col1']).col2.nunique().compute()
```

Thanks @jsignell. That's much more simple. And to return the frame I can do:
```
d_df.groupby(['col1']).col2.nunique().to_frame().compute()
```

You are welcome to open a pull request to add that to the docs if you like.
I posted back to the stack overflow question as well: https://stackoverflow.com/a/62175238/4021797
Do you know if there is a way to add to the docs which are derived from pandas?

e.g. could add
```
df = pd.DataFrame(data={'col1': [1, 2, 3, 4], 'col2': [5, 6, 7, 8]})
ddf = dd.from_pandas(df, npartitions=2)
ddf.groupby(['col1']).col2.nunique().compute()
```
to https://docs.dask.org/en/latest/dataframe-api.html#dask.dataframe.Series.nunique
e.g. https://github.com/dask/dask/blob/master/dask/dataframe/groupby.py#L1793

My justification would be it may be beneficial to people browsing the API for the first time to see how interchangeable dask is with pandas.  

I see `nunique()` pops up in other places of the docs though:

https://docs.dask.org/en/latest/dataframe-groupby.html#easy-case
https://docs.dask.org/en/latest/dataframe-best-practices.html#persist-intelligently

I may still add the nunique agg method to the docs but show that you would only use it if want to apply it to more that one column. In addition i'll make a note about if you are applying it to just one column use the builtin method.

```
import pandas as pd
import pandas._testing as tm
import dask.dataframe as dd

d = {'col1': [1, 2, 3, 4], 'col2': [5, 6, 7, 8], 'col3': [9, 10, 11, 12]}
df = pd.DataFrame(data=d)
expected = df.groupby('col1').agg({'col2':'nunique', 'col3':'nunique'})

ddf = dd.from_pandas(df, npartitions=2)

nunique = dd.Aggregation(
    name="nunique",
    chunk=lambda s : s.apply(lambda x: list(set(x))),
    agg=lambda s0 : s0._selected_obj.groupby(level=list(range(s0._selected_obj.index.nlevels))).sum(),
    finalize=lambda s1 : s1.apply(lambda final: len(set(final))),
    )

actual = ddf.groupby(['col1']).agg({'col2':nunique, 'col3':nunique}).compute()

tm.assert_equal(actual, expected)
```



Resolving patch:
diff --git a/dask/dataframe/groupby.py b/dask/dataframe/groupby.py
--- a/dask/dataframe/groupby.py
+++ b/dask/dataframe/groupby.py
@@ -1792,6 +1792,16 @@ def __init__(self, df, by=None, slice=None, **kwargs):
 
     @derived_from(pd.core.groupby.SeriesGroupBy)
     def nunique(self, split_every=None, split_out=1):
+        """
+        Examples
+        --------
+        >>> import pandas as pd
+        >>> import dask.dataframe as dd
+        >>> d = {'col1': [1, 2, 3, 4], 'col2': [5, 6, 7, 8]}
+        >>> df = pd.DataFrame(data=d)
+        >>> ddf = dd.from_pandas(df, 2)
+        >>> ddf.groupby(['col1']).col2.nunique().compute()
+        """
         name = self._meta.obj.name
         levels = _determine_levels(self.index)
 


Test patch: