Repository: pyjanitor-devs/pyjanitor

Issue:
Preserve original names 
While renaming the dataframe, I need to preserve the original names. For e.g.

```
santandar_data = pd.read_csv(r"train.csv", nrows=40000)  
santandar_data.shape  

santandar_data.original_names=santandar_data.columns

ndf=santandar_data

ndf.original_names

Index(['ID', 'var3', 'var15', 'imp_ent_var16_ult1', 'imp_op_var39_comer_ult1',
       'imp_op_var39_comer_ult3', 'imp_op_var40_comer_ult1',
       'imp_op_var40_comer_ult3', 'imp_op_var40_efect_ult1',
       'imp_op_var40_efect_ult3',
       ...
       'saldo_medio_var33_hace2', 'saldo_medio_var33_hace3',
       'saldo_medio_var33_ult1', 'saldo_medio_var33_ult3',
       'saldo_medio_var44_hace2', 'saldo_medio_var44_hace3',
       'saldo_medio_var44_ult1', 'saldo_medio_var44_ult3', 'var38', 'TARGET'],
      dtype='object', length=371)

```
The ndf dataframe object has a property original_names that works correctly. But when I use clean_names function, I do not get this functionality.

```
df=santandar_data.clean_names(case_type="upper", remove_special=True).limit_column_characters(3)
df.original_names
```

> AttributeError: 'DataFrame' object has no attribute 'original_names'



Discussion:

Resolving patch:
diff --git a/examples/example.py b/examples/example.py
--- a/examples/example.py
+++ b/examples/example.py
@@ -13,3 +13,4 @@
 )
 
 print(df)
+print(df.original_names)
diff --git a/janitor/functions.py b/janitor/functions.py
--- a/janitor/functions.py
+++ b/janitor/functions.py
@@ -57,6 +57,7 @@ def clean_names(
     strip_underscores: str = None,
     case_type: str = "lower",
     remove_special: bool = False,
+    preserve_original_columns: bool = True,
 ):
     """
     Clean column names.
@@ -96,7 +97,10 @@ def clean_names(
     :param remove_special: (optional) Remove special characters from columns.
         Only letters, numbers and underscores are preserved.
     :returns: A pandas DataFrame.
+    :param preserve_original_columns: (optional) Preserve original names.
+        This is later retrievable using `df.original_columns`.
     """
+    original_column_names = list(df.columns)
 
     assert case_type.lower() in {
         "preserve",
@@ -133,6 +137,10 @@ def _remove_special(col):
 
     df = df.rename(columns=lambda x: re.sub("_+", "_", x))
     df = _strip_underscores(df, strip_underscores)
+
+    # Store the original column names, if enabled by user
+    if preserve_original_columns:
+        df.original_columns = original_column_names
     return df
 
 
@@ -537,16 +545,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


Test patch:
diff --git a/tests/test_functions.py b/tests/test_functions.py
--- a/tests/test_functions.py
+++ b/tests/test_functions.py
@@ -117,6 +117,18 @@ def test_clean_names_uppercase(dataframe):
     assert set(df.columns) == set(expected_columns)
 
 
+def test_clean_names_original_columns(dataframe):
+    df = dataframe.clean_names(preserve_original_columns=True)
+    expected_columns = [
+        "a",
+        "Bell__Chart",
+        "decorated-elephant",
+        "animals@#$%^",
+        "cities",
+    ]
+    assert set(df.original_columns) == set(expected_columns)
+
+
 def test_remove_empty(null_df):
     df = remove_empty(null_df)
     assert df.shape == (8, 2)
@@ -513,44 +525,44 @@ def test_add_column(dataframe):
 
     # column already exists
     with pytest.raises(ValueError):
-        dataframe.add_column('a', 42)
+        dataframe.add_column("a", 42)
 
     # too many values for dataframe num rows:
     with pytest.raises(ValueError):
-        dataframe.add_column('toomany', np.ones(100))
+        dataframe.add_column("toomany", np.ones(100))
 
     # functionality testing
 
     # column appears in DataFrame
-    df = dataframe.add_column('fortytwo', 42)
-    assert 'fortytwo' in df.columns
+    df = dataframe.add_column("fortytwo", 42)
+    assert "fortytwo" in df.columns
 
     # values are correct in dataframe for scalar
     series = pd.Series([42] * len(dataframe))
-    series.name = 'fortytwo'
-    pd.testing.assert_series_equal(df['fortytwo'], series)
+    series.name = "fortytwo"
+    pd.testing.assert_series_equal(df["fortytwo"], series)
 
     # values are correct in dataframe for iterable
     vals = np.linspace(0, 43, len(dataframe))
-    df = dataframe.add_column('fortythree', vals)
+    df = dataframe.add_column("fortythree", vals)
     series = pd.Series(vals)
-    series.name = 'fortythree'
-    pd.testing.assert_series_equal(df['fortythree'], series)
+    series.name = "fortythree"
+    pd.testing.assert_series_equal(df["fortythree"], series)
 
     # fill_remaining works - iterable shorter than DataFrame
     vals = [0, 42]
     target = [0, 42] * 4 + [0]
-    df = dataframe.add_column('fill_in_iterable', vals, fill_remaining=True)
+    df = dataframe.add_column("fill_in_iterable", vals, fill_remaining=True)
     series = pd.Series(target)
-    series.name = 'fill_in_iterable'
-    pd.testing.assert_series_equal(df['fill_in_iterable'], series)
+    series.name = "fill_in_iterable"
+    pd.testing.assert_series_equal(df["fill_in_iterable"], series)
 
     # fill_remaining works - value is scalar
     vals = 42
-    df = dataframe.add_column('fill_in_scalar', vals, fill_remaining=True)
+    df = dataframe.add_column("fill_in_scalar", vals, fill_remaining=True)
     series = pd.Series([42] * len(df))
-    series.name = 'fill_in_scalar'
-    pd.testing.assert_series_equal(df['fill_in_scalar'], series)
+    series.name = "fill_in_scalar"
+    pd.testing.assert_series_equal(df["fill_in_scalar"], series)
 
 
 def test_add_columns(dataframe):
@@ -564,12 +576,12 @@ def test_add_columns(dataframe):
     df = dataframe.add_columns(x=x_vals, y=y_vals)
 
     series = pd.Series([x_vals] * len(dataframe))
-    series.name = 'x'
-    pd.testing.assert_series_equal(df['x'], series)
+    series.name = "x"
+    pd.testing.assert_series_equal(df["x"], series)
 
     series = pd.Series(y_vals)
-    series.name = 'y'
-    pd.testing.assert_series_equal(df['y'], series)
+    series.name = "y"
+    pd.testing.assert_series_equal(df["y"], series)
 
 
 def test_limit_column_characters(dataframe):