Repository: scikit-learn/scikit-learn
Pull request: API Improves error msg when passing non-arrays to SimpleImputer
Fixes #14877.
An alternative would be calling ``asarray`` but that would be less informative imho.

Still needs a regression test. Probably one for list and one for dataframe?

Problem statement:
DataFrames not properly validated in SimpleImputer
```python
import pandas as pd
from sklearn.impute import SimpleImputer

SimpleImputer().fit(pd.DataFrame({'a': ['b', 'c']}))
```
is not validated correctly:

```pythontb
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/checkout/scikit-learn/sklearn/impute/_base.py in _validate_input(self, X)
    198             X = check_array(X, accept_sparse='csc', dtype=dtype,
--> 199                             force_all_finite=force_all_finite, copy=self.copy)
    200         except ValueError as ve:

~/checkout/scikit-learn/sklearn/utils/validation.py in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, warn_on_dtype, estimator)
    496                 warnings.simplefilter('error', ComplexWarning)
--> 497                 array = np.asarray(array, dtype=dtype, order=order)
    498             except ComplexWarning:

~/miniconda3/lib/python3.7/site-packages/numpy/core/numeric.py in asarray(a, dtype, order)
    537     """
--> 538     return array(a, dtype, copy=False, order=order)
    539 

ValueError: could not convert string to float: 'b'

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
<ipython-input-6-f08c4f6715ce> in <module>
----> 1 SimpleImputer().fit(pd.DataFrame({'a': ['b', 'c']}))

~/checkout/scikit-learn/sklearn/impute/_base.py in fit(self, X, y)
    230         self : SimpleImputer
    231         """
--> 232         X = self._validate_input(X)
    233 
    234         # default fill_value is 0 for numerical input and "missing_value"

~/checkout/scikit-learn/sklearn/impute/_base.py in _validate_input(self, X)
    202                 raise ValueError("Cannot use {0} strategy with non-numeric "
    203                                  "data. Received datatype :{1}."
--> 204                                  "".format(self.strategy, X.dtype.kind))
    205             else:
    206                 raise ve

~/miniconda3/lib/python3.7/site-packages/pandas/core/generic.py in __getattr__(self, name)
   5065             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5066                 return self[name]
-> 5067             return object.__getattribute__(self, name)
   5068 
   5069     def __setattr__(self, name, value):

AttributeError: 'DataFrame' object has no attribute 'dtype'
```

Hints from issue discussion:

Commit message: improve error message when passing non-arrays

Patch to review:
diff --git a/sklearn/impute/_base.py b/sklearn/impute/_base.py
index f7be7fd49cb64..034908414a63b 100644
--- a/sklearn/impute/_base.py
+++ b/sklearn/impute/_base.py
@@ -199,9 +199,9 @@ def _validate_input(self, X):
                             force_all_finite=force_all_finite, copy=self.copy)
         except ValueError as ve:
             if "could not convert" in str(ve):
-                raise ValueError("Cannot use {0} strategy with non-numeric "
-                                 "data. Received datatype :{1}."
-                                 "".format(self.strategy, X.dtype.kind))
+                new_ve = ValueError("Cannot use {} strategy with non-numeric "
+                                    "data:\n{}".format(self.strategy, str(ve)))
+                raise new_ve from None
             else:
                 raise ve
 


Code review comments:
diff_hunk: @@ -199,9 +199,9 @@ def _validate_input(self, X):
                             force_all_finite=force_all_finite, copy=self.copy)
         except ValueError as ve:
             if "could not convert" in str(ve):
-                raise ValueError("Cannot use {0} strategy with non-numeric "
-                                 "data. Received datatype :{1}."
-                                 "".format(self.strategy, X.dtype.kind))
+                new_ve = ValueError("Cannot use {} strategy with non-numeric "
+                                    "data:\n{}".format(self.strategy, str(ve)))
path: sklearn/impute/_base.py
text: @user1:
you shouldn't need `str` with `format`. But I think you might benefit from `!r` to use repr.