from evalml.pipelines.components import Imputer
df = pd.DataFrame({"a": [1, 2, 3], "b": ["1", "2", None]})
imputer = Imputer()
imputer.fit(df)
from evalml.pipelines.components import Imputer
df_with_bool = pd.DataFrame({"a": [1, 2, 3], "b": [True, False, None]})
imputer = Imputer()
imputer.fit(df_with_bool)
Les deux ont le même stacktrace :
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-69-9af4cfc17aec> in <module>
1 df_with_bool = pd.DataFrame({"a": [1, 2, 3], "b": [True, False, None]})
2 imputer = Imputer()
----> 3 imputer.fit(df_with_bool)
~/sources/evalml/evalml/utils/base_meta.py in _set_fit(self, X, y)
12 @wraps(method)
13 def _set_fit(self, X, y=None):
---> 14 return_value = method(self, X, y)
15 self._is_fitted = True
16 return return_value
~/sources/evalml/evalml/pipelines/components/transformers/imputers/imputer.py in fit(self, X, y)
76 X_categorical = X_null_dropped.select_dtypes(include=categorical_dtypes + boolean)
77 if len(X_categorical.columns) > 0:
---> 78 self._categorical_imputer.fit(X_categorical, y)
79 self._categorical_cols = X_categorical.columns
80 return self
~/sources/evalml/evalml/utils/base_meta.py in _set_fit(self, X, y)
12 @wraps(method)
13 def _set_fit(self, X, y=None):
---> 14 return_value = method(self, X, y)
15 self._is_fitted = True
16 return return_value
~/sources/evalml/evalml/pipelines/components/transformers/imputers/simple_imputer.py in fit(self, X, y)
42 if not isinstance(X, pd.DataFrame):
43 X = pd.DataFrame(X)
---> 44 self._component_obj.fit(X, y)
45 self._all_null_cols = set(X.columns) - set(X.dropna(axis=1, how='all').columns)
46 return self
~/miniconda3/envs/evalml/lib/python3.8/site-packages/sklearn/impute/_base.py in fit(self, X, y)
300 fill_value)
301 else:
--> 302 self.statistics_ = self._dense_fit(X,
303 self.strategy,
304 self.missing_values,
~/miniconda3/envs/evalml/lib/python3.8/site-packages/sklearn/impute/_base.py in _dense_fit(self, X, strategy, missing_values, fill_value)
384 row_mask = np.logical_not(row_mask).astype(np.bool)
385 row = row[row_mask]
--> 386 most_frequent[i] = _most_frequent(row, np.nan, 0)
387
388 return most_frequent
~/miniconda3/envs/evalml/lib/python3.8/site-packages/sklearn/impute/_base.py in _most_frequent(array, extra_value, n_repeat)
40 # has already been NaN-masked.
41 warnings.simplefilter("ignore", RuntimeWarning)
---> 42 mode = stats.mode(array)
43
44 most_frequent_value = mode[0][0]
~/miniconda3/envs/evalml/lib/python3.8/site-packages/scipy/stats/stats.py in mode(a, axis, nan_policy)
498 counts = np.zeros(a_view.shape[:-1], dtype=np.int)
499 for ind in inds:
--> 500 modes[ind], counts[ind] = _mode1D(a_view[ind])
501 newshape = list(a.shape)
502 newshape[axis] = 1
~/miniconda3/envs/evalml/lib/python3.8/site-packages/scipy/stats/stats.py in _mode1D(a)
485
486 def _mode1D(a):
--> 487 vals, cnts = np.unique(a, return_counts=True)
488 return vals[cnts.argmax()], cnts.max()
489
<__array_function__ internals> in unique(*args, **kwargs)
~/miniconda3/envs/evalml/lib/python3.8/site-packages/numpy/lib/arraysetops.py in unique(ar, return_index, return_inverse, return_counts, axis)
259 ar = np.asanyarray(ar)
260 if axis is None:
--> 261 ret = _unique1d(ar, return_index, return_inverse, return_counts)
262 return _unpack_tuple(ret)
263
~/miniconda3/envs/evalml/lib/python3.8/site-packages/numpy/lib/arraysetops.py in _unique1d(ar, return_index, return_inverse, return_counts)
320 aux = ar[perm]
321 else:
--> 322 ar.sort()
323 aux = ar
324 mask = np.empty(aux.shape, dtype=np.bool_)
TypeError: '<' not supported between instances of 'NoneType' and 'bool'
Cela fonctionne quand c'est np.nan
au lieu de None
@freddyaboulton merci pour le reproducteur clair ! Il semble que cela explique également un autre bogue #1092.
Problème
Si une fonctionnalité dans le cadre de données des pandas a le type object
et contient une None
, notre Imputer
échoue.
X = pd.DataFrame({'feature1': [False, True, None, np.nan]})
crée une fonctionnalité avec le type object
. Imputer.fit
échoue.X = pd.DataFrame({'feature1': [False, True, np.nan]})
crée une entité avec le type object
. Imputer.fit
fonctionne.X = pd.DataFrame({'feature1': [False, True]})
crée une entité avec le type bool
. Imputer.fit
fonctionne.Il en est de même pour le type category
. Une situation similaire se produit pour les types chaîne, bien que le dernier cas ne s'applique pas.
Remarques
La chose déroutante ici est que None
peut signifier différentes choses. Il peut être identique à nan
, ou il peut être conçu comme sa propre catégorie.
Je pense que c'est bien de le traiter comme nan
tant que nous documentons et expliquons cette convention.
solution de contournement
Supprimer None
des caractéristiques bool/catégorie/chaîne : df = df.fillna(value=np.nan)
Réparer
Court terme:
Imputer
jour None
par np.nan
Imputer
jour le Imputer
avec l'inclusion de None
dans les données, pour tous les types de données souhaités.Nous pourrions plutôt ajouter un DataCheck
qui génère des erreurs s'il y a des None
dans les données. Mais cela semble inutile car les None
s peuvent être facilement convertis.
Long terme:
Une fois que nous aurons mis à jour evalml pour utiliser la nouvelle structure de données DataTable
, les utilisateurs pourront configurer les types de chaque fonctionnalité à l'avance. J'espère que cela signifie que la normalisation rendra ce genre d'erreurs non pertinent.
Est-ce lié au #540 ?
@angela97lin 🤦 100% lié... en fait c'est un dupe. Haha. Nous avons même décidé là-bas que l'imputeur convertisse les None
s en np.nan
s.
Clôture #540 en faveur de cela parce que les articles ici sont plus à jour.
Merci!