Giter Site home page Giter Site logo

ioam / parambokeh Goto Github PK

View Code? Open in Web Editor NEW
22.0 22.0 7.0 595 KB

Generate widgets from Parameterized objects in Jupyter or Bokeh server

Home Page: https://ioam.github.com/parambokeh

License: BSD 3-Clause "New" or "Revised" License

Shell 1.26% Python 98.74%

parambokeh's People

Contributors

ceball avatar jbampton avatar jbednar avatar jlstevens avatar jsignell avatar kayibal avatar philippjfr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

parambokeh's Issues

Move JSONInit class to param?

In PR #52, the JSONInit class is added to parambokeh. Instead of duplicating this class across paramnb and parambokeh, it might be worth moving this class and the corresponding documentation somewhere else (probably to param?).

Widgets very slow to appear in parambokeh web site

When first visiting the ParamBokeh web site, it looks broken because none of the widgets show up:

image

However, if I revisit that page in another tab, it now works:

image

And if I go back to the original tab after many minutes (more than two, less than 45) the widgets show up there too. The first time that happened, I did shift reload, in which case this cycle started again, and the second time the widgets were there after shift reload right away, and then the third time there was a pause again.

????

Visualization format of param.Date

I have a parambokeh widget implemntation of param.Date.

Is it possible to display the time (HH:MM:SS) of the datetime object in addition to the Month DD, YYYY?
Is there a way to change that format?
The widget takes user input from a popup calendar. Is there a way for widget input from the user to include the time?

import fails with bokeh 0.12.9

Is this indicative of any general problem with compatibility with bokeh 0.12.9 or is it just an outdated import?

In [1]: import parambokeh
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-a6ae99d594b0> in <module>()
----> 1 import parambokeh

~/anaconda/envs/hv/lib/python3.6/site-packages/parambokeh/__init__.py in <module>()
     14 from bokeh.models import CustomJS
     15
---> 16 from .comms import JupyterCommJS, JS_CALLBACK, notebook_show
     17 from .widgets import wtype, literal_params
     18 from .util import named_objs, get_method_owner

~/anaconda/envs/hv/lib/python3.6/site-packages/parambokeh/comms.py in <module>()
      6 from bokeh.embed import notebook_div
      7 from bokeh.io import _CommsHandle
----> 8 from bokeh.util.notebook import publish_display_data, get_comms
      9
     10 try:

ImportError: cannot import name 'publish_display_data'

Target bokeh 0.12.10

Significant API changes in bokeh should reduce from 0.12.10 onwards, so I think it makes sense to make the next release of parambokeh target 0.12.10 and drop support for previous bokeh versions.

Below are things that don't seem to work (please add to this list!). Note: these things have not necessarily been working in previous versions, but it seems worthwhile fixing them all in one go for bokeh 0.12.10.

  • anything at all in the notebook
  • float sliders seem to have an integer step
  • param.Action -> javascript error
  • unbounded numbers -> javascript error (e.g. param.Number(default=0)
  • run/next_n (does nothing in notebook at least)
  • constant parameters show up with bold label and regular value (the opposite is true for non-constant parameters)

Ongoing work to fix these is in #17.

Example in View Parameters notebook not updating

Using holoviews 1.10, I get the following traceback in the web console (View_Parameters.ipynb notebook) when trying to switch to Scatter in the last example:

VM337:45 Python callback returned following output: 
        Traceback (most recent call last):
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/plotting/util.py", line 260, in get_plot_frame
            return map_obj[key]
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/core/spaces.py", line 1128, in __getitem__
            self._cache(tuple_key, val)
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/core/spaces.py", line 1175, in _cache
            self[key] = val
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/core/ndmapping.py", line 523, in __setitem__
            self._add_item(key, value, update=False)
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/core/ndmapping.py", line 160, in _add_item
            self._item_check(dim_vals, data)
          File "/Users/jstevens/Desktop/development/holoviews/holoviews/core/ndmapping.py", line 824, in _item_check
            (self.__class__.__name__, type(data).__name__, self.type.__name__))
        AssertionError: DynamicMap must only contain one type of object, not both Scatter and Curve.

Actually, none of the widgets update that example right now, even just changing frequency and amplitude doesn't work. Eg. changing frequency gives this traceback in the web console:

Python failed with the following traceback: /Users/jstevens/miniconda3/envs/analytics/lib/python3.6/site-packages/traitlets/traitlets.py validate L2054
	TraitError: The 'target_name' trait of a Comm instance must be a unicode string, but a value of CurvePlot(apply_extents=True, apply_ranges=True, bgcolor=None, border=10, default_tools=['save', 'pan', 'wheel_zoom', 'box_zoom', 'reset'], finalize_hooks=[], fontsize={'title': '12pt'}, gridstyle={}, height=300, interpolation='linear', invert_axes=False, invert_xaxis=False, invert_yaxis=False, labelled=['x', 'y'], lod={'factor': 10, 'interval': 300, 'threshold': 2000, 'timeout': 500}, logx=False, logy=False, name='CurvePlot01083', normalize=True, projection=None, shared_axes=True, shared_datasource=True, show_frame=True, show_grid=False, show_legend=True, show_title=True, sizing_mode='fixed', title_format='{label} {group} {dimensions}', toolbar='right', tools=[], width=300, xaxis='bottom', xrotation=None, xticks=None, yaxis='left', yrotation=None, yticks=None) <class 'holoviews.plotting.bokeh.chart.CurvePlot'> was specified.

Add some tests of the widgets

Right now, we check the notebooks run. However, that checks little about whether or not parambokeh works.

We could add some unit tests with pytest.

We could add some more advanced notebook tests.

We could at least check that bokeh models are created as expected.

Differing behaviors with sliders for Number parameters between parambokeh and paramnb

I'm doing the tutorial at pyviz on the notebook 12_Parameters_and_Widgets and noticed the following, based on this experiment:

import holoviews as hv
import param
import paramnb
import parambokeh

class NumOptions(hv.streams.Stream):
    intdef = param.Number(default=1, bounds=(0,10))
    fourdig = param.Number(default=1.1234, bounds=(0,10))
    
bokopts = NumOptions(name='parambokeh')
nbopts = NumOptions(name='paramnb')
parambokeh.Widgets(bokopts),  paramnb.Widgets(nbopts)

What happens here is that for parambokeh, the precision of the slider is dependent on the number of significant digits of the initial value. So the slider for intdef has integer values, and the slider for fourdig has values to 4 places after the decimal point.

In the case of paramnb, the precision is 2 digits, independent of the initial value.

I'm not sure what the actual behavior should be, but the inconsistent behavior can be confusing.

Maybe, for both cases, there should be a way to specify the precision of the slider as it is moved.

IPython is not available, cannot use Widgets in notebook mode.

I get this error when trying to use parambokeh in a Jupyter notebook. Example:

import numpy as np
import pandas as pd

from bokeh.models import HoverTool
import holoviews as hv
hv.extension("bokeh")
renderer = hv.renderer("bokeh")
# Remove Bokeh logo from plots (https://stackoverflow.com/questions/47585887/remove-bokeh-logo-in-holoviews)
def disable_logo(plot, element):
    plot.state.toolbar.logo = None
hv.plotting.bokeh.ElementPlot.finalize_hooks.append(disable_logo)
import param, parambokeh

df = pd.DataFrame({"pax": ["a", "b", "c"], "val": [3, 1, 2], "day": ["Wednesday", "Thursday", "Friday"]})

class Test(hv.streams.Stream):
    pax = param.ObjectSelector(default="ALL", objects=["ALL"] + np.unique(df["pax"]).tolist())

    def make_view(self, **kwargs):
        if self.pax != "ALL":
            df_tmp = df[df["pax"] == self.pax]
        else:
            df_tmp = df
        return hv.Bars(df_tmp, kdims=["day"], vdims=["val"])

explorer = Test(name="Test")
parambokeh.Widgets(explorer, callback=explorer.event)
hv.DynamicMap(explorer.make_view, streams=[explorer])

Error message:

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-60-df1791955b28> in <module>()
     25 
     26 explorer = Test(name="Test")
---> 27 parambokeh.Widgets(explorer, callback=explorer.event)
     28 hv.DynamicMap(explorer.make_view, streams=[explorer])

~\AppData\Local\Continuum\anaconda3\lib\site-packages\param\parameterized.py in __new__(class_, *args, **params)
   1886         inst = class_.instance()
   1887         inst._set_name(class_.__name__)
-> 1888         return inst.__call__(*args,**params)
   1889 
   1890     def __call__(self,*args,**kw):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\parambokeh\__init__.py in __call__(self, parameterized, doc, plots, **params)
    108         if self.p.mode == 'notebook':
    109             if not IPYTHON_AVAILABLE:
--> 110                 raise ImportError('IPython is not available, cannot use '
    111                                   'Widgets in notebook mode.')
    112             self.comm = JupyterCommJS(on_msg=self.on_msg)

ImportError: IPython is not available, cannot use Widgets in notebook mode.

Provide `interact()` API?

Param and ParamBokeh were designed to support large, complex systems, with hierarchically organized options mirroring a class hierarchy that allows a clean separation between parameters for objects and any widgets that might be appropriate to display when those objects are used in a GUI context. Thus parameters are all currently declared as class attributes of Parameter type, independently of any package like ParamBokeh that will create GUI widgets from those definitions. For complex systems, separating the parameter declaration from the widget instantiation provides major benefits to the code even apart from any widgets, such as type and range checking that helps keep interactions between components manageable.

However, there are other contexts where it would be useful to have Bokeh-based widgets that don't involve complex hierarchical systems, and the class Parameter approach is not necessarily the best approach in those cases. For instance, when using a Jupyter notebook as a command prompt, it's often useful to create a widget explicitly for a particular local task, without designing a class hierarchy. The API from ipywidgets' interact() function is a good example of this alternative approach, which won't necessarily scale to larger projects, but can be very convenient in a single Jupyter cell or in a very basic dashboard.

Mirroring or nearly mirroring the interact() API specifically would also make it easier for people to switch between ipywidgets and param-based approaches, e.g. to evaluate Bokeh Server as a deployment mechanism. If we provide an interact() equivalent, we would need to be very clear in the documentation about its purpose, and to make its limitations clear.

User guide example error

From user guide example:
https://ioam.github.io/parambokeh/user_guide/View_Parameters.html

holoviews=1.10.5
parambokeh=0.2.2

/mnt/c/Users/sephi/GOOGLE~1/Bash/parambokeh/parambokeh/view.py in render_function(obj, view)
     16         plot = renderer.get_plot(obj, doc=view._document)
     17         if view._notebook:
---> 18             from holoviews.plotting.comms import JupyterComm
     19             comm = JupyterComm(plot, view._comm_target)
     20             plot.comm = comm


ModuleNotFoundError: No module named 'holoviews.plotting.comms'

I tried replacing that line with from pyviz_comms import JupyterComm but then I get a new error stemming from on_init=True input

     32 example = CurveExample(name='HoloViews Example')
     33 parambokeh.Widgets(example, callback=example.event, push=False,
---> 34                    on_init=True, view_position='right')

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in __new__(class_, *args, **params)
   1889         inst = class_.instance()
   1890         inst._set_name(class_.__name__)
-> 1891         return inst.__call__(*args,**params)
   1892 
   1893     def __call__(self,*args,**kw):

/mnt/c/Users/sephi/GOOGLE~1/Bash/parambokeh/parambokeh/__init__.py in __call__(self, parameterized, doc, plots, **params)
    187 
    188         if self.p.on_init:
--> 189             self.execute()
    190 
    191         if self.p.mode == 'raw':

/mnt/c/Users/sephi/GOOGLE~1/Bash/parambokeh/parambokeh/__init__.py in execute(self, changed)
    381         if self.p.callback is not None:
    382             if get_method_owner(self.p.callback) is self.parameterized:
--> 383                 self.p.callback(**changed)
    384             else:
    385                 self.p.callback(self.parameterized, **changed)

<ipython-input-8-65291cacb15b> in event(self, **kwargs)
     26     def event(self, **kwargs):
     27         if not self.output or any(k in kwargs for k in ['color', 'element']):
---> 28             self.output = hv.DynamicMap(self.view, streams=[self])
     29         else:
     30             super(CurveExample, self).event(**kwargs)

/mnt/c/Users/sephi/GOOGLE~1/Bash/parambokeh/parambokeh/view.py in __set__(self, obj, val)
     48         obj_id = id(obj)
     49         if obj_id in self.callbacks:
---> 50             self.callbacks[obj_id](self.renderer(val, self))
     51 
     52 

/mnt/c/Users/sephi/GOOGLE~1/Bash/parambokeh/parambokeh/view.py in render_function(obj, view)
     17         if view._notebook:
     18             from pyviz_comms import JupyterComm
---> 19             comm = JupyterComm(plot, view._comm_target)
     20             plot.comm = comm
     21         plot.document = view._document

~/anaconda3/lib/python3.6/site-packages/pyviz_comms/__init__.py in __init__(self, id, on_msg)
    193         self._on_msg = on_msg
    194         self._comm = None
--> 195         super(Comm, self).__init__(id = id if id else uuid.uuid4().hex)
    196 
    197 

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in __init__(self, **params)
   1039         self.__generate_name()
   1040 
-> 1041         self._setup_params(**params)
   1042         object_count += 1
   1043 

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in override_initialization(parameterized_instance, *args, **kw)
    976         original_initialized=parameterized_instance.initialized
    977         parameterized_instance.initialized=False
--> 978         fn(parameterized_instance,*args,**kw)
    979         parameterized_instance.initialized=original_initialized
    980     return override_initialization

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in _setup_params(self, **params)
   1439                 self.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters",name,val)
   1440             # i.e. if not desc it's setting an attribute in __dict__, not a Parameter
-> 1441             setattr(self,name,val)
   1442 
   1443 

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in __set__(self, obj, val)
    535 
    536     def __set__(self,obj,val):
--> 537         self._check_value(val)
    538         super(String,self).__set__(obj,val)
    539 

~/anaconda3/lib/python3.6/site-packages/param/parameterized.py in _check_value(self, val)
    532     def _check_value(self,val):
    533         if not isinstance(val, self.basestring) and not (self.allow_None and val is None):
--> 534             raise ValueError("String '%s' only takes a string value."%self._attrib_name)
    535 
    536     def __set__(self,obj,val):```

How to deploy the Django and parambokeh sample on a remote web server

How to run the Sample django + parambokeh apps on a web server other than on a local machine?
The web server is an Apache http server running on an EC2 single instance, listening on port 80.
I tried many different host addresses and ports configurations but I don't really understand how things work.
I believe that the following bk_worker code starts the the bokeh server in the background when a new thread is started:

def bk_worker():

Note: num_procs must be 1; see e.g. flask_gunicorn_embed.py for num_procs>1

server = Server({'/bk_sliders_app': bk_sliders.app},
io_loop=IOLoop(),
address=bk_config.server['address'],
port=bk_config.server['port'],
allow_websocket_origin=["localhost:8000"])
server.start()
server.io_loop.start()

What should be the address and port specified there? It is currently set to localhost and 5006 because I believe that, since Server() is called on the web server, then it should spawn a bokeh server on the same local host, correct? If not, then what should be the address and port (I also tried 0.0.0.0 but it didn't work)?

I believe that the bokeh server has started because I see a thread running under the wsgi user and started by httpd when running ps -ael on the EC2 instance:
4 S 0 20701 3057 0 80 0 - 62977 - ? 00:00:00 httpd
0 S 0 20705 20701 0 80 0 - 6295 - ? 00:00:00 rotatelogs
5 S 497 20706 20701 0 80 0 - 247053 - ? 00:00:01 httpd

Is there a better way to check that the bokeh server is running correctly and is there a way to see a log from the bokeh server?

I slightly modified the base.html template to display the output of server_document() called from view.py:

<title>parambokeh in django: sliders</title>
{{server_script}}
{% block content %} {{server_script|safe}} {% endblock %}

The script returned when accessing the http://django2-env.xpy8pjmgjb.us-east-2.elasticbeanstalk.com/sliders/ url is

<script src="http://localhost:5006/bk_sliders_app/autoload.js?bokeh-autoload-element=1002&bokeh-app-path=/bk_sliders_app&bokeh-absolute-url=http://localhost:5006/bk_sliders_app" id="1002"></script>

Where does this script run? Does it run on the client browser or does it run on the web server? If it runs on the client, then the http://localhost:5006 src should be incorrect and should be django2-env.xpy8pjmgjb.us-east-2.elasticbeanstalk.com instead, correct? Then what about the port number? 5006 is not open to internet access, so should it be port 80 instead? In this case, do I need to setup the Apache http server as a proxy/reverse proxy? If yes, how do I do it?

Misleading error on missing Range bounds

Setting Range bounds=None (or not providing them) results in a misleading error:

float_range = param.Range(default=(0.0, 1.57), bounds=None)

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-8-ede5c729d901> in <module>()
      6     colormap = param.ObjectSelector(default='rainbow', objects=['rainbow', 'bgy', 'bgyw', 'blues', 'coolwarm', 'gray,'])
      7 opts = plot_options(name='')
----> 8 parambokeh.Widgets(opts)

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\param\parameterized.py in __new__(class_, *args, **params)
   2047         inst = class_.instance()
   2048         inst.param._set_name(class_.__name__)
-> 2049         return inst.__call__(*args,**params)
   2050 
   2051     def __call__(self,*args,**kw):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in __call__(self, parameterized, doc, plots, **params)
    232 
    233         # Initialize widgets and populate container
--> 234         widgets, views = self.widgets()
    235         plots = views + plots
    236         widget_box.children = widgets

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in widgets(self)
    495 
    496         if self.p.show_labels:
--> 497             widgets += [self.widget(pname) for pname in ordered_params]
    498         else:
    499             widgets += [self.widget(pname) for pname in ordered_params]

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in <listcomp>(.0)
    495 
    496         if self.p.show_labels:
--> 497             widgets += [self.widget(pname) for pname in ordered_params]
    498         else:
    499             widgets += [self.widget(pname) for pname in ordered_params]

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in widget(self, param_name)
    459         """Get widget for param_name"""
    460         if param_name not in self._widgets:
--> 461             self._widgets[param_name] = self._make_widget(param_name)
    462         return self._widgets[param_name]
    463 

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\__init__.py in _make_widget(self, p_name)
    403             kw['start'], kw['end'] = p_obj.get_soft_bounds()
    404 
--> 405         w = widget_class(**kw)
    406 
    407         if hasattr(p_obj, 'callbacks') and value is not None:

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\parambokeh\widgets.py in RangeWidget(*args, **kw)
     92     if isinstance(kw['start'], int) and isinstance(kw['end'], int):
     93         kw['step'] = 1
---> 94     return RangeSlider(*args, **kw)
     95 
     96 def PlotWidget(*args, **kw):

C:\ProgramData\Anaconda3\envs\earthsimAUG\lib\site-packages\bokeh\models\widgets\sliders.py in __init__(self, **kwargs)
     22         if 'start' in kwargs and 'end' in kwargs:
     23             if kwargs['start'] == kwargs['end']:
---> 24                 raise ValueError("Slider 'start' and 'end' cannot be equal.")
     25         super(Widget, self).__init__(**kwargs)
     26 

ValueError: Slider 'start' and 'end' cannot be equal.

example server usage?

Can you put up a minimal working example (e.g., in a doc notebook) of how to use parambokeh with the bokeh server? I've been trying it out in the notebook but I can't get it to work with a server.

IPYTHON_AVAILABLE evaluates to false because of failed import from bokeh

here: https://github.com/ioam/parambokeh/blob/master/parambokeh/__init__.py#L16

Looks like this is the exception being raised; doesn't seem to be the one intended to be caught. Seems to have popped up with bokeh 0.12.10.

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-14-119ad22f1a00> in <module>()
----> 1 from parambokeh.comms import JupyterCommJS, JS_CALLBACK, notebook_show

~/.local/lib/python3.6/site-packages/parambokeh-0.2.1-py3.6.egg/parambokeh/comms.py in <module>()
      4 import traceback
      5 
----> 6 from bokeh.embed import notebook_div
      7 
      8 from IPython.display import publish_display_data

ImportError: cannot import name 'notebook_div'

Having a param.String variable named `name` should error or at least warn

name = param.String(default='a name')

Here it's overlapping with CurveExample(name='HoloViews Example')

import param
import parambokeh
from bokeh.io import output_notebook
import numpy as np
import pandas as pd

output_notebook()
import holoviews as hv

class CurveExample(hv.streams.Stream):

    color = param.Color(default='#000000', precedence=0)

    element = param.ObjectSelector(default=hv.Curve,
                                   objects=[hv.Curve, hv.Scatter, hv.Area],
                                  precedence=0)

    amplitude = param.Number(default=2, bounds=(2, 5))
    
    frequency = param.Number(default=2, bounds=(1, 10))
    
    name = param.String(default='Hello')
    
    output = parambokeh.view.Plot()
    
    def view(self, *args, **kwargs):
        return self.element(self.amplitude*np.sin(np.linspace(0, np.pi*self.frequency)),
                        vdims=[hv.Dimension('y', range=(-5, 5))]).opts(style=dict(color=self.color)) * hv.Text(20, 3, self.name)
    
    def event(self, **kwargs):
        if not self.output or any(k in kwargs for k in ['color', 'element']):
            self.output = hv.DynamicMap(self.view, streams=[self], cache_size=0)
        else:
            super(CurveExample, self).event(**kwargs)

example = CurveExample(name='HoloViews Example')
parambokeh.Widgets(example, callback=example.event, on_init=True, view_position='right')

image

Best practice for getting boolean value

I have a param.Parameterized class with a bunch of Booleans. If the parambokeh.Widget has it toggled as True, I want to collect that value and process it. My problem is that what I get back from param is a param.Boolean object. In many other cases, python just understands it, but when I check that value against True its not understood. My current workaround is to use val.__get__(None,None) but surely there is a better approach?

Test snippet:

import param

class BaseClass(param.Parameterized):
    boolean1                 = param.Boolean(True, doc="A sample Boolean parameter")
    boolean2                 = param.Boolean(False, doc="A sample Boolean parameter")
    
    def widget_to_list(self):
        for name, val in self.params().items():
            if val.__get__(None,None) is True:
                print(val.__get__(None,None))
base = BaseClass()
base.widget_to_list()

Add documentation about how to install

pip install parambokeh or pip install parambokeh[examples] to be able to run the examples

conda install -c pyviz parambokeh or conda install -c pyviz parambokeh holoviews bokeh ... etc to be able to run the examples

Website improvements TODO

  • put full-sized logo with wordmark on homepage
  • add param(bokeh) style (currently just default nbsite)
  • make sure logo is transparent, scalable, etc. if appropriate for each version
  • add gallery
  • name user guide pages with numbers (but see holoviz-dev/nbsite#18)
  • deploy website for commits to master to an alternative address [testing: https://ioam-docs.github.io/parambokeh-master/)
  • deploy website for PRs to an alternative address
  • indicate/allow selection of version on website (depends on auto versioning)

Datashader output not updating

I'm having trouble making a holoviews/parambokeh widgety-thing work with a datashader plot as output:

import param
import parambokeh
from holoviews.operation.datashader import datashade, dynspread
import colorcet as cc

class WidgetTest(hv.streams.Stream):
    cmap = param.ObjectSelector(default='coolwarm',
                               objects=['coolwarm', 'fire', 'blue'])

    output = parambokeh.view.Plot()

    def __init__(self, pts, *args, **kwargs):
        super(WidgetTest, self).__init__(*args, **kwargs)
        self.pts = pts
    
    def event(self, **kwargs):
        super(WidgetTest, self).event
        self.output = datashade(self.pts, cmap=cc.palette[self.cmap])        

When I run the following, the plot doesn't update on changing the colormap with the widget. What am I missing?

import numpy as np
n = 100000
pts = hv.Points(np.random.multivariate_normal([0,0], [[1,0], [0,1]], size=n))
example = WidgetTest(pts)

parambokeh.Widgets(example, callback=example.event, push=False,
                   on_init=True, view_position='right')

DateRange slider widget

Wasn't sure whether this is an upstream (bokeh) issue or a param issue, so I decided to try and put it here first in parambokeh. I noticed there is a possibility of setting DateRange parameters in param since holoviz/param#168, so I thought about making a DateRange slider using param.DateRange, similar to the param.Range parameter. Inspiration taken from this jupyter notebook.

I create a class like so:

!pip install --upgrade https://github.com/ioam/param/archive/master.zip  #requires https://github.com/ioam/param/pull/168
class IceSatExplorer(hv.streams.Stream):
    colormap  = param.ObjectSelector(default=cm["isolum"], objects=cm.values())
    altitude  = param.Range(default=(minAlt, maxAlt), bounds=(minAlt, maxAlt))
    timerange = param.DateRange(default=(minTime, maxTime), bounds=(minTime, maxTime))
    
     def make_view(self, x_range=None, y_range=None, **kwargs):
         ...

And call it using:

explorer = IceSatExplorer()
parambokeh.Widgets(explorer, callback=explorer.event)
hv.DynamicMap(explorer.make_view, streams=[explorer, RangeXY()])

which results in an error complaining:

ValueError: expected an element of Tuple(Float, Float), got (Timestamp('2003-02-20 11:16:02.592235'), Timestamp('2003-02-23 03:41:56.198538'))

I've put together a Jupyter notebook (.ipynb) at this gist, but with the timerange = param.DateRange(...) line commented out so it works as so:

image

Below is the full error reported if I uncomment the timerange = param.DateRange(...). Final error points to this code block:

ValueError                                Traceback (most recent call last)
<ipython-input-8-77a7ec6347eb> in <module>()
      1 explorer = IceSatExplorer()
      2 #paramnb.Widgets(explorer, callback=explorer.event)
----> 3 parambokeh.Widgets(explorer, callback=explorer.event)
      4 hv.DynamicMap(explorer.make_view, streams=[explorer, RangeXY()])

~/miniconda3/lib/python3.6/site-packages/param/parameterized.py in __new__(class_, *args, **params)
   1889         inst = class_.instance()
   1890         inst._set_name(class_.__name__)
-> 1891         return inst.__call__(*args,**params)
   1892 
   1893     def __call__(self,*args,**kw):

~/miniconda3/lib/python3.6/site-packages/parambokeh/__init__.py in __call__(self, parameterized, doc, plots, **params)
    121         self.shown = False
    122 
--> 123         widgets, views = self.widgets()
    124         plots = views + plots
    125         container = widgetbox(widgets, width=self.width)

~/miniconda3/lib/python3.6/site-packages/parambokeh/__init__.py in widgets(self)
    346 
    347         if self.p.show_labels:
--> 348             widgets += [self.widget(pname) for pname in ordered_params]
    349         else:
    350             widgets += [self.widget(pname) for pname in ordered_params]

~/miniconda3/lib/python3.6/site-packages/parambokeh/__init__.py in <listcomp>(.0)
    346 
    347         if self.p.show_labels:
--> 348             widgets += [self.widget(pname) for pname in ordered_params]
    349         else:
    350             widgets += [self.widget(pname) for pname in ordered_params]

~/miniconda3/lib/python3.6/site-packages/parambokeh/__init__.py in widget(self, param_name)
    310         """Get widget for param_name"""
    311         if param_name not in self._widgets:
--> 312             self._widgets[param_name] = self._make_widget(param_name)
    313         return self._widgets[param_name]
    314 

~/miniconda3/lib/python3.6/site-packages/parambokeh/__init__.py in _make_widget(self, p_name)
    261             kw['start'], kw['end'] = p_obj.get_soft_bounds()
    262 
--> 263         w = widget_class(**kw)
    264 
    265         if hasattr(p_obj, 'callbacks') and value is not None:

~/miniconda3/lib/python3.6/site-packages/parambokeh/widgets.py in RangeWidget(*args, **kw)
     53     if isinstance(kw['start'], int) and isinstance(kw['end'], int):
     54         kw['step'] = 1
---> 55     return RangeSlider(*args, **kw)
     56 
     57 def PlotWidget(*args, **kw):

~/miniconda3/lib/python3.6/site-packages/bokeh/model.py in __init__(self, **kwargs)
    218         self._id = kwargs.pop("id", make_id())
    219         self._document = None
--> 220         super(Model, self).__init__(**kwargs)
    221         default_theme.apply_to_model(self)
    222 

~/miniconda3/lib/python3.6/site-packages/bokeh/core/has_props.py in __init__(self, **properties)
    234 
    235         for name, value in properties.items():
--> 236             setattr(self, name, value)
    237 
    238     def __setattr__(self, name, value):

~/miniconda3/lib/python3.6/site-packages/bokeh/core/has_props.py in __setattr__(self, name, value)
    262 
    263         if name in props or name in deprecated:
--> 264             super(HasProps, self).__setattr__(name, value)
    265         else:
    266             matches, text = difflib.get_close_matches(name.lower(), props), "similar"

~/miniconda3/lib/python3.6/site-packages/bokeh/core/property/descriptors.py in __set__(self, obj, value, setter)
    501             raise RuntimeError("%s.%s is a readonly property" % (obj.__class__.__name__, self.name))
    502 
--> 503         self._internal_set(obj, value, setter=setter)
    504 
    505     def __delete__(self, obj):

~/miniconda3/lib/python3.6/site-packages/bokeh/core/property/descriptors.py in _internal_set(self, obj, value, hint, setter)
    722 
    723         '''
--> 724         value = self.property.prepare_value(obj, self.name, value)
    725 
    726         old = self.__get__(obj, obj.__class__)

~/miniconda3/lib/python3.6/site-packages/bokeh/core/property/bases.py in prepare_value(self, obj_or_cls, name, value)
    279                     break
    280             else:
--> 281                 raise e
    282         else:
    283             value = self.transform(value)

~/miniconda3/lib/python3.6/site-packages/bokeh/core/property/bases.py in prepare_value(self, obj_or_cls, name, value)
    272     def prepare_value(self, obj_or_cls, name, value):
    273         try:
--> 274             self.validate(value)
    275         except ValueError as e:
    276             for tp, converter in self.alternatives:

~/miniconda3/lib/python3.6/site-packages/bokeh/core/properties.py in validate(self, value)
   1386             if not (isinstance(value, (tuple, list)) and len(self.type_params) == len(value) and \
   1387                     all(type_param.is_valid(item) for type_param, item in zip(self.type_params, value))):
-> 1388                 raise ValueError("expected an element of %s, got %r" % (self, value))
   1389 
   1390     def _sphinx_type(self):

ValueError: expected an element of Tuple(Float, Float), got (Timestamp('2003-02-20 11:16:02.592235'), Timestamp('2003-02-23 03:41:56.198538'))

Recommended dashboarding approach

This issue summarizes a discussion I had with @jbednar, @philippjfr and @ceball about building dashboard with parambokeh and bokeh server.

We believe we can build dashboards in a fairly flexible and general way using a three level hierarchy:

  1. The dashboard object which represents one bokeh application (i.e a tab/webpage). This object has parameters that apply to the entire dashboard (displayed with parambokeh).
  2. This dashboard object builds bokeh layouts out of subobjects which also output bokeh layouts. These sub-objects are 'widgeted plots' which combine a set of widgets with a corresponding visualization. These objects have parameters of interest to the user, intended for display with parambokeh: the goal is to build a meaningful unit of functionality combining parambokeh widgets with the associated visualization. I'm not sure what we are calling these components yet.
  3. These components in turn own parameterized objects which generate the bokeh plots themselves (i.e via bokeh models or holoviews objects). These components may have many more parameters than are exposed at the dashboard level.

For step 2, you can either use mode='raw' and build a bokeh layout for yourself or you can supply a bokeh model in plots and a view_position. There are a few ways this could be improved:

  1. A whitelist of parameter names making it easier to order parameters and drop unwanted parameters from the widgets, instead of relying on precedences.
  2. A mechanism to partition the parameters and display them in groups in different locations. e.g you might want parameters 'A' and 'B' on the left, and parameter 'C' on the top of the plot.

I'm not sure how this will work when multiple bokeh models are supplied to plots and to be honest, I haven't checked how it works right now , maybe the widgets are placed in view_position relative to the first element in plots?

One other thing we discussed was the possibility of associating a visualization with corresponding parameters/widgets at the param level and not at the parambokeh level. The idea is that parambokeh would be able to generate a default visualization (with widgets) given a parameterized object and nothing else. You would then be able to customize the details of this visualization by supplying optional information to paramb.

We also discussed how holoviews widgets could fit into this system: perhaps the holoviews widgets would use bokeh widgets and for matplotlib support, we could simply output Div models in a bokeh layout where the Div holds the matplotlib output.

FileSelector requires at least one file matching the path glob to exist

import param, parambokeh

class Example(param.Parameterized):
    single_file = param.FileSelector(path='*.xyz',precedence=0.5)

parambokeh.Widgets(Example)
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-37-8b2a7dc851db> in <module>()
----> 1 parambokeh.Widgets(Example)

~/x/y/param/parameterized.py in __new__(class_, *args, **params)
   1886         inst = class_.instance()
   1887         inst._set_name(class_.__name__)
-> 1888         return inst.__call__(*args,**params)
   1889 
   1890     def __call__(self,*args,**kw):

~/x/y/parambokeh3/parambokeh/__init__.py in __call__(self, parameterized, doc, plots, **params)
    133         self.shown = False
    134 
--> 135         widgets, views = self.widgets()
    136         plots = views + plots
    137         container = widgetbox(widgets, width=self.p.width)

~/x/y/parambokeh3/parambokeh/__init__.py in widgets(self)
    372 
    373         if self.p.show_labels:
--> 374             widgets += [self.widget(pname) for pname in ordered_params]
    375         else:
    376             widgets += [self.widget(pname) for pname in ordered_params]

~/x/y/parambokeh3/parambokeh/__init__.py in <listcomp>(.0)
    372 
    373         if self.p.show_labels:
--> 374             widgets += [self.widget(pname) for pname in ordered_params]
    375         else:
    376             widgets += [self.widget(pname) for pname in ordered_params]

~/x/y/parambokeh3/parambokeh/__init__.py in widget(self, param_name)
    336         """Get widget for param_name"""
    337         if param_name not in self._widgets:
--> 338             self._widgets[param_name] = self._make_widget(param_name)
    339         return self._widgets[param_name]
    340 

~/x/y/parambokeh3/parambokeh/__init__.py in _make_widget(self, p_name)
    272                 kw['value'] = [lookup[v] for v in value]
    273             else:
--> 274                 kw['value'] = lookup[value]
    275             opt_lookup = {k: v for k, v in options}
    276             self._widget_options[p_name] = opt_lookup

Multi-level dynamic parameters?

I was trying to do this with DynamicMap, but kept getting errors when I tried instantiating a DynamicMap inside a DynamicMap callback. I have some data that looks (roughly) like:

{ project1: { step1: [1,2,3], step2: [10,11,12]},
  project2: {step2: [15,16,17], step3: [20,21,22]} }

Basically, the first pull-down should have Select between project1 and project2. If I select project1, then my step pull-down should be step1, step2. If I select project2, the step pull-down should be step2, step3.

Will parambokeh do this? I'm also not quite sure how to fit this into holoviews.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.