it-swarm.com.de

Konvertieren Sie ein strukturiertes Array in ein numpy-Array zur Verwendung mit Scikit-Learn

Ich habe Schwierigkeiten beim Konvertieren eines strukturierten Arrays, das aus einem CSV mit np.genfromtxt geladen wurde, in einen np.array, um die Daten an einen Scikit-Learn-Schätzer anzupassen. Das Problem ist, dass irgendwann eine Umwandlung vom strukturierten Array in ein reguläres Array auftritt, was zu einem ValueError: can't cast from structure to non-structure führt. Ich habe lange Zeit .view verwendet, um die Konvertierung durchzuführen, aber dies hat zu einer Anzahl von Abwertungswarnungen von NumPy geführt. Der Code lautet wie folgt:

import numpy as np
from sklearn.ensemble import GradientBoostingClassifier

data = np.genfromtxt(path, dtype=float, delimiter=',', names=True)

target = "occupancy"
features = [
    "temperature", "relative_humidity", "light", "C02", "humidity"
]

# Doesn't work directly
X = data[features]
y = data[target].astype(int)

clf = GradientBoostingClassifier(random_state=42)
clf.fit(X, y)

Die Ausnahme, die ausgelöst wird, ist: ValueError: Can't cast from structure to non-structure, except if the structure only has a single field.

Mein zweiter Versuch bestand darin, eine Ansicht wie folgt zu verwenden:

# View is raising deprecation warnings
X = data[features]
X = X.view((float, len(X.dtype.names)))
y = data[target].astype(int)

Was funktioniert und genau das tut, was ich möchte (ich brauche keine Kopie der Daten), führt aber zu Warnungen, die zu veraltet sind:

FutureWarning: Numpy has detected that you may be viewing or writing to 
an array returned by selecting multiple fields in a structured array.

This code may break in numpy 1.15 because this will return a view 
instead of a copy -- see release notes for details.

Im Moment verwenden wir tolist(), um das strukturierte Array in eine Liste und dann in einen np.array umzuwandeln. Dies funktioniert, erscheint jedoch als äußerst ineffizient:

# Current method (efficient?)
X = np.array(data[features].tolist())
y = data[target].astype(int)

Es muss einen besseren Weg geben, ich würde mich über Ratschläge freuen. 

NOTE: Die Daten für dieses Beispiel stammen aus dem UCI ML Occupancy Repository und die Daten sehen wie folgt aus: 

array([(nan, 23.18, 27.272 , 426.  ,  721.25, 0.00479299, 1.),
       (nan, 23.15, 27.2675, 429.5 ,  714.  , 0.00478344, 1.),
       (nan, 23.15, 27.245 , 426.  ,  713.5 , 0.00477946, 1.), ...,
       (nan, 20.89, 27.745 , 423.5 , 1521.5 , 0.00423682, 1.),
       (nan, 20.89, 28.0225, 418.75, 1632.  , 0.00427949, 1.),
       (nan, 21.  , 28.1   , 409.  , 1864.  , 0.00432073, 1.)],
      dtype=[('datetime', '<f8'), ('temperature', '<f8'), ('relative_humidity', '<f8'), 
             ('light', '<f8'), ('C02', '<f8'), ('humidity', '<f8'), ('occupancy', '<f8')])
8
bbengfort

Fügen Sie eine .copy() zu data[features] hinzu: 

X = data[features].copy()
X = X.view((float, len(X.dtype.names)))

und die FutureWarning-Nachricht ist weg.

Dies sollte effizienter sein als das Konvertieren in eine Liste.

3
Mike Müller

Sie können das Kopieren vermeiden, wenn Sie die Daten zuerst in ein einfaches NumPy-Array einlesen können (indem Sie den Parameter names weglassen):

data = np.genfromtxt(path, dtype=float, delimiter=',', skip_header=1)

Dann (zum Glück für uns) besteht X aus allen außer der ersten und letzten Spalte (d. H. Ohne die Spalten datetime und occupancy). So können wir X und y als Slices ausdrücken:

X = data[:, 1:-1]
y = data[:, -1].astype(int)

Dann können wir diese einfach an die Scikit-Learn-Funktionen weitergeben:

clf = GradientBoostingClassifier(random_state=42)
clf.fit(X, y)

und wenn wir möchten, können wir das einfache NumPy-Array anschließend als strukturiertes Array anzeigen:

features = ["temperature", "relative_humidity", "light", "C02", "humidity"]
X = X.ravel().view([(field, X.dtype.type) for field in features])

Leider hängt diese Problemumgehung davon ab, dass X als Slice ausgedrückt werden kann. Wir können das Kopieren nicht vermeiden, wenn occupancy zwischen den anderen Feature-Spalten angezeigt wird. Es bedeutet auch, dass Sie X mit X = data[:, 1:-1] anstelle des verständlicheren X = data[features] definieren müssen. 


import numpy as np
from sklearn.ensemble import GradientBoostingClassifier

data = np.genfromtxt(path, dtype=float, delimiter=',', skip_header=1)

X = data[:, 1:-1]
y = data[:, -1].astype(int)

clf = GradientBoostingClassifier(random_state=42)
clf.fit(X, y)

features = ["temperature", "relative_humidity", "light", "C02", "humidity"]
X = X.ravel().view([(field, X.dtype.type) for field in features])

Wenn Sie mit dem strukturierten Array beginnen müssen, zeigt hpauljs Antwort , wie Sie das strukturierte Array view/reshape/slice verwenden können, um ein einfaches Array zu erhalten, ohne zu kopieren: 

import numpy as np
nan = np.nan
data = np.array([(nan, 23.18, 27.272 , 426.  ,  721.25, 0.00479299, 1.),
       (nan, 23.15, 27.2675, 429.5 ,  714.  , 0.00478344, 1.),
       (nan, 23.15, 27.245 , 426.  ,  713.5 , 0.00477946, 1.), 
       (nan, 20.89, 27.745 , 423.5 , 1521.5 , 0.00423682, 1.),
       (nan, 20.89, 28.0225, 418.75, 1632.  , 0.00427949, 1.),
       (nan, 21.  , 28.1   , 409.  , 1864.  , 0.00432073, 1.)],
      dtype=[('datetime', '<f8'), ('temperature', '<f8'), ('relative_humidity', '<f8'), 
             ('light', '<f8'), ('C02', '<f8'), ('humidity', '<f8'), ('occupancy', '<f8')])

target = 'occupancy'
nrows = len(data)
X = data.view('<f8').reshape(nrows, -1)[:, 1:-1]
y = data[target].astype(int)

Dies nutzt die Tatsache, dass jedes Feld 8 Byte lang ist. Daher ist es einfach, das strukturierte Array in ein einfaches Array des Typs <f8 zu konvertieren. Das Umformen macht es zu einem 2D-Array mit der gleichen Anzahl von Zeilen. Beim Slicing werden die Spalten/Felder datetime und occupancy aus dem Array entfernt.

1
unutbu