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