Repository: pyjanitor-devs/pyjanitor

Issue:
`slice` fails for columns selection in `pivot_wider`
# Brief Description

Using `slice` when selecting columns in `pivot_wider` fails.

# System Information

- Operating system: macOS/
- Python version (required):3.9

# Minimally Reproducible Code

```py
df = pd.DataFrame({'Author': {0: 1, 1: 1, 2: 2, 3: 2},
'Article': {0: 11, 1: 11, 2: 22, 3: 22},
'Year': {0: 2017, 1: 2018, 2: 2017, 3: 2018},
'First': {0: 1, 1: 0, 2: 0, 3: 0},
'Second': {0: 0, 1: 1, 2: 1, 3: 1}})

df

   Author  Article  Year  First  Second
0       1       11  2017      1       0
1       1       11  2018      0       1
2       2       22  2017      0       1
3       2       22  2018      0       1

# works fine
df.pivot_wider(index = ['Author','Article'], names_from = 'Year', flatten_levels = False)
Out[159]:
               First      Second
Year            2017 2018   2017 2018
Author Article
1      11          1    0      0    1
2      22          0    0      1    1


# this fails
df.pivot_wider(index = slice('Author','Article'), names_from = 'Year', flatten_levels = False)
```

# Error Messages

```py
ValueError                                Traceback (most recent call last)
Input In [160], in <cell line: 1>()
----> 1 df.pivot_wider(index = slice('Author','Article'), names_from = 'Year', flatten_levels = False)

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/pandas_flavor/register.py:29, in register_dataframe_method.<locals>.inner.<locals>.AccessorMethod.__call__(self, *args, **kwargs)
     27 @wraps(method)
     28 def __call__(self, *args, **kwargs):
---> 29     return method(self._obj, *args, **kwargs)

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/janitor/functions/pivot.py:1317, in pivot_wider(df, index, names_from, values_from, flatten_levels, names_sep, names_glue, reset_index, names_expand, index_expand)
   1207 """
   1208 Reshapes data from *long* to *wide* form.
   1209
   (...)
   1312     form.
   1313 """
   1315 df = df.copy()
-> 1317 return _computations_pivot_wider(
   1318     df,
   1319     index,
   1320     names_from,
   1321     values_from,
   1322     flatten_levels,
   1323     names_sep,
   1324     names_glue,
   1325     reset_index,
   1326     names_expand,
   1327     index_expand,
   1328 )

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/janitor/functions/pivot.py:1377, in _computations_pivot_wider(df, index, names_from, values_from, flatten_levels, names_sep, names_glue, reset_index, names_expand, index_expand)
   1343 """
   1344 This is the main workhorse of the `pivot_wider` function.
   1345
   (...)
   1350 A dataframe pivoted from long to wide form is returned.
   1351 """
   1353 (
   1354     df,
   1355     index,
   (...)
   1374     index_expand,
   1375 )
-> 1377 df = df.pivot(  # noqa: PD010
   1378     index=index, columns=names_from, values=values_from
   1379 )
   1381 indexer = df.index
   1382 if index_expand and index:

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/pandas/core/frame.py:7870, in DataFrame.pivot(self, index, columns, values)
   7865 @Substitution("")
   7866 @Appender(_shared_docs["pivot"])
   7867 def pivot(self, index=None, columns=None, values=None) -> DataFrame:
   7868     from pandas.core.reshape.pivot import pivot
-> 7870     return pivot(self, index=index, columns=columns, values=values)

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/pandas/core/reshape/pivot.py:493, in pivot(data, index, columns, values)
    490     append = index is None
    491     # error: Unsupported operand types for + ("List[Any]" and "ExtensionArray")
    492     # error: Unsupported left operand type for + ("ExtensionArray")
--> 493     indexed = data.set_index(
    494         cols + columns_listlike, append=append  # type: ignore[operator]
    495     )
    496 else:
    497     if index is None:

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/pandas/util/_decorators.py:311, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
    305 if len(args) > num_allow_args:
    306     warnings.warn(
    307         msg.format(arguments=arguments),
    308         FutureWarning,
    309         stacklevel=stacklevel,
    310     )
--> 311 return func(*args, **kwargs)

File ~/miniforge3/envs/playground/lib/python3.9/site-packages/pandas/core/frame.py:5538, in DataFrame.set_index(self, keys, drop, append, inplace, verify_integrity)
   5533             to_remove.append(col)
   5535     if len(arrays[-1]) != len(self):
   5536         # check newest element against length of calling frame, since
   5537         # ensure_index_from_sequences would not raise for append=False.
-> 5538         raise ValueError(
   5539             f"Length mismatch: Expected {len(self)} rows, "
   5540             f"received array of length {len(arrays[-1])}"
   5541         )
   5543 index = ensure_index_from_sequences(arrays, names)
   5545 if verify_integrity and not index.is_unique:

ValueError: Length mismatch: Expected 4 rows, received array of length 2
```



Discussion:

Resolving patch:
diff --git a/janitor/functions/pivot.py b/janitor/functions/pivot.py
--- a/janitor/functions/pivot.py
+++ b/janitor/functions/pivot.py
@@ -1492,8 +1492,9 @@ def _data_checks_pivot_wider(
 
     if index is not None:
         if is_list_like(index):
-            index = [*index]
+            index = list(index)
         index = _select_column_names(index, df)
+        index = list(index)
 
     if names_from is None:
         raise ValueError(
@@ -1501,13 +1502,15 @@ def _data_checks_pivot_wider(
         )
 
     if is_list_like(names_from):
-        names_from = [*names_from]
+        names_from = list(names_from)
     names_from = _select_column_names(names_from, df)
+    names_from = list(names_from)
 
     if values_from is not None:
         if is_list_like(values_from):
-            values_from = [*values_from]
+            values_from = list(values_from)
         out = _select_column_names(values_from, df)
+        out = list(out)
         # hack to align with pd.pivot
         if values_from == out[0]:
             values_from = out[0]


Test patch:
diff --git a/tests/functions/test_pivot_wider.py b/tests/functions/test_pivot_wider.py
--- a/tests/functions/test_pivot_wider.py
+++ b/tests/functions/test_pivot_wider.py
@@ -433,7 +433,7 @@ def test_names_glue_single_column(df_checks_output):
     )
 
     result = df_checks_output.pivot_wider(
-        ["geoid", "name"],
+        slice("geoid", "name"),
         "variable",
         "estimate",
         names_glue="{variable}_estimate",