holoviz / geoviews

Simple, concise geographical visualization in Python
http://geoviews.org
BSD 3-Clause "New" or "Revised" License
595 stars 77 forks source link

geopandas import causes attribute error in Geometries.ipynb example #434

Closed karosc closed 4 years ago

karosc commented 4 years ago

I was playing around with the Geometries Exampe and found that if I import geopandas (version 0.7.0), I start getting an error when using the bokeh back end. The code cell and resulting error are below. To be clear, this error does not happen if I do not import geopandas. Only if I add import geopandas as geo to the top of the notebook does this happen.

I installed holoviz from conda with conda install -c pyviz holoviz and then geopandas from conda as well with conda install geopandas.


gv.output(backend='bokeh')

(gf.ocean * gf.land.options(scale='110m', global_extent=True) * gv.Feature(graticules, group='Lines') + 
 gf.ocean * gf.land.options(scale='50m', global_extent=True) * gv.Feature(graticules, group='Lines'))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~\Miniconda\envs\pyviz\lib\site-packages\IPython\core\formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\core\dimension.py in _repr_mimebundle_(self, include, exclude)
   1292         combined and returned.
   1293         """
-> 1294         return Store.render(self)
   1295 
   1296 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\core\options.py in render(cls, obj)
   1366         data, metadata = {}, {}
   1367         for hook in hooks:
-> 1368             ret = hook(obj)
   1369             if ret is None:
   1370                 continue

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\ipython\display_hooks.py in pprint_display(obj)
    279     if not ip.display_formatter.formatters['text/plain'].pprint:
    280         return None
--> 281     return display(obj, raw_output=True)
    282 
    283 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\ipython\display_hooks.py in display(obj, raw_output, **kwargs)
    252     elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    253         with option_state(obj):
--> 254             output = layout_display(obj)
    255     elif isinstance(obj, (HoloMap, DynamicMap)):
    256         with option_state(obj):

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\ipython\display_hooks.py in wrapped(element)
    144         try:
    145             max_frames = OutputSettings.options['max_frames']
--> 146             mimebundle = fn(element, max_frames=max_frames)
    147             if mimebundle is None:
    148                 return {}, {}

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\ipython\display_hooks.py in layout_display(layout, max_frames)
    217         return None
    218 
--> 219     return render(layout)
    220 
    221 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\ipython\display_hooks.py in render(obj, **kwargs)
     66         renderer = renderer.instance(fig='png')
     67 
---> 68     return renderer.components(obj, **kwargs)
     69 
     70 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\renderer.py in components(self, obj, fmt, comm, **kwargs)
    248         # Bokeh has to handle comms directly in <0.12.15
    249         comm = False if bokeh_version < '0.12.15' else comm
--> 250         return super(BokehRenderer, self).components(obj,fmt, comm, **kwargs)
    251 
    252 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\renderer.py in components(self, obj, fmt, comm, **kwargs)
    319             plot = obj
    320         else:
--> 321             plot, fmt = self._validate(obj, fmt)
    322 
    323         data, metadata = {}, {}

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\renderer.py in _validate(self, obj, fmt, **kwargs)
    218         if isinstance(obj, tuple(self.widgets.values())):
    219             return obj, 'html'
--> 220         plot = self.get_plot(obj, renderer=self, **kwargs)
    221 
    222         fig_formats = self.mode_formats['fig'][self.mode]

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
    133             curdoc().theme = self_or_cls.theme
    134         doc.theme = self_or_cls.theme
--> 135         plot = super(BokehRenderer, self_or_cls).get_plot(obj, renderer, **kwargs)
    136         plot.document = doc
    137         return plot

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\renderer.py in get_plot(self_or_cls, obj, renderer, **kwargs)
    205             init_key = tuple(v if d is None else d for v, d in
    206                              zip(plot.keys[0], defaults))
--> 207             plot.update(init_key)
    208         else:
    209             plot = obj

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\plot.py in update(self, key)
    612     def update(self, key):
    613         if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn:
--> 614             return self.initialize_plot()
    615         item = self.__getitem__(key)
    616         self.traverse(lambda x: setattr(x, '_updated', True))

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\plot.py in initialize_plot(self, plots, ranges)
   1008 
   1009                 shared_plots = list(passed_plots) if self.shared_axes else None
-> 1010                 subplots = subplot.initialize_plot(ranges=ranges, plots=shared_plots)
   1011                 nsubplots = len(subplots)
   1012 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\plot.py in initialize_plot(self, ranges, plots)
   1128             else:
   1129                 passed_plots = plots + adjoined_plots
-> 1130                 adjoined_plots.append(subplot.initialize_plot(ranges=ranges, plots=passed_plots))
   1131         self.drawn = True
   1132         if not adjoined_plots: adjoined_plots = [None]

~\Miniconda\envs\pyviz\lib\site-packages\geoviews\plotting\bokeh\plot.py in initialize_plot(self, ranges, plot, plots, source)
    110     def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
    111         opts = {} if isinstance(self, HvOverlayPlot) else {'source': source}
--> 112         fig = super(GeoPlot, self).initialize_plot(ranges, plot, plots, **opts)
    113         if self.geographic and self.show_bounds and not self.overlaid:
    114             from . import GeoShapePlot

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\element.py in initialize_plot(self, ranges, plot, plots)
   2110             if self.tabs:
   2111                 subplot.overlaid = False
-> 2112             child = subplot.initialize_plot(ranges, plot, plots)
   2113             if isinstance(element, CompositeOverlay):
   2114                 # Ensure that all subplots are in the same state

~\Miniconda\envs\pyviz\lib\site-packages\geoviews\plotting\bokeh\plot.py in initialize_plot(self, ranges, plot, plots, source)
    110     def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
    111         opts = {} if isinstance(self, HvOverlayPlot) else {'source': source}
--> 112         fig = super(GeoPlot, self).initialize_plot(ranges, plot, plots, **opts)
    113         if self.geographic and self.show_bounds and not self.overlaid:
    114             from . import GeoShapePlot

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\element.py in initialize_plot(self, ranges, plot, plots, source)
   1278         self.handles['plot'] = plot
   1279 
-> 1280         self._init_glyphs(plot, element, ranges, source)
   1281         if not self.overlaid:
   1282             self._update_plot(key, plot, style_element)

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\element.py in _init_glyphs(self, plot, element, ranges, source)
   1225         else:
   1226             style = self.style[self.cyclic_index]
-> 1227             data, mapping, style = self.get_data(element, ranges, style)
   1228             current_id = element._plot_id
   1229 

~\Miniconda\envs\pyviz\lib\site-packages\geoviews\plotting\bokeh\__init__.py in get_data(self, element, ranges, style)
    217             el_type = Polygons
    218         polys = el_type(geoms, crs=element.crs, **util.get_param_values(element))
--> 219         return super(FeaturePlot, self).get_data(polys, ranges, style)
    220 
    221 

~\Miniconda\envs\pyviz\lib\site-packages\geoviews\plotting\bokeh\plot.py in get_data(self, element, ranges, style)
    163         if self._project_operation and self.geographic:
    164             element = self._project_operation(element, projection=self.projection)
--> 165         return super(GeoPlot, self).get_data(element, ranges, style)
    166 
    167 

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\path.py in get_data(self, element, ranges, style)
    208         else:
    209             if has_holes and bokeh_version >= '1.0':
--> 210                 xs, ys = multi_polygons_data(element)
    211             else:
    212                 paths = element.split(datatype='array', dimensions=element.kdims)

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\plotting\bokeh\util.py in multi_polygons_data(element)
    864     correct list of holes is assigned to each sub-polygon.
    865     """
--> 866     paths = element.split(datatype='array', dimensions=element.kdims)
    867     xs, ys = ([path[:, idx] for path in paths] for idx in (0, 1))
    868     holes = element.holes()

~\Miniconda\envs\pyviz\lib\site-packages\holoviews\element\path.py in split(self, start, end, datatype, **kwargs)
    136                 raise ValueError("%s datatype not support" % datatype)
    137             return [obj]
--> 138         return self.interface.split(self, start, end, datatype, **kwargs)
    139 
    140     # Deprecated methods

~\Miniconda\envs\pyviz\lib\site-packages\geoviews\data\geopandas.py in split(cls, dataset, start, end, datatype, **kwargs)
    339         if not len(dataset.data):
    340             return []
--> 341         row = dataset.data.iloc[0]
    342         col = cls.geo_column(dataset.data)
    343         arr = geom_to_array(row[col])

~\Miniconda\envs\pyviz\lib\site-packages\pandas\core\indexing.py in __getitem__(self, key)
   1498 
   1499             maybe_callable = com.apply_if_callable(key, self.obj)
-> 1500             return self._getitem_axis(maybe_callable, axis=axis)
   1501 
   1502     def _is_scalar_access(self, key):

~\Miniconda\envs\pyviz\lib\site-packages\pandas\core\indexing.py in _getitem_axis(self, key, axis)
   2230             self._validate_integer(key, axis)
   2231 
-> 2232             return self._get_loc(key, axis=axis)
   2233 
   2234     def _convert_to_indexer(self, obj, axis=None, is_setter=False):

~\Miniconda\envs\pyviz\lib\site-packages\pandas\core\indexing.py in _get_loc(self, key, axis)
    144         if axis is None:
    145             axis = self.axis
--> 146         return self.obj._ixs(key, axis=axis)
    147 
    148     def _slice(self, obj, axis=None, kind=None):

~\Miniconda\envs\pyviz\lib\site-packages\pandas\core\frame.py in _ixs(self, i, axis)
   2859                                                       index=self.columns,
   2860                                                       name=self.index[i],
-> 2861                                                       dtype=new_values.dtype)
   2862                 result._set_is_copy(self, copy=copy)
   2863                 return result

AttributeError: 'MultiPolygon' object has no attribute 'dtype'
philippjfr commented 4 years ago

Thanks for the bug report. Please also report the HoloViews and GeoViews versions you are using.

karosc commented 4 years ago

as reported by the __version__ property of each module....

GeoViews 1.6.6 HoloViews 1.12.7 bokeh 1.4.0 pandas 0.24.2

karosc commented 4 years ago

After some investigation, I have found this may be a problem with geopandas. The issue comes when trying to use iloc with a GeoDataFrame that only has a single column for geometry. I was able to reproduce the error with the following code:


import geopandas as gpd
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
gdf=gpd.GeoDataFrame(world.geometry)
gdf.iloc[0]
jbednar commented 4 years ago

I was able to reproduce this problem using geopandas 0.7 and pandas 0.24.2, but it goes away with pandas 0.25.3 or 1.0.1. So it sounds like there should be an issue filed on geopandas about appearing to require pandas 0.25 or later. Meanwhile, I'll close this issue as not being something geoviews can address.