J'ai des difficultés à convertir un tableau structuré chargé à partir d'un fichier CSV à l'aide de np.genfromtxt
en un np.array
afin d'adapter les données à un estimateur Scikit-Learn. Le problème est qu’à un moment donné, une conversion du tableau structuré en un tableau normal se produira, donnant lieu à un ValueError: can't cast from structure to non-structure
. Cela faisait longtemps que j'utilisais .view
pour effectuer la conversion, mais cela a entraîné un certain nombre d'avertissements relatifs à la dépréciation de NumPy. Le code est comme suit:
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)
L'exception levée est: ValueError: Can't cast from structure to non-structure, except if the structure only has a single field.
Ma deuxième tentative a été d'utiliser une vue comme suit:
# View is raising deprecation warnings
X = data[features]
X = X.view((float, len(X.dtype.names)))
y = data[target].astype(int)
Ce qui fonctionne et fait exactement ce que je veux qu'il fasse (je n'ai pas besoin d'une copie des données), mais entraîne des avertissements de dépréciation:
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.
Pour le moment, nous utilisons tolist()
pour convertir le tableau structuré en liste, puis en np.array
. Cela fonctionne, mais cela semble terriblement inefficace:
# Current method (efficient?)
X = np.array(data[features].tolist())
y = data[target].astype(int)
Il doit y avoir un meilleur moyen, je vous serais reconnaissant de tout conseil.
NOTE: les données de cet exemple proviennent du Répertoire d'occupation de UCI ML et les données s'affichent comme suit:
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')])
Ajoutez une .copy()
à data[features]
:
X = data[features].copy()
X = X.view((float, len(X.dtype.names)))
et le message FutureWarning
est parti.
Cela devrait être plus efficace que de convertir en une liste en premier.
Vous pouvez éviter la copie si vous pouvez d'abord lire les données dans un tableau NumPy ordinaire (en omettant le paramètre names
):
data = np.genfromtxt(path, dtype=float, delimiter=',', skip_header=1)
Ensuite (heureusement pour nous), X
est composé de toutes les colonnes sauf la première et la dernière (c.-à-d. En omettant les colonnes datetime
et occupancy
). Nous pouvons donc exprimer X
et y
sous forme de tranches:
X = data[:, 1:-1]
y = data[:, -1].astype(int)
Ensuite, nous pouvons passer facilement ces fonctions aux fonctions d’apprentissage de scikit:
clf = GradientBoostingClassifier(random_state=42)
clf.fit(X, y)
et, si nous le souhaitons, nous pouvons voir le tableau NumPy comme un tableau structuré par la suite:
features = ["temperature", "relative_humidity", "light", "C02", "humidity"]
X = X.ravel().view([(field, X.dtype.type) for field in features])
Malheureusement, cette solution de contournement repose sur le fait que X
peut être exprimé sous forme de tranche - nous ne pourrions pas éviter de copier si occupancy
apparaissait entre les autres colonnes de fonctions, par exemple. Cela signifie également que vous devez définir X
en utilisant X = data[:, 1:-1]
au lieu du X = data[features]
, plus compréhensible pour l'homme,.
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])
Si vous devez commencer par le tableau structuré, alors la réponse de hpaulj montre comment view/reshape/slice
le tableau structuré pour obtenir un tableau en clair sans copier:
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)
Cela tire parti du fait que chaque champ a une longueur de 8 octets. Il est donc facile de convertir le tableau structuré en un tableau simple de type <f8
. Le remodelage en fait un tableau 2D avec le même nombre de lignes. Le découpage en tranches supprime les colonnes/champs datetime
et occupancy
du tableau.