Repository: pyjanitor-devs/pyjanitor (python)

Issue:
[ENH] make `encode_categorical` `kwargs` option simpler
# Brief Description

reduce the kwargs option to either `str` or `array-like`.

# Example API

```python
# if just `str`:
df.encode_categorical(col = 'sort'/'appearance')

# if an array-like is passed, then order is irrelevant:
df.encode_categorical(col = [array])
```


Pull request:
[ENH] simpler `encode_categorical`
# PR Description

Please describe the changes proposed in the pull request:

- remove tuple use in `kwargs`
- use either a string(`sort`/`appearance`) or a 1D array.
- update tests, and add examples(shamelessly copied from @thatlittleboy )

**This PR resolves #1021 .**

<!-- As you go down the PR template, please feel free to delete sections that are irrelevant. -->

# PR Checklist

<!-- This checklist exists for newcomers who are not yet familiar with our requirements. If you are experienced with
the project, please feel free to delete this section. -->

Please ensure that you have done the following:

1. [ ] PR in from a fork off your branch. Do not PR from `<your_username>`:`dev`, but rather from `<your_username>`:`<feature-branch_name>`.
<!-- Doing this helps us keep the commit history much cleaner than it would otherwise be. -->
2. [ ] If you're not on the contributors list, add yourself to `AUTHORS.rst`.
<!-- We'd like to acknowledge your contributions! -->
3. [ ] Add a line to `CHANGELOG.md` under the latest version header (i.e. the one that is "on deck") describing the contribution.
    - Do use some discretion here; if there are multiple PRs that are related, keep them in a single line.

# Automatic checks

There will be automatic checks run on the PR. These include:

- Building a preview of the docs on Netlify
- Automatically linting the code
- Making sure the code is documented
- Making sure that all tests are passed
- Making sure that code coverage doesn't go down.

# Relevant Reviewers

<!-- Finally, please tag relevant maintainers to review. -->

Please tag maintainers to review.

- @ericmjl


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5dcbd00..f12d42e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
 -   [BUG] Extend `fill_empty`'s `column_names` type range. Issue #998. @Zeroto521
 -   [BUG] Removed/updated error-inducing default arguments in `row_to_names` (#1004) and `round_to_fraction` (#1005). @thatlittleboy
 -   [ENH] `patterns` deprecated in favour of importing `re.compile`. #1007 @samukweku
+-   [ENH] Changes to kwargs in `encode_categorical`, where the values can either be a string or a 1D array. #1021 @samukweku
 
 ## [v0.22.0] - 2021-11-21
 
diff --git a/janitor/functions/encode_categorical.py b/janitor/functions/encode_categorical.py
index c473f5f..016b61e 100644
--- a/janitor/functions/encode_categorical.py
+++ b/janitor/functions/encode_categorical.py
@@ -22,82 +22,90 @@ def encode_categorical(
 
     This method does not mutate the original DataFrame.
 
-    Note: In versions < 0.20.11, this method mutates the original DataFrame.
-
-    If `categories` is `None` in the `kwargs` tuple, then the
-    values for `categories` are inferred from the column;
-    if `order` is `None`, then the values for `categories` are applied unordered.
+    Simply pass a string, or a sequence of column names to `column_names`;
+    alternatively, you can pass kwargs, where the keys are the column names
+    and the values can either be None, `sort`, `appearance`
+    or a 1-D array-like object.
+
+    - None: column is cast to an unordered categorical.
+    - `sort`: column is cast to an ordered categorical,
+              with the order defined by the sort-order of the categories.
+    - `appearance`: column is cast to an ordered categorical,
+                    with the order defined by the order of appearance
+                    in the original column.
+    - 1d-array-like object: column is cast to an ordered categorical,
+                            with the categories and order as specified
+                            in the input array.
 
     `column_names` and `kwargs` parameters cannot be used at the same time.
 
-    Functional usage syntax:
-
-    ```python
-        import pandas as pd
-        import janitor as jn
-    ```
-
-    - With `column_names`
-
-    ```python
-        categorical_cols = ['col1', 'col2', 'col4']
-        df = jn.encode_categorical(
-                    df,
-                    columns = categorical_cols)  # one way
-    ```
-
-    - With `kwargs`
-
-    ```python
-        df = jn.encode_categorical(
-                    df,
-                    col1 = (categories, order),
-                    col2 = (categories = [values],
-                    order="sort"  # or "appearance" or None
-
-                )
-    ```
-
-    Method chaining syntax:
-
-    - With `column_names`
-
-    ```python
-        categorical_cols = ['col1', 'col2', 'col4']
-        df = (pd.DataFrame(...)
-                .encode_categorical(columns=categorical_cols)
-            )
-    ```
-
-    - With `kwargs`
-
-    ```python
-        df = (
-            pd.DataFrame(...)
-            .encode_categorical(
-                col1 = (categories, order),
-                col2 = (categories = [values]/None,
-                        order="sort"  # or "appearance" or None
-                        )
-        )
-    ```
-
-    :param df: The pandas DataFrame object.
+    Example: Using `column_names`
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({
+        ...     "foo": ["b", "b", "a", "c", "b"],
+        ...     "bar": range(4, 9),
+        ... })
+        >>> df
+          foo  bar
+        0   b    4
+        1   b    5
+        2   a    6
+        3   c    7
+        4   b    8
+        >>> df.dtypes
+        foo    object
+        bar     int64
+        dtype: object
+        >>> enc_df = df.encode_categorical(column_names="foo")
+        >>> enc_df.dtypes
+        foo    category
+        bar       int64
+        dtype: object
+        >>> enc_df["foo"].cat.categories
+        Index(['a', 'b', 'c'], dtype='object')
+        >>> enc_df["foo"].cat.ordered
+        False
+
+    Example: Using `kwargs` to specify an ordered categorical.
+
+        >>> import pandas as pd
+        >>> import janitor
+        >>> df = pd.DataFrame({
+        ...     "foo": ["b", "b", "a", "c", "b"],
+        ...     "bar": range(4, 9),
+        ... })
+        >>> df.dtypes
+        foo    object
+        bar     int64
+        dtype: object
+        >>> enc_df = df.encode_categorical(foo="appearance")
+        >>> enc_df.dtypes
+        foo    category
+        bar       int64
+        dtype: object
+        >>> enc_df["foo"].cat.categories
+        Index(['b', 'a', 'c'], dtype='object')
+        >>> enc_df["foo"].cat.ordered
+        True
+
+
+
+    :param df: A pandas DataFrame object.
     :param column_names: A column name or an iterable (list or tuple)
         of column names.
-    :param kwargs: A pairing of column name to a tuple of (`categories`, `order`).
-        This is useful in creating categorical columns that are ordered, or
+    :param kwargs: A mapping from column name to either `None`,
+        `sort` or `appearance`, or a 1-D array. This is useful
+        in creating categorical columns that are ordered, or
         if the user needs to explicitly specify the categories.
     :returns: A pandas DataFrame.
-    :raises ValueError: if both ``column_names`` and ``kwargs`` are provided.
+    :raises ValueError: if both `column_names` and `kwargs` are provided.
     """  # noqa: E501
 
     if all((column_names, kwargs)):
         raise ValueError(
-            """
-            Only one of `column_names` or `kwargs`
-            can be provided.
-            """
+            "Only one of `column_names` or `kwargs` can be provided."
         )
     # column_names deal with only category dtype (unordered)
     # kwargs takes care of scenarios where user wants an ordered category
@@ -121,42 +129,33 @@ def _computations_as_categorical(df: pd.DataFrame, **kwargs) -> pd.DataFrame:
     categorical columns are created with an order,
     or specific values supplied for the categories.
     It uses a kwarg, where the key is the column name,
-    and the value is a tuple of categories, order.
-    The defaults for the tuple are (None, None)
-    and will return a categorical dtype
+    and the value is either a string, or a 1D array.
+    The default for value is None and will return a categorical dtype
     with no order and categories inferred from the column.
-    A DataFrame, with catetorical columns, is returned.
+    A DataFrame, with categorical columns, is returned.
     """
 
     categories_dict = _as_categorical_checks(df, **kwargs)
 
     categories_dtypes = {}
 
-    for column_name, (
-        cat,
-        order,
-    ) in categories_dict.items():
-        error_msg = f"""
-                     Kindly ensure there is at least
-                     one non-null value in {column_name}.
-                     """
-        if (cat is None) and (order is None):
+    for column_name, value in categories_dict.items():
+        if value is None:
             cat_dtype = pd.CategoricalDtype()
+        elif isinstance(value, str):
+            if value == _CategoryOrder.SORT.value:
+                _, cat_dtype = df[column_name].factorize(sort=True)
+            else:
+                _, cat_dtype = df[column_name].factorize(sort=False)
+            if cat_dtype.empty:
+                raise ValueError(
+                    "Kindly ensure there is at least "
+                    f"one non-null value in {column_name}."
+                )
+            cat_dtype = pd.CategoricalDtype(categories=cat_dtype, ordered=True)
 
-        elif (cat is None) and (order is _CategoryOrder.SORT.value):
-            cat = df[column_name].factorize(sort=True)[-1]
-            if cat.empty:
-                raise ValueError(error_msg)
-            cat_dtype = pd.CategoricalDtype(categories=cat, ordered=True)
-
-        elif (cat is None) and (order is _CategoryOrder.APPEARANCE.value):
-            cat = df[column_name].factorize(sort=False)[-1]
-            if cat.empty:
-                raise ValueError(error_msg)
-            cat_dtype = pd.CategoricalDtype(categories=cat, ordered=True)
-
-        elif cat is not None:  # order is irrelevant if cat is provided
-            cat_dtype = pd.CategoricalDtype(categories=cat, ordered=True)
+        else:  # 1-D array
+            cat_dtype = pd.CategoricalDtype(categories=value, ordered=True)
 
         categories_dtypes[column_name] = cat_dtype
 
@@ -166,26 +165,20 @@ def _computations_as_categorical(df: pd.DataFrame, **kwargs) -> pd.DataFrame:
 def _as_categorical_checks(df: pd.DataFrame, **kwargs) -> dict:
     """
     This function raises errors if columns in `kwargs` are
-    absent in the the dataframe's columns.
-    It also raises errors if the tuple in `kwargs`
-    has a length greater than 2, or the `order` value,
-    if not None, is not one of `appearance` or `sort`.
-    Error is raised if the `categories` in the tuple in `kwargs`
-    is not a 1-D array-like object.
+    absent from the dataframe's columns.
+    It also raises errors if the value in `kwargs`
+    is not a string (`appearance` or `sort`), or a 1D array.
 
     This function is executed before proceeding to the computation phase.
 
-    If all checks pass, a dictionary of column names and tuple
-    of (categories, order) is returned.
+    If all checks pass, a dictionary of column names and value is returned.
 
     :param df: The pandas DataFrame object.
-    :param kwargs: A pairing of column name
-        to a tuple of (`categories`, `order`).
+    :param kwargs: A pairing of column name and value.
     :returns: A dictionary.
-    :raises TypeError: if the value in ``kwargs`` is not a tuple.
-    :raises ValueError: if ``categories`` is not a 1-D array.
-    :raises ValueError: if ``order`` is not one of
-        `sort`, `appearance`, or `None`.
+    :raises TypeError: If `value` is not a 1-D array, or a string.
+    :raises ValueError: If `value` is a 1-D array, and contains nulls,
+        or is non-unique.
     """
 
     # column checks
@@ -195,107 +188,74 @@ def _as_categorical_checks(df: pd.DataFrame, **kwargs) -> dict:
 
     for column_name, value in kwargs.items():
         # type check
-        check("Pair of `categories` and `order`", value, [tuple])
-
-        len_value = len(value)
-
-        if len_value != 2:
-            raise ValueError(
-                f"""
-                The tuple of (categories, order) for {column_name}
-                should be length 2; the tuple provided is
-                length {len_value}.
-                """
-            )
-
-        cat, order = value
-        if cat is not None:
-            if not is_list_like(cat):
-                raise TypeError(f"{cat} should be list-like.")
-
-            if not hasattr(cat, "shape"):
-                checker = pd.Index([*cat])
-            else:
-                checker = cat
-
-            arr_ndim = checker.ndim
-            if (arr_ndim != 1) or isinstance(checker, pd.MultiIndex):
+        if (value is not None) and not (
+            is_list_like(value) or isinstance(value, str)
+        ):
+            raise TypeError(f"{value} should be list-like or a string.")
+        if is_list_like(value):
+            if not hasattr(value, "shape"):
+                value = pd.Index([*value])
+
+            arr_ndim = value.ndim
+            if (arr_ndim != 1) or isinstance(value, pd.MultiIndex):
                 raise ValueError(
-                    f"""
-                    {cat} is not a 1-D array.
-                    Kindly provide a 1-D array-like object to `categories`.
-                    """
+                    f"{value} is not a 1-D array. "
+                    "Kindly provide a 1-D array-like object."
                 )
 
-            if not isinstance(checker, (pd.Series, pd.Index)):
-                checker = pd.Index(checker)
+            if not isinstance(value, (pd.Series, pd.Index)):
+                value = pd.Index(value)
 
-            if checker.hasnans:
+            if value.hasnans:
                 raise ValueError(
-                    "Kindly ensure there are no nulls in `categories`."
+                    "Kindly ensure there are no nulls in the array provided."
                 )
 
-            if not checker.is_unique:
+            if not value.is_unique:
                 raise ValueError(
-                    """
-                    Kindly provide unique,
-                    non-null values for `categories`.
-                    """
+                    "Kindly provide unique, "
+                    "non-null values for the array provided."
                 )
 
-            if checker.empty:
+            if value.empty:
                 raise ValueError(
-                    """
-                    Kindly ensure there is at least
-                    one non-null value in `categories`.
-                    """
+                    "Kindly ensure there is at least "
+                    "one non-null value in the array provided."
                 )
 
             # uniques, without nulls
             uniques = df[column_name].factorize(sort=False)[-1]
             if uniques.empty:
                 raise ValueError(
-                    f"""
-                     Kindly ensure there is at least
-                     one non-null value in {column_name}.
-                     """
+                    "Kindly ensure there is at least "
+                    f"one non-null value in {column_name}."
                 )
 
-            missing = uniques.difference(checker, sort=False)
+            missing = uniques.difference(value, sort=False)
             if not missing.empty and (uniques.size > missing.size):
                 warnings.warn(
-                    f"""
-                     Values {tuple(missing)} are missing from
-                     the provided categories {cat}
-                     for {column_name}; this may create nulls
-                     in the new categorical column.
-                     """,
+                    f"Values {tuple(missing)} are missing from "
+                    f"the provided categories {value} "
+                    f"for {column_name}; this may create nulls "
+                    "in the new categorical column.",
                     UserWarning,
                     stacklevel=2,
                 )
 
             elif uniques.equals(missing):
                 warnings.warn(
-                    f"""
-                     None of the values in {column_name} are in
-                     {cat};
-                     this might create nulls for all values
-                     in the new categorical column.
-                     """,
+                    f"None of the values in {column_name} are in "
+                    f"{value}; this might create nulls for all values "
+                    f"in the new categorical column.",
                     UserWarning,
                     stacklevel=2,
                 )
 
-        if order is not None:
-            check("order", order, [str])
-
-            category_order_types = [ent.value for ent in _CategoryOrder]
-            if order.lower() not in category_order_types:
+        elif isinstance(value, str):
+            category_order_types = {ent.value for ent in _CategoryOrder}
+            if value.lower() not in category_order_types:
                 raise ValueError(
-                    """
-                    `order` argument should be one of
-                    "appearance", "sort" or `None`.
-                    """
+                    "argument should be one of `appearance` or `sort`."
                 )
 
         categories_dict[column_name] = value


Test patch:
diff --git a/tests/functions/test_encode_categorical.py b/tests/functions/test_encode_categorical.py
index 240179d..3be7864 100644
--- a/tests/functions/test_encode_categorical.py
+++ b/tests/functions/test_encode_categorical.py
@@ -1,5 +1,6 @@
 import numpy as np
 import pandas as pd
+import datetime
 import pytest
 from hypothesis import given
 from pandas.testing import assert_frame_equal
@@ -10,6 +11,18 @@ from janitor.testing_utils.strategies import (
 )
 
 
+@pytest.fixture
+def df_checks():
+    """pytest fixture"""
+    return pd.DataFrame(
+        [
+            {"region": "Pacific", "2007": 1039, "2009": 2587},
+            {"region": "Southwest", "2007": 51, "2009": 176},
+            {"region": "Rocky Mountains and Plains", "2007": 200, "2009": 338},
+        ]
+    )
+
+
 @pytest.mark.functions
 @given(df=categoricaldf_strategy())
 def test_encode_categorical(df):
@@ -77,200 +90,146 @@ def test_encode_categorical_multiple_column_names(df):
     )
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_both_column_names_kwargs(df):
+def test_both_column_names_kwargs(df_checks):
     """
     Raise Error if both `column_names`
     and kwargs are provided.
     """
     with pytest.raises(ValueError):
-        df.encode_categorical(
-            column_names=["a", "cities"], Bell__Chart=(None, "sort")
-        )
+        df_checks.encode_categorical(column_names="region", region="sort")
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_check_presence_column_names_in_kwargs(df):
+def test_check_presence_column_names_in_kwargs(df_checks):
     """
     Raise ValueError if column names in `kwargs`
     do not exist in the dataframe.
     """
     with pytest.raises(ValueError):
-        df.encode_categorical(col_1=(None, "sort"))
+        df_checks.encode_categorical(regon=None)
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_check_type_tuple_in_kwargs(df):
+def test_categories_type_in_kwargs(df_checks):
     """
-    Raise TypeError if the categories, order pairing
-    in `kwargs` is not a tuple.
+    Raise TypeError if the value provided is not array-like or a string.
     """
     with pytest.raises(TypeError):
-        df.encode_categorical(a=[None, "sort"])
+        df_checks.encode_categorical(region=datetime.datetime(2017, 1, 1))
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_tuple_length_in_kwargs(df):
+def test_categories_ndim_array_gt_1_in_kwargs(df_checks):
     """
-    Raise ValueError if the length of the tuple
-    in kwargs is not equal to 2.
-    """
-    with pytest.raises(ValueError):
-        df.encode_categorical(a=(None, None, 2))
-
-
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_categories_type_in_kwargs(df):
-    """
-    Raise TypeError if the wrong argument is supplied to
-    the `categories` parameter in kwargs.
-    """
-    with pytest.raises(TypeError):
-        df.encode_categorical(a=("category", None))
-
-
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_categories_ndim_array_gt_1_in_kwargs(df):
-    """
-    Raise ValueError if the argument supplied to
-    the `categories` parameter in kwargs is not
-    1-D array-like.
+    Raise ValueError if categories is provided, but is not a 1D array.
     """
     arrays = [[1, 1, 2, 2], ["red", "blue", "red", "blue"]]
     with pytest.raises(ValueError):
-        df.encode_categorical(a=(arrays, None))
+        df_checks.encode_categorical(region=arrays)
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_categories_ndim_MultiIndex_gt_1_in_kwargs(df):
+def test_categories_ndim_MultiIndex_gt_1_in_kwargs(df_checks):
     """
-    Raise ValueError if the argument supplied to
-    the `categories` parameter in kwargs is not
-    1-D array-like.
+    Raise ValueError if categories is provided, but is not a 1D array.
     """
     arrays = [[1, 1, 2, 2], ["red", "blue", "red", "blue"]]
     arrays = pd.MultiIndex.from_arrays(arrays, names=("number", "color"))
     with pytest.raises(ValueError):
-        df.encode_categorical(a=(arrays, None))
+        df_checks.encode_categorical(region=arrays)
 
 
-@pytest.mark.functions
-@given(df=df_strategy())
-def test_categories_ndim_DataFrame_gt_1_in_kwargs(df):
+def test_categories_ndim_DataFrame_gt_1_in_kwargs(df_checks):
     """
-    Raise ValueError if the argument supplied to
-    the `categories` parameter in kwargs is not
-    1-D array-like.
+    Raise ValueError if categories is provided, but is not a 1D array.
     """
     arrays = {"name": [1, 1, 2, 2], "number": ["red", "blue", "red", "blue"]}
     arrays = pd.DataFrame(arrays)
     with pytest.raises(ValueError):
-        df.encode_categorical(a=(arrays, None))
+        df_checks.encode_categorical(region=arrays)
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_categories_null_in_categories(df):
     """
-    Raise ValueError if there are nulls in the `categories`.
+    Raise ValueError if categories is provided, but has nulls.
     """
     with pytest.raises(ValueError):
-        df.encode_categorical(a=([None, 2, 3], None))
+        df.encode_categorical(a=[None, 2, 3])
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_non_unique_cat(df):
-    """Raise ValueError if `categories` is not unique."""
+    """Raise ValueError if categories is provided, but is not unique."""
     with pytest.raises(ValueError):
-        df.encode_categorical(a=([1, 2, 3, 3], "sort"))
+        df.encode_categorical(a=[1, 2, 3, 3])
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_empty_cat(df):
-    """Raise ValueError if `categories` is empty."""
+    """Raise ValueError if empty categories is provided."""
     with pytest.raises(ValueError):
-        df.encode_categorical(a=([], "sort"))
+        df.encode_categorical(a=[])
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_empty_col(df):
     """
-    Raise ValueError if `categories`,
-    and the relevant column is empty, or all nulls.
+    Raise ValueError if categories is provided,
+    but the relevant column is all nulls.
     """
     with pytest.raises(ValueError):
         df["col1"] = np.nan
-        df.encode_categorical(col1=([1, 2, 3], "sort"))
+        df.encode_categorical(col1=[1, 2, 3])
 
 
 @pytest.mark.functions
-@given(df=df_strategy())
-def test_empty_col_2(df):
+@given(df=categoricaldf_strategy())
+def test_warnings(df):
     """
-    Raise ValueError if `categories` is None,
-    and the relevant column is empty, or all nulls.
+    Test that warnings are raised if categories is provided, and
+    the categories do not match the unique values in the column, or
+    some values in the column are missing in the categories provided.
     """
-    with pytest.raises(ValueError):
-        df["col1"] = np.nan
-        df.encode_categorical(col1=(None, "sort"))
+    with pytest.warns(UserWarning):
+        df.encode_categorical(
+            numbers=[4, 5, 6], names=["John", "Mark", "Luke"]
+        )
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
-def test_empty_col_3(df):
+def test_order_wrong_option_in_kwargs(df):
     """
-    Raise ValueError if `categories` is None,
-    and the relevant column is empty, or all nulls.
+    Raise ValueError if a string is provided, but is not
+    one of None, 'sort', or 'appearance'.
     """
     with pytest.raises(ValueError):
-        df["col1"] = pd.Series([], dtype="object")
-        df.encode_categorical(col1=(None, "appearance"))
-
-
-@pytest.mark.functions
-@given(df=categoricaldf_strategy())
-def test_warnings(df):
-    """
-    Test that warnings are raised if `categories` is provided, and
-    the categories do not match the unique values in the column, or
-    some values in the column are missing in `categories`.
-    """
-    with pytest.warns(UserWarning):
-        df.encode_categorical(
-            numbers=([4, 5, 6], None), names=(["John", "Mark", "Luke"], "sort")
-        )
+        df.encode_categorical(a="sorted")
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
-def test_order_type_in_kwargs(df):
+def test_empty_col_sort(df):
     """
-    Raise TypeError if the wrong argument is supplied to
-    the `order` parameter in kwargs.
+    Raise ValueError if a string is provided,
+    but the relevant column is all nulls.
     """
-    with pytest.raises(TypeError):
-        df.encode_categorical(a=({1, 2, 3, 3}, {"sort"}))
+    with pytest.raises(ValueError):
+        df["col1"] = np.nan
+        df.encode_categorical(col1="sort")
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
-def test_order_wrong_option_in_kwargs(df):
+def test_empty_col_appearance(df):
     """
-    Raise ValueError if the value supplied to the `order`
-    parameter in kwargs is not one of None, 'sort', or 'appearance'.
+    Raise ValueError if a string is provided,
+    but the relevant column is all nulls.
     """
     with pytest.raises(ValueError):
-        df.encode_categorical(a=({1, 2, 3, 3}, "sorted"))
+        df["col1"] = np.nan
+        df.encode_categorical(col1="appearance")
 
 
 # directly comparing columns is safe -
@@ -288,9 +247,9 @@ def test_order_wrong_option_in_kwargs(df):
 @given(df=categoricaldf_strategy())
 def test_all_None(df):
     """
-    Test output where `categories` and `order` are None.
+    Test output where value is None.
     """
-    result = df.encode_categorical(names=(None, None))
+    result = df.encode_categorical(names=None)
 
     expected = df.astype({"names": "category"})
     assert expected["names"].equals(result["names"])
@@ -300,9 +259,9 @@ def test_all_None(df):
 @given(df=categoricaldf_strategy())
 def test_all_cat_None_1(df):
     """
-    Test output where `categories` is None.
+    Test output where a string is provided.
     """
-    result = df.encode_categorical(names=(None, "sort"))
+    result = df.encode_categorical(names="sort")
     categories = pd.CategoricalDtype(
         categories=df.names.factorize(sort=True)[-1], ordered=True
     )
@@ -314,9 +273,9 @@ def test_all_cat_None_1(df):
 @given(df=categoricaldf_strategy())
 def test_all_cat_None_2(df):
     """
-    Test output where `categories` is None.
+    Test output where a string is provided.
     """
-    result = df.encode_categorical(names=(None, "appearance"))
+    result = df.encode_categorical(names="appearance")
     categories = pd.CategoricalDtype(
         categories=df.names.factorize(sort=False)[-1], ordered=True
     )
@@ -328,9 +287,9 @@ def test_all_cat_None_2(df):
 @given(df=categoricaldf_strategy())
 def test_all_cat_not_None(df):
     """
-    Test output where `categories` is  not None.
+    Test output where categories is provided.
     """
-    result = df.encode_categorical(numbers=(np.array([3, 1, 2]), "appearance"))
+    result = df.encode_categorical(numbers=np.array([3, 1, 2]))
     categories = pd.CategoricalDtype(categories=[3, 1, 2], ordered=True)
     expected = df.astype({"numbers": categories})
     assert expected["numbers"].equals(result["numbers"])
diff --git a/tests/functions/test_pivot_wider.py b/tests/functions/test_pivot_wider.py
index 81298ea..6d70785 100644
--- a/tests/functions/test_pivot_wider.py
+++ b/tests/functions/test_pivot_wider.py
@@ -317,7 +317,7 @@ def test_no_index_names_from_order():
         }
     )
 
-    result = df_in.encode_categorical(gender=(None, "appearance")).pivot_wider(
+    result = df_in.encode_categorical(gender="appearance").pivot_wider(
         names_from="gender"
     )
 
@@ -404,9 +404,7 @@ def test_change_level_order():
         }
     )
 
-    result = df_in.encode_categorical(
-        variable=(None, "appearance")
-    ).pivot_wider(
+    result = df_in.encode_categorical(variable="appearance").pivot_wider(
         index=["geoid", "name"],
         names_from="variable",
         values_from=["estimate", "error"],