Wie verwende ich sklearn fit_transform mit Pandas und gebe Datenrahmen anstelle von numpy array zurück?

75

Ich möchte die Skalierung (mit StandardScaler () von sklearn.preprocessing) auf einen Pandas-Datenrahmen anwenden. Der folgende Code gibt ein Numpy-Array zurück, sodass ich alle Spaltennamen und Unabhängigkeiten verliere. Das will ich nicht.

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

Eine "Lösung", die ich online gefunden habe, ist:

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

Es scheint zu funktionieren, führt aber zu einer Abwertungswarnung:

/usr/lib/python3.5/site-packages/sklearn/preprocessing/data.py:583: DeprecationWarning: Das Übergeben von 1d-Arrays als Daten ist in 0.17 veraltet und erhöht ValueError in 0.19. Ändern Sie Ihre Daten entweder mit X.reshape (-1, 1), wenn Ihre Daten über ein einzelnes Feature verfügen, oder mit X.reshape (1, -1), wenn sie ein einzelnes Beispiel enthalten.

Ich habe deshalb versucht:

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

Aber das gibt:

Traceback (letzter Aufruf zuletzt): Datei "./analyse.py", Zeile 91, in features = features.apply (Lambda x: autoscaler.fit_transform (x.reshape (-1, 1))) Datei "/ usr / lib / python3.5 / site-packages / pandas / core / frame.py ", Zeile 3972, in apply return self._apply_standard (f, Achse, reduzieren = reduzieren) Datei" /usr/lib/python3.5/site- packages / pandas / core / frame.py ", Zeile 4081, in _apply_standard result = self._constructor (data = results, index = index) Datei" /usr/lib/python3.5/site-packages/pandas/core/frame .py ", Zeile 226, in init mgr = self._init_dict (Daten, Index, Spalten, dtype = dtype) Datei "/usr/lib/python3.5/site-packages/pandas/core/frame.py", Zeile 363, in _init_dict dtype = dtype) Datei "/usr/lib/python3.5/site-packages/pandas/core/frame.py", Zeile 5163, in der Datei _arrays_to_mgr arrays = _homogenize (Arrays, Index, dtype) "/usr/lib/python3.5/site -packages / pandas / core / frame.py ", Zeile 5477, in _homogenize raise_cast_failure = False) Datei" /usr/lib/python3.5/site-packages/pandas/core/series.py ", Zeile 2885, in _sanitize_array Ausnahme auslösen ('Daten müssen eindimensional sein') Ausnahme: Daten müssen eindimensional sein

Wie wende ich eine Skalierung auf den Pandas-Datenrahmen an, wobei der Datenrahmen intakt bleibt? Wenn möglich, ohne die Daten zu kopieren.

Louic
quelle

Antworten:

84

Sie können den DataFrame mithilfe von als Numpy-Array konvertieren as_matrix(). Beispiel für einen zufälligen Datensatz:

Bearbeiten: Ändern as_matrix()zu values(es ändert das Ergebnis nicht) gemäß dem letzten Satz der as_matrix()obigen Dokumente:

Im Allgemeinen wird empfohlen, '.values' zu verwenden.

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')

Beachten Sie, dass die Indizes 10-19 sind:

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

Nun fit_transformbekommt der DataFrame das 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]])

Weisen Sie die skalierten Daten einem DataFrame zu (Hinweis: Verwenden Sie die Argumente indexund die columnsSchlüsselwortargumente, um Ihre ursprünglichen Indizes und Spaltennamen beizubehalten :

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

Bearbeiten 2:

Kam über das sklearn-pandas- Paket. Es konzentriert sich darauf, das Erlernen von Scikit-Lernen mit Pandas zu vereinfachen. sklearn-pandasDies ist besonders nützlich, wenn Sie mehr als einen Transformationstyp auf Spaltenuntermengen des DataFramehäufigeren Szenarios anwenden müssen . Es ist dokumentiert, aber so würden Sie die Transformation erreichen, die wir gerade durchgeführt haben.

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)
Kevin
quelle
1
Vielen Dank für die Antwort, aber das Problem ist immer noch, dass die Zeilen neu nummeriert werden, wenn der neue Datenrahmen aus dem Array erstellt wird. Der ursprüngliche Datenrahmen enthält keine fortlaufend nummerierten Zeilen, da einige davon entfernt wurden. Ich könnte wohl auch ein Schlüsselwort index = [...] mit den alten Indexwerten hinzufügen. Wenn Sie Ihre Antwort entsprechend aktualisieren, kann ich sie akzeptieren.
Louic
Ich hoffe, die Bearbeitung hilft, ich denke, Ihre Intuition, die Indexwerte vom ersten df festzulegen, war korrekt. Die Zahlen, die ich verwendet habe, sind fortlaufend ... (wollte nur zeigen, dass Sie sie auf alles zurücksetzen können und der Bereich (10,20) war das Beste, was ich mir vorstellen konnte. Aber es funktioniert mit jedem zufälligen Index auf dem Original df. HTH!
Kevin
2
Ich sehe, dass Sie den letzten Schritt als Konvertierung der Ausgabe von DataFrameMapperin a DataFrame.. haben, also ist die Ausgabe nicht bereits eine DataFrame?
StephenBoesch
11
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)

Der df_scaled ist der 'gleiche' Datenrahmen, nur jetzt mit den skalierten Werten

Joe
quelle
1
Dies behält jedoch keine Datentypen bei
Leokury
1
Werden nicht alle Datentypen ohnehin zu Floats, da dies die einzige Ausgabe des Skalierers ist? Welche anderen Ergebnisse erwarten Sie davon? @leokury
jorijnsmit
4
features = ["col1", "col2", "col3", "col4"]
autoscaler = StandardScaler()
df[features] = autoscaler.fit_transform(df[features])
zzHQzz
quelle
5
Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
Piotr Labunski
0

Mit scaxit-learn können Sie mit Neuraxle mehrere Datentypen mischen :

Option 1: Verwerfen Sie die Zeilennamen und Spaltennamen

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(),
])

Dann gehen Sie wie beabsichtigt vor:

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

Option 2: Behalten Sie die ursprünglichen Spalten- und Zeilennamen bei

Sie können dies sogar mit einem Wrapper als solchem ​​tun:

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())

Dann gehen Sie wie beabsichtigt vor:

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

Sie können diesen Code ausprobieren. Dadurch erhalten Sie einen DataFrame mit Indizes

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]
Hassan K.
quelle
Vielen Dank für Ihre Antwort, aber die als akzeptierte Antwort angegebenen Lösungen sind viel besser. Es kann auch mit dask-ml gemacht werden: from dask_ml.preprocessing import StandardScaler; StandardScaler().fit_transform(df)
Louic