Repository: pyjanitor-devs/pyjanitor

Issue:
[BUG] Unexpected mutation of original df by `sort_column_value_order`
# Brief Description
The `sort_column_value_order` function adds a new column `cond_order` to the original dataframe (see [here](https://github.com/pyjanitor-devs/pyjanitor/blob/9d3653f959f11150bcc9aca87476efc72affc60a/janitor/functions/sort_column_value_order.py#L55)) for implementation purposes.

But this is mutating the original dataframe in a non-expected way -- the end user shouldn't be seeing this new column since `cond_order` is only used in the internal implementation of the sorting function.

# Minimally Reproducible Code

```python
df = pd.DataFrame({
    "a1": [4, 2, 9, 12],
    "a2": ["x", "y", "z", "y"],
})
df.sort_column_value_order("a2", {"x": 3, "y": 2, "z": 1});
df

# original df now has an additional column...
   a1 a2  cond_order
0   4  x           3
1   2  y           2
2   9  z           1
3  12  y           2
```



Discussion:
This is the kind of thing I know how to fix easily, first dibs!

Resolving patch:
diff --git a/janitor/functions/sort_column_value_order.py b/janitor/functions/sort_column_value_order.py
--- a/janitor/functions/sort_column_value_order.py
+++ b/janitor/functions/sort_column_value_order.py
@@ -1,5 +1,8 @@
+"""Implementation of the `sort_column_value_order` function."""
 import pandas_flavor as pf
 import pandas as pd
+from janitor.utils import check, check_column
+from .remove_columns import remove_columns  # noqa: F401
 
 
 @pf.register_dataframe_method
@@ -10,35 +13,35 @@ def sort_column_value_order(
     This function adds precedence to certain values in a specified column, then
     sorts based on that column and any other specified columns.
 
-    Functional usage syntax:
+    Example:
 
-    ```python
-
-        import pandas as pd
-        import janitor as jn
-
-        df = pd.DataFrame(...)
-
-        jn.sort_column_value_order(
-            column,
-            column_value_order = {col1: number, ...}
-            columns
-        )
-    ```
-
-    Method chaining usage syntax:
-
-    ```python
-
-        import pandas as pd
-        import janitor
-
-        df.sort_column_value_order(
-            column,
-            column_value_order = {col1: number, ...}
-            columns
-        )
-    ```
+        >>> import pandas as pd
+        >>> import janitor
+        >>> import numpy as np
+        >>> company_sales = {
+        ...     "SalesMonth": ["Jan", "Feb", "Feb", "Mar", "April"],
+        ...     "Company1": [150.0, 200.0, 200.0, 300.0, 400.0],
+        ...     "Company2": [180.0, 250.0, 250.0, np.nan, 500.0],
+        ...     "Company3": [400.0, 500.0, 500.0, 600.0, 675.0],
+        ... }
+        >>> df = pd.DataFrame.from_dict(company_sales)
+        >>> df
+          SalesMonth  Company1  Company2  Company3
+        0        Jan     150.0     180.0     400.0
+        1        Feb     200.0     250.0     500.0
+        2        Feb     200.0     250.0     500.0
+        3        Mar     300.0       NaN     600.0
+        4      April     400.0     500.0     675.0
+        >>> df.sort_column_value_order(
+        ...     "SalesMonth",
+        ...     {"April": 1, "Mar": 2, "Feb": 3, "Jan": 4}
+        ... )
+          SalesMonth  Company1  Company2  Company3
+        4      April     400.0     500.0     675.0
+        3        Mar     300.0       NaN     600.0
+        1        Feb     200.0     250.0     500.0
+        2        Feb     200.0     250.0     500.0
+        0        Jan     150.0     180.0     400.0
 
     :param df: This is our DataFrame that we are manipulating
     :param column: This is a column name as a string we are using to specify
@@ -48,19 +51,19 @@ def sort_column_value_order(
     :param columns: This is a list of additional columns that we can sort by
     :raises ValueError: raises error if chosen Column Name is not in
         Dataframe, or if column_value_order dictionary is empty.
-    :return: This function returns a Pandas DataFrame
+    :return: A sorted pandas DataFrame.
     """
-    if len(column_value_order) > 0:
-        if column in df.columns:
-            df["cond_order"] = df[column].replace(column_value_order)
-            if columns is None:
-                new_df = df.sort_values("cond_order")
-                del new_df["cond_order"]
-            else:
-                new_df = df.sort_values(columns + ["cond_order"])
-                del new_df["cond_order"]
-            return new_df
-        else:
-            raise ValueError("Column Name not in DataFrame")
-    else:
+    # Validation checks
+    check_column(df, column, present=True)
+    check("column_value_order", column_value_order, [dict])
+    if not column_value_order:
         raise ValueError("column_value_order dictionary cannot be empty")
+
+    df = df.assign(cond_order=df[column].replace(column_value_order))
+
+    sort_by = ["cond_order"]
+    if columns is not None:
+        sort_by = ["cond_order"] + columns
+
+    df = df.sort_values(sort_by).remove_columns("cond_order")
+    return df


Test patch:
diff --git a/tests/functions/test_sort_column_value_order.py b/tests/functions/test_sort_column_value_order.py
--- a/tests/functions/test_sort_column_value_order.py
+++ b/tests/functions/test_sort_column_value_order.py
@@ -1,9 +1,3 @@
-import numpy as np
-import pandas as pd
-import pytest
-
-from janitor.functions import sort_column_value_order
-
 """
 Below, company_sales and company_sales_2 are both dfs.
 
@@ -24,21 +18,34 @@
     the columns have been successfully ordered.
 """
 
+import numpy as np
+import pandas as pd
+import pytest
+
+from janitor.functions import sort_column_value_order
+
 
-def test_sort_column_value_order():
+@pytest.fixture
+def company_sales_df():
+    """Fixture for tests below."""
     company_sales = {
         "SalesMonth": ["Jan", "Feb", "Feb", "Mar", "April"],
-        "Company1": [150.0, 200.0, 200.0, 300.0, 400.0],
-        "Company2": [180.0, 250.0, 250.0, np.nan, 500.0],
+        "Company1": [150.0, 260.0, 230.0, 300.0, 400.0],
+        "Company2": [500.0, 210.0, 250.0, np.nan, 80.0],
         "Company3": [400.0, 500.0, 500.0, 600.0, 675.0],
     }
     df = pd.DataFrame.from_dict(company_sales)
-    df = df.set_index("Company1")
+    return df
+
+
+def test_sort_column_value_order(company_sales_df):
+    """Main test for sort_column_value_order."""
+    df = company_sales_df.set_index("Company1")
     company_sales_2 = {
-        "SalesMonth": ["April", "Mar", "Feb", "Feb", "Jan"],
-        "Company1": [400.0, 300.0, 200.0, 200.0, 150.0],
-        "Company2": [500.0, np.nan, 250.0, 250.0, 180.0],
-        "Company3": [675.0, 600.0, 500.0, 500.0, 400.0],
+        "SalesMonth": reversed(["Jan", "Feb", "Feb", "Mar", "April"]),
+        "Company1": reversed([150.0, 230.0, 260.0, 300.0, 400.0]),
+        "Company2": reversed([500.0, 250.0, 210.0, np.nan, 80.0]),
+        "Company3": reversed([400.0, 500.0, 500.0, 600.0, 675.0]),
     }
     df2 = pd.DataFrame.from_dict(company_sales_2)
     df2 = df2.set_index("Company1")
@@ -53,7 +60,33 @@ def test_sort_column_value_order():
             )
         )
     assert df2.equals(
-        sort_column_value_order(
-            df, "SalesMonth", {"April": 1, "Mar": 2, "Feb": 3, "Jan": 4}
+        df.sort_column_value_order(
+            "SalesMonth", {"April": 1, "Mar": 2, "Feb": 3, "Jan": 4}
         )
     )
+
+
+def test_sort_column_value_order_without_column_value_order(company_sales_df):
+    """
+    Test that sort_column_value_order raises a ValueError.
+
+    In this case, it should raise ValueError
+    when `column_value_order` is empty.
+    """
+    with pytest.raises(ValueError):
+        company_sales_df.sort_column_value_order("SalesMonth", {})
+
+
+def test_sort_column_value_order_with_columns(company_sales_df):
+    """Execution test for sort_column_value_order
+
+    Used to test the case when columns is also specified.
+
+    TODO: This test needs to be improved
+    to be more than just an execution test.
+    """
+    _ = company_sales_df.set_index("Company1").sort_column_value_order(
+        "SalesMonth",
+        {"April": 1, "Mar": 2, "Feb": 3, "Jan": 4},
+        columns=["Company2"],
+    )