Evalml: Imputer no puede encajar cuando no hay Ninguno en una columna categórica o booleana

Creado en 19 ago. 2020  ·  3Comentarios  ·  Fuente: alteryx/evalml

Reproductor

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)

Ambos tienen el mismo 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'

Esto funciona cuando es np.nan lugar de None

bug

Todos 3 comentarios

@freddyaboulton ¡ gracias por el claro reproductor! Parece que esto también explica otro error # 1092.

Problema
Si alguna característica en el marco de datos de pandas tiene el tipo object y contiene un None , nuestro Imputer falla.

  1. X = pd.DataFrame({'feature1': [False, True, None, np.nan]}) crea una función con el tipo object . Imputer.fit falla.
  2. X = pd.DataFrame({'feature1': [False, True, np.nan]}) crea una función con el tipo object . Imputer.fit funciona.
  3. X = pd.DataFrame({'feature1': [False, True]}) crea una función con el tipo bool . Imputer.fit funciona.

Lo mismo ocurre con el tipo category . Una situación similar ocurre con los tipos de cadenas, aunque el último caso no se aplica.

Notas
Lo confuso aquí es que None puede significar cosas diferentes. Podría ser lo mismo que nan , o podría ser su propia categoría.

Creo que está bien tratarlo como nan siempre que documentemos y expliquemos esa convención.

Solución alterna
Limpiar None de características bool / categoría / cadena: df = df.fillna(value=np.nan)

Reparar
Término corto:

  • Actualice Imputer para reemplazar None con np.nan
  • Actualice Imputer API doc y automl user guide para mencionar esto.
  • Agregue una cobertura de prueba de Imputer con la inclusión de None en los datos, para todos los tipos de datos previstos.

En su lugar, podríamos agregar un DataCheck que errores si hay None s en los datos. Pero esto parece innecesario ya que None s se pueden convertir fácilmente.

A largo plazo:
Una vez que actualicemos evalml para usar la nueva estructura de datos DataTable , los usuarios podrán configurar los tipos de cada característica con anticipación. Espero que esto signifique que la estandarización hará que este tipo de errores sean irrelevantes.

¿Está esto relacionado con el n. ° 540?

@ angela97lin 🤦 100% relacionado ... de hecho es un dup. Ja ja. Incluso decidimos que el imputador convirtiera None s en np.nan s.

Cerrando el n. ° 540 a favor de esto porque los informes aquí están más actualizados.

¡Gracias!

¿Fue útil esta página
0 / 5 - 0 calificaciones