pythonpandasbokehscatter-plotscatter

custom function for scatterplot in bokeh not running


dataset i am working on: https://www.kaggle.com/code/gauravsahani/housing-in-london-for-beginners/data

i am using bokeh and have produced the following scatter plot

source=ColumnDataSource(data=dict(df,av=df.average_price,cr=df.no_of_crimes,ar=df.area))

p=figure(sizing_mode='stretch_width',toolbar_location=None,height=500,
        x_axis_label='Average Salary',y_axis_label='crime rate')
p.xaxis.formatter = BasicTickFormatter(use_scientific=False)

p.add_layout(Legend(), 'right')

p.scatter(x='av',y='cr',source=source,size=9,alpha=0.4,legend_field='area',fill_color=factor_cmap('area',palette=magma(34),factors=df.area.unique()))
p.xgrid.grid_line_color=None

p.legend.label_text_font_size='12px'
p.legend.padding=4
p.legend.orientation='vertical'
p.legend.spacing=-7

p.add_tools(HoverTool(tooltips=[('Area','@ar')]))

show(p)

I am trying to produce a custom function for reusability which is the following:

source=ColumnDataSource(data=dict(df,av=df.average_price,
                                  cr=df.no_of_crimes,
                                  ar=df.area))
tooltips=[('Area','@ar')]
          
def scatter(source,x,y,xlabel=None,ylabel=None,size=None,alpha=None,legend_field=None,fill_color=None):
    p=figure(sizing_mode='stretch_width',
                   toolbar_location=None,
                   height=500,
                   x_axis_label=xlabel,
                   y_axis_label=ylabel)
          
    p.xgrid.grid_line_color=None
    p.xaxis.formatter = BasicTickFormatter(use_scientific=False)
    p.legend.label_text_font_size='12px'
    p.legend.padding=4
    p.legend.orientation='vertical'
    p.legend.spacing=-7
          
    p.scatter(source=source,x=x,y=y,size=size,alpha=alpha,legend_field=legend_field,fill_color=fill_color)
          
    return p
          
p=scatter(source,x='av',y='cr')
          
show(p)

however I keep getting an error and I cant seem to figure out why since both my x and y columns are numerical values and already run successfuly when not working with a function. the error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [234], in <cell line: 24>()
     20     p.scatter(source=source,x=x,y=y,size=size,alpha=alpha,legend_field=legend_field,fill_color=fill_color)
     22     return p
---> 24 p=scatter(source,x='av',y='cr')
     26 show(p)

Input In [234], in scatter(source, x, y, xlabel, ylabel, size, alpha, legend_field, fill_color)
     17 p.legend.orientation='vertical'
     18 p.legend.spacing=-7
---> 20 p.scatter(source=source,x=x,y=y,size=size,alpha=alpha,legend_field=legend_field,fill_color=fill_color)
     22 return p

File ~\Anaconda3\lib\site-packages\bokeh\plotting\glyph_api.py:962, in GlyphAPI.scatter(self, *args, **kwargs)
    960     return self.circle(*args, **kwargs)
    961 else:
--> 962     return self._scatter(*args, marker=marker_type, **kwargs)

File ~\Anaconda3\lib\site-packages\bokeh\plotting\_decorators.py:86, in glyph_method.<locals>.decorator.<locals>.wrapped(self, *args, **kwargs)
     84 if self.coordinates is not None:
     85     kwargs.setdefault("coordinates", self.coordinates)
---> 86 return create_renderer(glyphclass, self.plot, **kwargs)

File ~\Anaconda3\lib\site-packages\bokeh\plotting\_renderer.py:116, in create_renderer(glyphclass, plot, **kwargs)
    113 # handle the mute glyph, we always set one
    114 muted_visuals = pop_visuals(glyphclass, kwargs, prefix='muted_', defaults=glyph_visuals, override_defaults={'alpha':0.2})
--> 116 glyph = make_glyph(glyphclass, kwargs, glyph_visuals)
    117 nonselection_glyph = make_glyph(glyphclass, kwargs, nonselection_visuals)
    118 selection_glyph = make_glyph(glyphclass, kwargs, selection_visuals)

File ~\Anaconda3\lib\site-packages\bokeh\plotting\_renderer.py:145, in make_glyph(glyphclass, kws, extra)
    143 kws = kws.copy()
    144 kws.update(extra)
--> 145 return glyphclass(**kws)

File ~\Anaconda3\lib\site-packages\bokeh\model\model.py:128, in Model.__init__(self, **kwargs)
    121 def __init__(self, **kwargs: Any) -> None:
    122 
    123     # "id" is popped from **kw in __new__, so in an ideal world I don't
    124     # think it should be here too. But Python has subtle behavior here, so
    125     # it is necessary
    126     kwargs.pop("id", None)
--> 128     super().__init__(**kwargs)
    129     default_theme.apply_to_model(self)

File ~\Anaconda3\lib\site-packages\bokeh\core\has_props.py:206, in HasProps.__init__(self, **properties)
    203 self._unstable_themed_values = {}
    205 for name, value in properties.items():
--> 206     setattr(self, name, value)
    208 self._initialized = True

File ~\Anaconda3\lib\site-packages\bokeh\core\has_props.py:230, in HasProps.__setattr__(self, name, value)
    228 properties = self.properties(_with_props=True)
    229 if name in properties:
--> 230     return super().__setattr__(name, value)
    232 descriptor = getattr(self.__class__, name, None)
    233 if isinstance(descriptor, property): # Python property

File ~\Anaconda3\lib\site-packages\bokeh\core\property\descriptors.py:283, in PropertyDescriptor.__set__(self, obj, value, setter)
    280     class_name = obj.__class__.__name__
    281     raise RuntimeError(f"{class_name}.{self.name} is a readonly property")
--> 283 value = self.property.prepare_value(obj, self.name, value)
    284 old = self._get(obj)
    285 self._set(obj, old, value, setter=setter)

File ~\Anaconda3\lib\site-packages\bokeh\core\property\dataspec.py:515, in SizeSpec.prepare_value(self, cls, name, value)
    513 except TypeError:
    514     pass
--> 515 return super().prepare_value(cls, name, value)

File ~\Anaconda3\lib\site-packages\bokeh\core\property\bases.py:365, in Property.prepare_value(self, owner, name, value, hint)
    363 else:
    364     obj_repr = owner if isinstance(owner, HasProps) else owner.__name__
--> 365     raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
    367 if isinstance(owner, HasProps):
    368     obj = owner

ValueError: failed to validate Scatter(id='40569', ...).size: expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Float)) or Float, got None

any help would be great


Solution

  • def scatter(source,x,y,xlabel=None,ylabel=None,size=5,alpha=1,legend_field=str(None),fill_color=None):
    
        p=figure(sizing_mode='stretch_width',
        toolbar_location=None,
        height=500,
        x_axis_label=xlabel,
        y_axis_label=ylabel)  
    
        p.xgrid.grid_line_color=None
        p.xaxis.formatter = BasicTickFormatter(use_scientific=False)
        p.legend.label_text_font_size='12px'
        p.legend.padding=4
        p.legend.orientation='vertical'
        p.legend.spacing=-7
    
        if legend_field == str(None):
            p.scatter(source=source,x=x,y=y,size=size,alpha=alpha,fill_color=fill_color)
        else:
            p.scatter(source=source,x=x,y=y,size=size,alpha=alpha,legend_field=legend_field,fill_color=fill_color)
    
        return p