Evalml: Imputer passt nicht, wenn None in einer kategorialen oder booleschen Spalte steht

Erstellt am 19. Aug. 2020  ·  3Kommentare  ·  Quelle: alteryx/evalml

Wiedergabegerät

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)

Beide haben den gleichen 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'

Dies funktioniert, wenn es np.nan anstelle von None

bug

Alle 3 Kommentare

@freddyaboulton danke für den klaren Reproduzierer! Es scheint, dass dies auch einen anderen Fehler #1092 erklärt.

Problem
Wenn irgendeine Funktion in der Pandas hat Datenrahmen object Typen und enthält einen None Wert, unser Imputer ausfällt.

  1. X = pd.DataFrame({'feature1': [False, True, None, np.nan]}) erstellt ein Feature mit dem Typ object . Imputer.fit schlägt fehl.
  2. X = pd.DataFrame({'feature1': [False, True, np.nan]}) erstellt ein Feature mit dem Typ object . Imputer.fit funktioniert.
  3. X = pd.DataFrame({'feature1': [False, True]}) erstellt ein Feature mit dem Typ bool . Imputer.fit funktioniert.

Das gleiche gilt für den Typ category . Eine ähnliche Situation tritt für Zeichenfolgentypen auf, obwohl der letzte Fall nicht zutrifft.

Anmerkungen
Das Verwirrende hier ist, dass None verschiedene Bedeutungen haben kann. Es könnte dasselbe wie nan oder als eigene Kategorie gedacht sein.

Ich denke, es ist in Ordnung, es als nan zu behandeln, solange wir diese Konvention dokumentieren und erklären.

Problemumgehung
Bereinigen Sie None aus Bool-/Kategorie-/String-Features: df = df.fillna(value=np.nan)

Fix
Kurzfristig:

  • Imputer aktualisieren, um None durch np.nan zu ersetzen
  • Aktualisieren Sie das Imputer API-Dokument und das automl-Benutzerhandbuch, um dies zu erwähnen.
  • Fügen Sie für alle beabsichtigten Datentypen eine Testabdeckung von Imputer fügen Sie None in die Daten ein.

Wir könnten stattdessen ein DataCheck hinzufügen, welches Fehler enthält, wenn es None s in den Daten gibt. Dies fühlt sich jedoch unnötig an, da None s leicht konvertiert werden können.

Langfristig:
Sobald wir evalml so aktualisiert haben, dass es die neue DataTable Datenstruktur verwendet, können Benutzer die Typen jeder Funktion im Voraus konfigurieren. Ich hoffe, dies bedeutet, dass die Standardisierung diese Art von Fehlern irrelevant macht.

Hat das mit #540 zu tun?

@angela97lin 🤦 100% verwandt ... in der Tat ist es ein Dup. Haha. Dort haben wir sogar beschlossen, dass der Imputer None s in np.nan s umwandelt.

Schließe #540 dafür, weil die Zuschreibungen hier aktueller sind.

Vielen Dank!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen