Repository: pyjanitor-devs/pyjanitor

Issue:
[BUG] Unhelpful default arguments in `round_to_fraction`
# Brief description

## [`round_to_fraction`](https://github.com/pyjanitor-devs/pyjanitor/blob/9d3653f959f11150bcc9aca87476efc72affc60a/janitor/functions/round_to_fraction.py#L11) function
- The `column_name` parameter is currently given a non-useful default argument (`None`) while the function logic dictates that it should be an existing column within the dataframe. I propose to: 1) make this parameter non-optional, and 2) add an additional check to enforce that the given column name is present in the dataframe.
- Similarly, the `denominator` parameter is currently given a non-useful default `None`, while it should clearly be a number. Propose to either make it non-optional since I'm unsure if there is a sensible default value to place here.


### Minimally Reproducible Code

```python
import numpy as np
import pandas as pd
import janitor
df = pd.DataFrame({
    "a1": [1.203, 2.499, np.nan],
    "a2": ["x", "y", "z"],
})

df.round_to_fraction()  # KeyError: None
df.round_to_fraction("a1")  # TypeError: unsupported operand type(s) for *: 'float' and 'NoneType'
```

The first error should be replaced with a more informative errmsg as per the improvements suggested for `column_name` above.

The second error should be caught earlier (at the moment, the type check of the `denominator` parameter is only done if `denominator` is Truthy, which is not really accurate.)



Discussion:

Resolving patch:
diff --git a/janitor/functions/add_columns.py b/janitor/functions/add_columns.py
--- a/janitor/functions/add_columns.py
+++ b/janitor/functions/add_columns.py
@@ -69,7 +69,7 @@ def add_column(
         rows in the DataFrame.
     :raises ValueError: if attempting to add an iterable of values with
         a length not equal to the number of DataFrame rows.
-    :raises ValueError: if `value` has length of `0``.
+    :raises ValueError: if `value` has length of `0`.
     """
     df = df.copy()
     check("column_name", column_name, [str])
diff --git a/janitor/functions/round_to_fraction.py b/janitor/functions/round_to_fraction.py
--- a/janitor/functions/round_to_fraction.py
+++ b/janitor/functions/round_to_fraction.py
@@ -4,15 +4,15 @@
 import numpy as np
 import pandas as pd
 import pandas_flavor as pf
-from janitor.utils import check, deprecated_alias
+from janitor.utils import check, check_column, deprecated_alias
 
 
 @pf.register_dataframe_method
 @deprecated_alias(col_name="column_name")
 def round_to_fraction(
     df: pd.DataFrame,
-    column_name: Hashable = None,
-    denominator: float = None,
+    column_name: Hashable,
+    denominator: float,
     digits: float = np.inf,
 ) -> pd.DataFrame:
     """Round all values in a column to a fraction.
@@ -45,16 +45,19 @@ def round_to_fraction(
 
     :param df: A pandas DataFrame.
     :param column_name: Name of column to round to fraction.
-    :param denominator: The denominator of the fraction for rounding.
+    :param denominator: The denominator of the fraction for rounding. Must be
+        a positive number.
     :param digits: The number of digits for rounding after rounding to the
         fraction. Default is np.inf (i.e. no subsequent rounding).
     :returns: A pandas DataFrame with a column's values rounded.
+    :raises ValueError: If `denominator` is not a positive number.
     """
-    if denominator:
-        check("denominator", denominator, [float, int])
+    check_column(df, column_name)
+    check("denominator", denominator, [float, int])
+    check("digits", digits, [float, int])
 
-    if digits:
-        check("digits", digits, [float, int])
+    if denominator <= 0:
+        raise ValueError("denominator is expected to be a positive number.")
 
     df[column_name] = round(df[column_name] * denominator, 0) / denominator
     if not np.isinf(digits):


Test patch:
diff --git a/tests/functions/test_round_to_fraction.py b/tests/functions/test_round_to_fraction.py
--- a/tests/functions/test_round_to_fraction.py
+++ b/tests/functions/test_round_to_fraction.py
@@ -3,6 +3,7 @@
 
 @pytest.mark.functions
 def test_round_to_nearest_half(dataframe):
+    """Checks output for rounding to the nearest 1/2."""
     df = dataframe.round_to_fraction("Bell__Chart", 2)
     assert df.iloc[0, 1] == 1.0
     assert df.iloc[1, 1] == 2.5
@@ -13,3 +14,16 @@ def test_round_to_nearest_half(dataframe):
     assert df.iloc[6, 1] == 1.0
     assert df.iloc[7, 1] == 2.5
     assert df.iloc[8, 1] == 3.0
+
+
+@pytest.mark.functions
+@pytest.mark.parametrize(
+    "denominator",
+    [0, -5, -0.25],
+)
+def test_invalid_denominator_args(dataframe, denominator):
+    """Ensure ValueError's are raised if denominator value passed in
+    is invalid.
+    """
+    with pytest.raises(ValueError):
+        dataframe.round_to_fraction("Bell__Chart", denominator)