Repository: geopandas/geopandas

Issue:
REGR: *Sampled plotting scheme fails
GeoPandas 0.8.0 has a regression causing plots with scheme `JenksCaspallSampled` or `FisherJenksSampled` fail.

```python
df = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
df.plot(column='pop_est', scheme='JenksCaspallSampled')

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-3fe371919d49> in <module>
----> 1 df.plot(column='pop_est', scheme='JenksCaspallSampled')

~/Dropbox/Python/geopandas/martinfleis/geopandas/geodataframe.py in plot(self, *args, **kwargs)
    897         from there.
    898         """
--> 899         return plot_dataframe(self, *args, **kwargs)
    900 
    901     plot.__doc__ = plot_dataframe.__doc__

~/Dropbox/Python/geopandas/martinfleis/geopandas/plotting.py in plot_dataframe(df, column, cmap, color, ax, cax, categorical, legend, scheme, k, vmin, vmax, markersize, figsize, legend_kwds, categories, classification_kwds, missing_kwds, aspect, **style_kwds)
    672             classification_kwds["k"] = k
    673 
--> 674         binning = _mapclassify_choro(values[~nan_idx], scheme, **classification_kwds)
    675         # set categorical to True for creating the legend
    676         categorical = True

~/Dropbox/Python/geopandas/martinfleis/geopandas/plotting.py in _mapclassify_choro(values, scheme, **classification_kwds)
    917             del classification_kwds["k"]
    918     try:
--> 919         binning = scheme_class(values, **classification_kwds)
    920     except TypeError:
    921         raise TypeError("Invalid keyword argument for %r " % scheme)

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/mapclassify/classifiers.py in __init__(self, y, k, pct)
   2037         self.name = "JenksCaspallSampled"
   2038         self.y = y
-> 2039         self._summary()  # have to recalculate summary stats
   2040 
   2041     def _set_bins(self):

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/mapclassify/classifiers.py in _summary(self)
    625     def _summary(self):
    626         yb = self.yb
--> 627         self.classes = [np.nonzero(yb == c)[0].tolist() for c in range(self.k)]
    628         self.tss = self.get_tss()
    629         self.adcm = self.get_adcm()

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/mapclassify/classifiers.py in <listcomp>(.0)
    625     def _summary(self):
    626         yb = self.yb
--> 627         self.classes = [np.nonzero(yb == c)[0].tolist() for c in range(self.k)]
    628         self.tss = self.get_tss()
    629         self.adcm = self.get_adcm()

<__array_function__ internals> in nonzero(*args, **kwargs)

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/numpy/core/fromnumeric.py in nonzero(a)
   1894 
   1895     """
-> 1896     return _wrapfunc(a, 'nonzero')
   1897 
   1898 

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds)
     56     bound = getattr(obj, method, None)
     57     if bound is None:
---> 58         return _wrapit(obj, method, *args, **kwds)
     59 
     60     try:

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/numpy/core/fromnumeric.py in _wrapit(obj, method, *args, **kwds)
     49         if not isinstance(result, mu.ndarray):
     50             result = asarray(result)
---> 51         result = wrap(result)
     52     return result
     53 

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/pandas/core/generic.py in __array_wrap__(self, result, context)
   1916             return result
   1917         d = self._construct_axes_dict(self._AXIS_ORDERS, copy=False)
-> 1918         return self._constructor(result, **d).__finalize__(self)
   1919 
   1920     # ideally we would define this to avoid the getattr checks, but

/opt/miniconda3/envs/geo_dev/lib/python3.7/site-packages/pandas/core/series.py in __init__(self, data, index, dtype, name, copy, fastpath)
    290                     if len(index) != len(data):
    291                         raise ValueError(
--> 292                             f"Length of passed values is {len(data)}, "
    293                             f"index implies {len(index)}."
    294                         )

ValueError: Length of passed values is 1, index implies 177.
```

Mapclassify expects numpy array here, which we no longer pass after https://github.com/geopandas/geopandas/commit/df6db270fa76641e41ae149535ddd0c70bafb978#diff-9f920647ae0bb977ab8d642b436d8eacL624.

I'll fix it by wrapping `values` here in `np.asarray`: https://github.com/geopandas/geopandas/blob/eaae17f7177620fea9cdc3467826ee30c3aa2f65/geopandas/plotting.py#L919


Discussion:

Resolving patch:
diff --git a/geopandas/plotting.py b/geopandas/plotting.py
--- a/geopandas/plotting.py
+++ b/geopandas/plotting.py
@@ -916,7 +916,7 @@ class identifier and the bins.
         if "k" not in spec.args:
             del classification_kwds["k"]
     try:
-        binning = scheme_class(values, **classification_kwds)
+        binning = scheme_class(np.asarray(values), **classification_kwds)
     except TypeError:
         raise TypeError("Invalid keyword argument for %r " % scheme)
     return binning


Test patch:
diff --git a/geopandas/tests/test_plotting.py b/geopandas/tests/test_plotting.py
--- a/geopandas/tests/test_plotting.py
+++ b/geopandas/tests/test_plotting.py
@@ -902,6 +902,8 @@ def setup_class(cls):
             import mapclassify  # noqa
         except ImportError:
             pytest.importorskip("mapclassify")
+        cls.classifiers = list(mapclassify.classifiers.CLASSIFIERS)
+        cls.classifiers.remove("UserDefined")
         pth = get_path("naturalearth_lowres")
         cls.df = read_file(pth)
         cls.df["NEGATIVES"] = np.linspace(-10, 10, len(cls.df.index))
@@ -970,6 +972,11 @@ def test_scheme_name_compat(self, scheme):
         ax = self.df.plot(column="NEGATIVES", scheme=scheme, k=3, legend=True)
         assert len(ax.get_legend().get_texts()) == 3
 
+    def test_schemes(self):
+        # test if all available classifiers pass
+        for scheme in self.classifiers:
+            self.df.plot(column="pop_est", scheme=scheme, legend=True)
+
     def test_classification_kwds(self):
         ax = self.df.plot(
             column="pop_est",