Repository: pyjanitor-devs/pyjanitor

Issue:
Feature thought - chainable, in place reset_index 
As far as I'm aware, an `inplace=True` `reset_index()` does not return a `DataFrame`.


Discussion:
@zbarry hmm... gotta think about this. I think `reset_index()` without `inplace=True` is meant to be part of a method chain, as `reset_index()` returns a DataFrame.
I believe `reset_index()` copies the data:

```python
df = pd.DataFrame({'x': np.arange(0, 10)})

df2 = df.reset_index(drop=True)

print('before:')

display(df)
display(df2)

df2.iloc[0, 0] = 1

print('after:')

display(df)
display(df2)
```

`df2` is modified, but `df` is not.
Thinking just a little bit about this, I feel like if such a feature were to be implemented, it would have to be named something completely different instead of changing the fundamental behavior of `reset_index()`. I don't have enough experience with pandas, though, to know what an intuitive name would even be.
Proposal to just name it `reset_index_inplace()` and clearly explain the logic of why this exists in the docstring.

Resolving patch:
diff --git a/janitor/functions.py b/janitor/functions.py
--- a/janitor/functions.py
+++ b/janitor/functions.py
@@ -546,16 +546,12 @@ def fill_empty(df, columns, value):
         for col in columns:
             assert (
                 col in df.columns
-            ), "{col} missing from dataframe columns!".format(
-                col=col
-            )
+            ), "{col} missing from dataframe columns!".format(col=col)
             df[col] = df[col].fillna(value)
     else:
         assert (
             columns in df.columns
-        ), "{col} missing from dataframe columns!".format(
-            col=columns
-        )
+        ), "{col} missing from dataframe columns!".format(col=columns)
         df[columns] = df[columns].fillna(value)
 
     return df
@@ -1575,6 +1571,57 @@ def collapse_levels(df: pd.DataFrame, sep: str = "_"):
     return df
 
 
+@pf.register_dataframe_method
+def reset_index_inplace(df: pd.DataFrame, *args, **kwargs):
+    """
+    Returns the dataframe with an inplace resetting of the index.
+
+    Compared to non-inplace resetting, this avoids data copying, thus
+    providing a potential speedup.
+
+    In Pandas, `reset_index()`, when used in place, does not return a
+    `DataFrame`, preventing this option's usage in the function-chaining
+    scheme. `reset_index_inplace()` provides one the ability to save
+    computation time and memory while still being able to use the chaining
+    syntax core to pyjanitor. This function, therefore, is the chaining
+    equivalent of:
+
+    .. code-block:: python
+
+        df = (
+            pd.DataFrame(...)
+            .operation1(...)
+        )
+
+        df.reset_index(inplace=True)
+
+        df = df.operation2(...)
+
+    instead, being called simply as:
+
+    .. code-block:: python
+
+        df = (
+            pd.DataFrame(...)
+            .operation1(...)
+            .reset_index_inplace()
+            .operation2(...)
+        )
+
+    All supplied parameters are sent directly to `DataFrame.reset_index()`.
+
+    :param df: A pandas DataFrame.
+    :param args: Arguments supplied to `DataFrame.reset_index()`
+    :param kwargs: Arguments supplied to `DataFrame.reset_index()`
+    :returns: df
+    """
+
+    kwargs.update(inplace=True)
+
+    df.reset_index(*args, **kwargs)
+    return df
+
+
 def check(varname: str, value, expected_types: list):
     """
     One-liner syntactic sugar for checking types.


Test patch:
diff --git a/tests/test_functions.py b/tests/test_functions.py
--- a/tests/test_functions.py
+++ b/tests/test_functions.py
@@ -295,7 +295,9 @@ def test_single_column_fail_label_encode():
     with pytest.raises(AssertionError):
         df = pd.DataFrame(
             {"a": ["hello", "hello", "sup"], "b": [1, 2, 3]}
-        ).label_encode(columns="c")  # noqa: 841
+        ).label_encode(
+            columns="c"
+        )  # noqa: 841
 
 
 def test_multicolumn_label_encode():
@@ -842,3 +844,31 @@ def test_collapse_levels_functionality_3level(
             "decorated-elephantAsDfr.i.p-rhino :'(AsDfdeadly__flamingo",
         ]
     )
+
+
+def test_reset_index_inplace_obj_equivalence(dataframe):
+    """ Make sure operation is indeed in place. """
+
+    df_riip = dataframe.reset_index_inplace()
+
+    assert df_riip is dataframe
+
+
+def test_reset_index_inplace_after_group(dataframe):
+    """ Make sure equivalent output to non-in place. """
+
+    df_sum = dataframe.groupby(["animals@#$%^", "cities"]).sum()
+
+    df_sum_ri = df_sum.reset_index()
+    df_sum.reset_index_inplace()
+
+    pd.testing.assert_frame_equal(df_sum_ri, df_sum)
+
+
+def test_reset_index_inplace_drop(dataframe):
+    """ Test that correctly accepts `reset_index()` parameters. """
+
+    pd.testing.assert_frame_equal(
+        dataframe.reset_index(drop=True),
+        dataframe.reset_index_inplace(drop=True),
+    )