Repository: pyjanitor-devs/pyjanitor

Issue:
[ENH] Function that removes constant columns
Example from @CihanSoylu:

```python
def drop_constant_columns(df):
    """
    Finds and drops the constant columns from a Pandas data frame
    
    Parameters
    ----------
    df : pd.DataFrame
        The Pandas data frame.
    
    Returns
    -------
    pd.DataFrame
        The Pandas data frame with the constant columns dropped.
    """
    # Find the constant columns
    constant_columns = []
    for col in df.columns:
        if len(df[col].unique()) == 1:
            constant_columns.append(col)
    
    # Drop constant columns from df and return it
    return df.drop(labels = constant_columns, 
                   axis = 1
                  )
```


Discussion:
@ericmjl , @CihanSoylu, do you mind adding an example for this function
import pandas as pd
import janitor
data_dict = {
    "a": [1, 1, 1] * 3,
    "Bell__Chart": [1, 2, 3] * 3,
    "decorated-elephant": [1, 1, 1] * 3,
    "animals": ["rabbit", "leopard", "lion"] * 3,
    "cities": ["Cambridge", "Shanghai", "Basel"] * 3
}

example_dataframe = pd.DataFrame(data_dict)
#print(example_dataframe)
def drop_constant_columns(df):
    """
    Finds and drops the constant columns from a Pandas data frame
    
    Parameters
    ----------
    df : pd.DataFrame
        The Pandas data frame.
    
    Returns
    -------
    pd.DataFrame
        The Pandas data frame with the constant columns dropped.
    """
    # Find the constant columns
    constant_columns = []
    for col in df.columns:
        if len(df[col].unique()) == 1:
            constant_columns.append(col)
    
    # Drop constant columns from df and return it
    return df.drop(labels = constant_columns, axis = 1)
drop_constant_columns(example_dataframe)
this outputs:->
<html>
<body>
<!--StartFragment-->

Bell__Chart | animals | cities
-- | -- | --
1 | rabbit | Cambridge
2 | leopard | Shanghai
3 | lion | Basel
1 | rabbit | Cambridge
2 | leopard | Shanghai
3 | lion | Basel
1 | rabbit | Cambridge
2 | leopard | Shanghai
3 | lion | Basel

<!--EndFragment-->
</body>
</html>
So 
all the columns with constant values are dropped as expected? Is my understanding correct? 
@fireddd you have the example done right :smile:
So what the next step here? Add this to functions.py and setup a test for it.
That is right, looking forward to reviewing your PR!

Resolving patch:
diff --git a/janitor/functions.py b/janitor/functions.py
--- a/janitor/functions.py
+++ b/janitor/functions.py
@@ -4381,6 +4381,90 @@ def flag_nulls(
     return df
 
 
+@pf.register_dataframe_method
+def drop_constant_columns(
+    df: pd.DataFrame,
+) -> pd.DataFrame:
+    """
+    Finds and drops the constant columns from a Pandas data frame
+
+    This method does not mutate the original DataFrame.
+    Functional usage syntax:
+
+    .. code-block:: python
+
+        import pandas as pd
+        import janitor as jn
+
+        data_dict = {
+        "a": [1, 1, 1] * 3,
+        "Bell__Chart": [1, 2, 3] * 3,
+        "decorated-elephant": [1, 1, 1] * 3,
+        "animals": ["rabbit", "leopard", "lion"] * 3,
+        "cities": ["Cambridge", "Shanghai", "Basel"] * 3
+        }
+
+        df = pd.DataFrame(data_dict)
+
+        df = jn.functions.drop_constant_columns(df)
+
+    Method chaining usage example:
+
+    .. code-block:: python
+
+        import pandas as pd
+        import janitor
+
+        df = pd.DataFrame(...)
+
+        df = df.drop_constant_columns()
+
+    :param df: Input Pandas dataframe
+    :returns: The Pandas data frame with the constant columns dropped.
+    """
+
+    # :Example 1: Drop columns with a single value:
+
+    # .. code-block:: python
+
+    #         import pandas as pd
+    #         import janitor as jn
+
+    #         data_dict = {
+    #         "a": [1, 1, 1] * 3,
+    #         "Bell": [1, 2, 3] * 3,
+    #         "decorated-elephant": [1, 1, 1] * 3,
+    #         "animals": ["rabbit", "leopard", "lion"] * 3,
+    #         "cities": ["Cambridge", "Shanghai", "Basel"] * 3
+    #         }
+
+    # .. code-block:: python
+
+    #     df.drop_constant_columns()
+
+    # .. code-block:: python
+
+    #     Bell  animals cities
+    #   0   1   rabbit  Cambridge
+    #   1   2   leopard Shanghai
+    #   2   3   lion    Basel
+    #   3   1   rabbit  Cambridge
+    #   4   2   leopard Shanghai
+    #   5   3   lion    Basel
+    #   6   1   rabbit  Cambridge
+    #   7   2   leopard Shanghai
+    #   8   3   lion    Basel
+
+    # Find the constant columns
+    constant_columns = []
+    for col in df.columns:
+        if len(df[col].unique()) == 1:
+            constant_columns.append(col)
+
+    # Drop constant columns from df and return it
+    return df.drop(labels=constant_columns, axis=1)
+
+
 @pf.register_dataframe_method
 def count_cumulative_unique(
     df: pd.DataFrame,


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -132,6 +132,20 @@ def df_duplicated_columns():
     return clean_df
 
 
+@pytest.fixture
+def df_constant_columns():
+    """Return a dataframe that has columns with constant values."""
+    data = {
+        "a": [1] * 9,
+        "Bell__Chart": [1.234_523_45, 2.456_234, 3.234_612_5] * 3,
+        "decorated-elephant": [1, 2, 3] * 3,
+        "animals@#$%^": ["rabbit"] * 9,
+        "cities": ["Cambridge", "Shanghai", "Basel"] * 3,
+    }
+    df = pd.DataFrame(data)
+    return df
+
+
 def pytest_configure():
     pytest.TEST_DATA_DIR = TEST_DATA_DIR
     pytest.EXAMPLES_DIR = EXAMPLES_DIR
diff --git a/tests/functions/test_drop_constant_columns.py b/tests/functions/test_drop_constant_columns.py
new file mode 100644
--- /dev/null
+++ b/tests/functions/test_drop_constant_columns.py
@@ -0,0 +1,18 @@
+import pandas as pd
+import pytest
+from pandas.testing import assert_frame_equal
+
+
+@pytest.mark.functions
+def test_drop_constant_columns(df_constant_columns):
+    """Test that executes drop_constant_columns function."""
+    processed_df = df_constant_columns.drop_constant_columns()
+    expected_col_list = ["Bell__Chart", "decorated-elephant", "cities"]
+    assert processed_df.columns.to_list() == expected_col_list
+    data = {
+        "Bell__Chart": [1.234_523_45, 2.456_234, 3.234_612_5] * 3,
+        "decorated-elephant": [1, 2, 3] * 3,
+        "cities": ["Cambridge", "Shanghai", "Basel"] * 3,
+    }
+    expected_df = pd.DataFrame(data)
+    assert_frame_equal(processed_df, expected_df)