I want to apply scaling (using StandardScaler() from sklearn.preprocessing) to a pandas dataframe. The following code returns a numpy array, so I lose all the column names and indeces. This is not what I want.
features = df[["col1", "col2", "col3", "col4"]] autoscaler = StandardScaler() features = autoscaler.fit_transform(features)
A “solution” I found online is:
features = features.apply(lambda x: autoscaler.fit_transform(x))
It appears to work, but leads to a deprecationwarning:
/usr/lib/python3.5/site-packages/sklearn/preprocessing/data.py:583: DeprecationWarning: Passing 1d arrays as data is deprecated in 0.17 and will raise ValueError in 0.19. Reshape your data either using X.reshape(-1, 1) if your data has a single feature or X.reshape(1, -1) if it contains a single sample.
I therefore tried:
features = features.apply(lambda x: autoscaler.fit_transform(x.reshape(-1, 1)))
But this gives:
Traceback (most recent call last): File “./analyse.py”, line 91, in features = features.apply(lambda x: autoscaler.fit_transform(x.reshape(-1, 1))) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 3972, in apply return self._apply_standard(f, axis, reduce=reduce) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 4081, in _apply_standard result = self._constructor(data=results, index=index) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 226, in init mgr = self._init_dict(data, index, columns, dtype=dtype) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 363, in _init_dict dtype=dtype) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 5163, in _arrays_to_mgr arrays = _homogenize(arrays, index, dtype) File “/usr/lib/python3.5/site-packages/pandas/core/frame.py”, line 5477, in _homogenize raise_cast_failure=False) File “/usr/lib/python3.5/site-packages/pandas/core/series.py”, line 2885, in _sanitize_array raise Exception(‘Data must be 1-dimensional’) Exception: Data must be 1-dimensional
How do I apply scaling to the pandas dataframe, leaving the dataframe intact? Without copying the data if possible.
Advertisement
Answer
You could convert the DataFrame as a numpy array using as_matrix()
. Example on a random dataset:
Edit:
Changing as_matrix()
to values
, (it doesn’t change the result) per the last sentence of the as_matrix()
docs above:
Generally, it is recommended to use ‘.values’.
import pandas as pd import numpy as np #for the random integer example df = pd.DataFrame(np.random.randint(0.0,100.0,size=(10,4)), index=range(10,20), columns=['col1','col2','col3','col4'], dtype='float64')
Note, indices are 10-19:
In [14]: df.head(3) Out[14]: col1 col2 col3 col4 10 3 38 86 65 11 98 3 66 68 12 88 46 35 68
Now fit_transform
the DataFrame to get the scaled_features
array
:
from sklearn.preprocessing import StandardScaler scaled_features = StandardScaler().fit_transform(df.values) In [15]: scaled_features[:3,:] #lost the indices Out[15]: array([[-1.89007341, 0.05636005, 1.74514417, 0.46669562], [ 1.26558518, -1.35264122, 0.82178747, 0.59282958], [ 0.93341059, 0.37841748, -0.60941542, 0.59282958]])
Assign the scaled data to a DataFrame (Note: use the index
and columns
keyword arguments to keep your original indices and column names:
scaled_features_df = pd.DataFrame(scaled_features, index=df.index, columns=df.columns) In [17]: scaled_features_df.head(3) Out[17]: col1 col2 col3 col4 10 -1.890073 0.056360 1.745144 0.466696 11 1.265585 -1.352641 0.821787 0.592830 12 0.933411 0.378417 -0.609415 0.592830
Edit 2:
Came across the sklearn-pandas package. It’s focused on making scikit-learn easier to use with pandas. sklearn-pandas
is especially useful when you need to apply more than one type of transformation to column subsets of the DataFrame
, a more common scenario. It’s documented, but this is how you’d achieve the transformation we just performed.
from sklearn_pandas import DataFrameMapper mapper = DataFrameMapper([(df.columns, StandardScaler())]) scaled_features = mapper.fit_transform(df.copy(), 4) scaled_features_df = pd.DataFrame(scaled_features, index=df.index, columns=df.columns)