holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.66k stars 396 forks source link

AttributeError: 'Tiles' object has no attribute '_transforms' #4645

Open afonit opened 3 years ago

afonit commented 3 years ago

Recently when running a notebook, we have started to get an error. When I use the full production example, the issue seems to be when using link_selection

I was trying to get the error to reproduce with a very small fake dataset and was able to finally do so with:

import pandas as pd
import datashader as ds
import dask.dataframe as dd
import hvplot.dask
from holoviews.element.tiles import OSM

from holoviews.selection import link_selections
from holoviews.operation.datashader import datashade
import holoviews.operation.datashader as hd
import holoviews as hv
from bokeh.models import NumeralTickFormatter
import panel as pn

some_data = {'x':
             ['26.1440180000', '26.1440280000', '26.1441580000'],
             'y':
             ['-80.1722130000', '-80.1723130000', '-80.1748130000'],
            'typeofint': [0, 0, 1],
            'typeof': ['a', 'a', 'b']
            }
ddf_done = pd.DataFrame(some_data)

def get_labels(hist):
    xs, ys = hist.dimension_values(0), hist.dimension_values(1)
    return hv.Labels((xs, ys+0.05*ys.max(), ys),
                    kdims=hist.dimensions(), dataset=hist.dataset)

points = hv.Scatter(ddf_done, ['x', 'y'], ['typeofint', 'typeof'])
datashaded = hd.datashade(points, aggregator=ds.count_cat('typeof')).opts(width=800, height=800)
main_plot = OSM()*datashaded
dim_expr = ((0.1+hv.dim('typeofint')/2).round()).categorize(hv.Cycle('Set1').values)
histogram = points.hist(num_bins=2, adjoin=False, normed=False).opts(color=dim_expr, yformatter=NumeralTickFormatter(format='0,0'), ylim=(-1000000, 8000000))#, alpha=.5)#, tools=['hover'], logy=True)
labels = histogram.apply(get_labels)

pn.ipywidget(link_selections(main_plot + histogram * labels.opts(text_font_size='10pt', width=400)))

That will produce this error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-30-5f5b257b6e07> in <module>
----> 1 pn.ipywidget(link_selections(main_plot + histogram * labels.opts(text_font_size='10pt', width=400)))
      2 #pn.ipywidget(main_plot + histogram * labels.opts(text_font_size='10pt', width=400))

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/param/parameterized.py in __new__(class_, *args, **params)
   2810         inst = class_.instance()
   2811         inst.param._set_name(class_.__name__)
-> 2812         return inst.__call__(*args,**params)
   2813 
   2814     def __call__(self,*args,**kw):

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in __call__(self, hvobj, **kwargs)
    115 
    116         # Perform transform
--> 117         return self._selection_transform(hvobj.clone())
    118 
    119     def _selection_transform(self, hvobj, operations=()):

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in _selection_transform(self, hvobj, operations)
    154         elif isinstance(hvobj, (Layout, Overlay, NdOverlay, GridSpace)):
    155             data = OrderedDict([(k, self._selection_transform(v, operations))
--> 156                                  for k, v in hvobj.items()])
    157             if isinstance(hvobj, NdOverlay):
    158                 def compose(*args, **kwargs):

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in <listcomp>(.0)
    154         elif isinstance(hvobj, (Layout, Overlay, NdOverlay, GridSpace)):
    155             data = OrderedDict([(k, self._selection_transform(v, operations))
--> 156                                  for k, v in hvobj.items()])
    157             if isinstance(hvobj, NdOverlay):
    158                 def compose(*args, **kwargs):

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in _selection_transform(self, hvobj, operations)
    127             if len(callback.inputs) > 1:
    128                 return Overlay([
--> 129                     self._selection_transform(el) for el in callback.inputs
    130                 ]).collate()
    131 

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in <listcomp>(.0)
    127             if len(callback.inputs) > 1:
    128                 return Overlay([
--> 129                     self._selection_transform(el) for el in callback.inputs
    130                 ]).collate()
    131 

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in _selection_transform(self, hvobj, operations)
    146             if getattr(chart, 'selection_display', None):
    147                 element = hvobj.clone(link=self.link_inputs)
--> 148                 self._register(element)
    149                 return chart.selection_display(element).build_selection(
    150                     self._selection_streams, element, operations,

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/selection.py in _register(self, hvobj)
     87 
     88         # Create SelectionExpr stream
---> 89         expr_stream = SelectionExpr(source=hvobj, index_cols=self.index_cols)
     90         expr_stream.add_subscriber(
     91             lambda **kwargs: self._expr_stream_updated(hvobj, **kwargs)

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/streams.py in __init__(self, source, **params)
    925         input_streams = self._build_selection_streams(source)
    926         super(SelectionExpr, self).__init__(
--> 927             source=source, input_streams=input_streams, exclusive=True, **params
    928         )
    929 

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/streams.py in __init__(self, input_streams, exclusive, **params)
    796         self.exclusive = exclusive
    797         self._register_input_streams()
--> 798         self.update()
    799 
    800     def _register_input_streams(self):

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/streams.py in update(self, **kwargs)
    422         """
    423         self._set_stream_parameters(**kwargs)
--> 424         transformed = self.transform()
    425         if transformed:
    426             self._set_stream_parameters(**transformed)

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/streams.py in transform(self)
    835     def transform(self):
    836         stream_values = [s.contents for s in self.input_streams]
--> 837         return self.transform_function(stream_values, self.constants)
    838 
    839     @classmethod

~/anaconda3/envs/data_analysis_lab/lib/python3.7/site-packages/holoviews/streams.py in transform_function(cls, stream_values, constants)
    969                 break
    970 
--> 971         for expr_transform in element._transforms[::-1]:
    972             if selection_expr is not None:
    973                 selection_expr = expr_transform(selection_expr)

AttributeError: 'Tiles' object has no attribute '_transforms'

When playing with the full example, I can plot all the components separately, but when adding in that link_selection it throws that error.

I am not aware of any software components we have upgraded but here are the versions: package version
panel 0.9.7
bokeh 2.1.1
datashader 0.10.0
holoviews 1.13.4
philippjfr commented 3 years ago

Agree that this should work, for the time being you should be able to work around this by applying the linked selections to each component individually and only composing them after the fact.

afonit commented 3 years ago

@philippjfr thanks, again, not sure what changed in the libraries to make this behavior not work now.

Reading through the documentation - I am not seeing any examples of applying the linked selections to each component separately. I tried to instantiate a selector independent of anything then attach it to the objects - but I did not see any methods that would be useful in that.

Do you know of any examples that expound upon that?

philippjfr commented 3 years ago

Do you know of any examples that expound upon that?

You can use it like this:

ls = hv.link_selections.instance()

tiles * ls(el1) + ls(el2)
banesullivan-kobold commented 5 months ago

My colleague and I ran into a similar issue with geoviews' WMTS:

AttributeError: 'WMTS' object has no attribute '_transforms'

We found that simply patching on an empty _transforms fixed it for our use case:

from geoviews.element.geo import WMTS
# HACK: this is necessary to get around the fact that geoviews' WMTS class does not have `_transforms` and therefore
# when you try to link against anything containing basemaps you will get an error
WMTS._transforms = []