Як використовувати sklearn fit_transform з пандами і повернути фрейм даних замість масиву numpy?


80

Я хочу застосувати масштабування (за допомогою StandardScaler () від sklearn.preprocessing) до фрейму даних pandas. Наступний код повертає масив numpy, тому я втрачаю всі імена стовпців та індекси. Це не те, що я хочу.

features = df[["col1", "col2", "col3", "col4"]]
autoscaler = StandardScaler()
features = autoscaler.fit_transform(features)

"Рішення", яке я знайшов в Інтернеті:

features = features.apply(lambda x: autoscaler.fit_transform(x))

Здається, це працює, але призводить до попередження про припинення дії:

/usr/lib/python3.5/site-packages/sklearn/preprocessing/data.py:583: DeprecationWarning: Передача 1d-масивів, оскільки дані застаріли в 0.17, а ValueError - в 0.19. Змініть форму ваших даних, використовуючи X.reshape (-1, 1), якщо ваші дані мають одну функцію, або X.reshape (1, -1), якщо вони містять один зразок.

Тому я спробував:

features = features.apply(lambda x: autoscaler.fit_transform(x.reshape(-1, 1)))

Але це дає:

Відстеження (останній останній дзвінок): Файл "./analyse.py", рядок 91, у features = features.apply (лямбда x: autoscaler.fit_transform (x.reshape (-1, 1))) Файл "/ usr / lib / python3.5 / site-package / pandas / core / frame.py ", рядок 3972, у застосуванні return self._apply_standard (f, ось, зменшення = зменшення) Файл" /usr/lib/python3.5/site- package / pandas / core / frame.py ", рядок 4081, у _apply_standard result = self._constructor (дані = результати, індекс = індекс) Файл" /usr/lib/python3.5/site-packages/pandas/core/frame .py ", рядок 226, init mgr = self._init_dict (дані, індекс, стовпці, dtype = dtype) Файл "/usr/lib/python3.5/site-packages/pandas/core/frame.py", рядок 363, у _init_dict dtype = dtype) Файл "/usr/lib/python3.5/site-packages/pandas/core/frame.py", рядок 5163, у _arrays_to_mgr arrays = _homogenize (масиви, індекс, dtype) Файл "/usr/lib/python3.5/site -packages / pandas / core / frame.py ", рядок 5477, у _homogenize rise_cast_failure = False) Файл" /usr/lib/python3.5/site-packages/pandas/core/series.py ", рядок 2885, у _sanitize_array raise Exception ('Дані повинні бути одновимірними') Виняток: Дані повинні бути одновимірними

Як застосувати масштабування до фрейму даних pandas, залишаючи його незмінним? Без копіювання даних, якщо це можливо.

Відповіді:


84

Ви можете перетворити DataFrame як масив numpy за допомогою as_matrix(). Приклад випадкового набору даних:

Редагувати: зміна as_matrix()на values, (це не змінює результат) відповідно до останнього речення наведених as_matrix()вище документів:

Як правило, рекомендується використовувати '.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')

Примітка, індекси становлять 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

Тепер fit_transformDataFrame, щоб отримати 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]])

Призначте масштабовані дані DataFrame (Примітка: використовуйте аргументи indexта columnsключове слово, щоб зберегти вихідні індекси та імена стовпців:

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

Редагувати 2:

Натрапив на пакет sklearn-pandas . Вона зосереджена на тому, щоб зробити scikit-learn простішим у використанні з пандами. sklearn-pandasособливо корисний, коли вам потрібно застосувати більше одного типу перетворення до підмножин стовпців DataFrame, більш поширеного сценарію. Це задокументовано, але ось як би ви досягли трансформації, яку ми щойно здійснили.

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)

1
Дякуємо за відповідь, але проблема все-таки полягає в тому, що рядки перенумеруються, коли новий масив даних створюється з масиву. Оригінальний фрейм даних не містить послідовно пронумерованих рядків, оскільки деякі з них були видалені. Припускаю, я міг би також додати ключове слово index = [...] зі старими значеннями індексу. Якщо ви відповідно оновите свою відповідь, я можу її прийняти.
Louic

Сподіваюсь, редагування допоможе, я думаю, ваша інтуїція щодо встановлення значень індексу з першого df була правильною. Цифри, які я використовував, є послідовними ... (просто хотів показати, що ви можете скинути їх до чого завгодно і діапазон (10,20) - найкраще, що я міг придумати. Але це буде працювати з будь-яким випадковим індексом на вихідному df. HTH!
Kevin

2
Я бачу , що у вас є останній крок , як перетворення вихідного сигналу DataFrameMapperдо DataFrame.. тому виходу не вжеDataFrame ?
StephenBoesch

@StephenBoesch: Так, результат не є DataFrame. Якщо ви хочете отримати його безпосередньо від mapper, вам слід скористатися df_out=Trueопцією для DataFrameMapper.
Nerxis

13
import pandas as pd    
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('your file here')
ss = StandardScaler()
df_scaled = pd.DataFrame(ss.fit_transform(df),columns = df.columns)

Df_scaled буде "тим самим" фреймом даних, лише тепер із масштабованими значеннями


1
Але це не підтримує типи даних
leokury

1
Чи не стануть усі типи даних так чи інакше плаваючими, оскільки це єдиний результат скалера? Яких інших результатів ви очікуєте від цього? @leokury
jorijnsmit

5
features = ["col1", "col2", "col3", "col4"]
autoscaler = StandardScaler()
df[features] = autoscaler.fit_transform(df[features])

5
Хоча цей код може відповісти на питання, надання додаткового контексту щодо того, як та / або чому він вирішує проблему, покращило б довгострокове значення відповіді.
Piotr Labunski

0

Ви можете змішати кілька типів даних у scikit-learn за допомогою Neuraxle :

Варіант 1: відкиньте імена рядків та імен стовпців

from neuraxle.pipeline import Pipeline
from neuraxle.base import NonFittableMixin, BaseStep

class PandasToNumpy(NonFittableMixin, BaseStep):
    def transform(self, data_inputs, expected_outputs): 
        return data_inputs.values

pipeline = Pipeline([
    PandasToNumpy(),
    StandardScaler(),
])

Потім ви продовжуєте, як задумали:

features = df[["col1", "col2", "col3", "col4"]]  # ... your df data
pipeline, scaled_features = pipeline.fit_transform(features)

Варіант 2: зберегти оригінальні назви стовпців та назви рядків

Ви навіть можете зробити це за допомогою обгортки як такої:

from neuraxle.pipeline import Pipeline
from neuraxle.base import MetaStepMixin, BaseStep

class PandasValuesChangerOf(MetaStepMixin, BaseStep):
    def transform(self, data_inputs, expected_outputs): 
        new_data_inputs = self.wrapped.transform(data_inputs.values)
        new_data_inputs = self._merge(data_inputs, new_data_inputs)
        return new_data_inputs

    def fit_transform(self, data_inputs, expected_outputs): 
        self.wrapped, new_data_inputs = self.wrapped.fit_transform(data_inputs.values)
        new_data_inputs = self._merge(data_inputs, new_data_inputs)
        return self, new_data_inputs

    def _merge(self, data_inputs, new_data_inputs): 
        new_data_inputs = pd.DataFrame(
            new_data_inputs,
            index=data_inputs.index,
            columns=data_inputs.columns
        )
        return new_data_inputs

df_scaler = PandasValuesChangerOf(StandardScaler())

Потім ви продовжуєте, як задумали:

features = df[["col1", "col2", "col3", "col4"]]  # ... your df data
df_scaler, scaled_features = df_scaler.fit_transform(features)

-1

Ви можете спробувати цей код, це дасть вам DataFrame з індексами

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston # boston housing dataset

dt= load_boston().data
col= load_boston().feature_names

# Make a dataframe
df = pd.DataFrame(data=dt, columns=col)

# define a method to scale data, looping thru the columns, and passing a scaler
def scale_data(data, columns, scaler):
    for col in columns:
        data[col] = scaler.fit_transform(data[col].values.reshape(-1, 1))
    return data

# specify a scaler, and call the method on boston data
scaler = StandardScaler()
df_scaled = scale_data(df, col, scaler)

# view first 10 rows of the scaled dataframe
df_scaled[0:10]

Дякуємо за вашу відповідь, але рішення, прийняті як прийнята відповідь, набагато кращі. Крім того, це можна зробити за допомогою dask-ml: from dask_ml.preprocessing import StandardScaler; StandardScaler().fit_transform(df)
Louic
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.