示例#1
0
 def test_stream_good_data(self):
     ds = ColumnDataSource(data=dict(a=[10], b=[20]))
     ds._document = "doc"
     stuff = {}
     def mock(*args, **kw):
         stuff['args'] = args
         stuff['kw'] = kw
     ds.data._stream = mock
     ds.stream(dict(a=[11, 12], b=[21, 22]), "foo")
     self.assertEqual(stuff['args'], ("doc", ds, dict(a=[11, 12], b=[21, 22]), "foo"))
     self.assertEqual(stuff['kw'], {})
class BokehVisuals:
    def create_testing_charts(self):
        # create charts for testing headset
        self.figs = [
            figure(plot_width=200, plot_height=125, toolbar_location=None)
            for i in range(self.channels_number)
        ]
        for f in self.figs:
            f.extra_y_ranges = {"foo": Range1d(start=-100, end=100)}

        [
            f.add_layout(LinearAxis(y_range_name="foo"), "right")
            for f in self.figs
        ]

        self.cds = ColumnDataSource(
            data={
                i: []
                for i in [
                    item for sublist in [["y" + str(i), "f" + str(i)]
                                         for i in range(self.channels_number)]
                    for item in sublist
                ] + ["x"]
            })

        [(
            self.figs[i].line("x",
                              "y" + str(i),
                              source=self.cds,
                              line_alpha=0.3),
            self.figs[i].line(
                "x",
                "f" + str(i),
                source=self.cds,
                line_color="red",
                y_range_name="foo",
                line_width=1,
            ),
        ) for i in range(self.channels_number)]

        for f in self.figs:
            f.axis.visible = False
            f.xgrid.grid_line_color = None
            f.ygrid.grid_line_color = None

    def create_features_charts(self):
        # create charts for visualising features
        cds_feats_data = {"x": [], "y": []}
        for i in range(self.channels_number):
            cds_feats_data["value_" + str(i)] = []

        self.cds_feats = ColumnDataSource(data=cds_feats_data)

        colors = [
            "#75968f",
            "#a5bab7",
            "#c9d9d3",
            "#e2e2e2",
            "#dfccce",
            "#ddb7b1",
            "#cc7878",
            "#933b41",
            "#550b1d",
        ]
        mapper = LinearColorMapper(palette=colors, low=10, high=70)

        self.figs_feats = [
            figure(plot_width=400, plot_height=120, toolbar_location=None)
            for i in range(self.channels_number)
        ]

        for f in self.figs_feats:
            f.axis.visible = False
            f.xgrid.grid_line_color = None
            f.ygrid.grid_line_color = None

        [
            self.figs_feats[i].rect(
                x="x",
                y="y",
                width=1,
                height=1,
                source=self.cds_feats,
                line_color=None,
                fill_color={
                    "field": "value_" + str(i),
                    "transform": mapper
                },
            ) for i in range(self.channels_number)
        ]

    def create_forecast_status_charts(self):
        self.forecast_status_cds = ColumnDataSource(data={
            "x": [],
            "stage": [],
            "prediction": []
        })
        self.forecast_status_figure = figure(plot_width=400,
                                             plot_height=100,
                                             toolbar_location=None)
        self.forecast_status_figure.line(
            "x",
            "stage",
            source=self.forecast_status_cds,
            line_color="red",
            line_alpha=0.5,
        )
        self.forecast_status_figure.line("x",
                                         "prediction",
                                         source=self.forecast_status_cds,
                                         line_color="black")
        self.forecast_status_figure.axis.visible = False
        self.forecast_status_figure.xgrid.grid_line_color = None
        self.forecast_status_figure.ygrid.grid_line_color = None

        # figure for status
        self.status_text_cds = ColumnDataSource(data={
            "x": [0],
            "y": [0],
            "text": ["Calibration"]
        })
        self.status_text_figure = figure(plot_width=200,
                                         plot_height=100,
                                         toolbar_location=None)
        self.status_text_figure.axis.visible = False
        self.status_text_figure.xgrid.grid_line_color = None
        self.status_text_figure.ygrid.grid_line_color = None
        status_text = Text(x="x", y="y", text="text", text_color="black")
        self.status_text_figure.add_glyph(self.status_text_cds, status_text)

        self.update_button = Button(label="Start session")

    def __init__(self, helmet):

        self.channels_number = helmet.channels_number

        self.create_testing_charts()
        self.create_features_charts()
        self.create_forecast_status_charts()

        self.doc = curdoc()

        # todo add port selector for real data

    def button_link(self, button_f):
        # todo add stop button
        self.update_button.on_click(button_f)

    def add_layout(self):

        rows = []
        for i in range(int(self.channels_number) / 2):
            rows.append(
                row(self.figs[i * 2:i * 2 + 2] +
                    self.figs_feats[i * 2:i * 2 + 2]))
        # todo add option for uneven channel count

        rows.append(
            row(
                widgetbox([self.update_button], width=200),
                self.status_text_figure,
                self.forecast_status_figure,
            ))

        layout = column(*rows)
        self.doc.add_root(layout)

    def update_tuning(self, new_data, new_filtered_data, filtered_data):
        lsd = len(filtered_data)
        update_data = {"x": np.arange(lsd - len(new_data[:, 0]), lsd, 1)}

        for i in range(self.channels_number):
            update_data["f" + str(i)] = new_filtered_data[:, i]
            update_data["y" + str(i)] = new_data[:, i]

        self.cds.stream(update_data, 1000)

    def update_protocol(self, new_features_data, current_state,
                        current_prediction):
        update_data = {
            "x": new_features_data.index.get_level_values(0).values,
            "y": new_features_data.index.get_level_values(1).values,
        }

        for i in range(self.channels_number):
            update_data["value_" + str(i)] = new_features_data[i].values

        self.cds_feats.stream(
            update_data,
            len(new_features_data.columns) * self.channels_number * 100)

        if current_state[0] == "target":
            stage = 1
        else:
            stage = 0

        self.forecast_status_cds.stream(
            {
                "x": [int(time.time()) - 1],
                "stage": [stage],
                "prediction": [current_prediction],
            },
            1000,
        )

        if current_state[1] == "calibration":
            self.status_text_cds.stream(
                {
                    "x": [0],
                    "y": [0],
                    "text": ["calibration"]
                }, 1)
        else:
            self.status_text_cds.stream(
                {
                    "x": [0],
                    "y": [0],
                    "text": ["feedback"]
                }, 1)
示例#3
0
    def test_stream_bad_data(self):
        ds = ColumnDataSource(data=dict(a=[10], b=[20]))
        with self.assertRaises(ValueError) as cm:
            ds.stream(dict())
        self.assertEqual(str(cm.exception), "Must stream updates to all existing columns (missing: a, b)")
        with self.assertRaises(ValueError) as cm:
            ds.stream(dict(a=[10]))
        self.assertEqual(str(cm.exception), "Must stream updates to all existing columns (missing: b)")
        with self.assertRaises(ValueError) as cm:
            ds.stream(dict(a=[10], b=[10], x=[10]))
        self.assertEqual(str(cm.exception), "Must stream updates to all existing columns (extra: x)")
        with self.assertRaises(ValueError) as cm:
            ds.stream(dict(a=[10], x=[10]))
        self.assertEqual(str(cm.exception), "Must stream updates to all existing columns (missing: b, extra: x)")
        with self.assertRaises(ValueError) as cm:
            ds.stream(dict(a=[10], b=[10, 20]))
        self.assertEqual(str(cm.exception), "All streaming column updates must be the same length")

        with self.assertRaises(ValueError) as cm:
            ds.stream(dict(a=[10], b=np.ones((1,1))))
        self.assertTrue(
            str(cm.exception).startswith("stream(...) only supports 1d sequences, got ndarray with size (")
        )
示例#4
0
class Tabulator(Widget):
    """The Tabulator Pane wraps the [Tabulator](http://tabulator.info/) table to provide an
awesome interative table.

You can
- Specify a `configuration` dictionary at instantation. See http://tabulator.info/.
- Provide an initial `value` as a Pandas DataFrame or Bokeh ColumnDataSource.
- `stream` (append) to the `value`.
- `patch` (update) the `value`.

Example: Data specified in configuration

>>> from awesome_panel_extensions.widgets.tabulator import Tabulator
>>> configuration = {
...     "layout": "fitColumns",
...     "data": [
...         {"x": [1], "y": 'a'},
...         {"x": [2], "y": 'b'}
...         ],
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> Tabulator(configuration=configuration)
Tabulator(...)

Example: Data specified as Pandas.DataFrame value

>>> import pandas as pd
>>> configuration = {
...     "layout": "fitColumns",
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> value = pd.DataFrame([
...     {"x": [1], "y": 'a'},
...     {"x": [2], "y": 'b'}
... ])
>>> Tabulator(configuration=configuration, value=value)
Tabulator(...)

Example: Data specified as Bokeh ColumnDataSource value

>>> configuration = {
...     "layout": "fitColumns",
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> value = ColumnDataSource({"x": [1,2], "y": ["a", "b"]})
>>> Tabulator(configuration=configuration, value=value)
Tabulator(...)
"""

    value = param.Parameter(
        doc="""One of pandas.DataFrame or bokeh.models.ColumnDataSource.

        Please note when specifying a Pandas.Dataframe we currently have some narrow requirements

        - The index should be a single index which should be called 'index' and be unique. To make
        sure things work we suggest you `.reset_index()` before usage.
        """)
    selection = param.List(
        doc="The list of selected row indexes. For example [1,4]. Default is []"
    )
    configuration = param.Dict(
        constant=True,
        doc=
        """The initial Tabulator configuration. See https://tabulator.info for lots of
        examples.

        If None is provided at instantiation, then {'autocolumns': True} is added

        Please note that in order to get things working we add the below to the configuration
        on the js side.
        {
            "rowSelectionChanged": rowSelectionChanged,
            "cellEdited": cellEdited,
            "index": "index",
        }
        """,
    )

    height = param.Integer(
        default=300,
        bounds=(0, None),
        doc=
        """The height of the Tabulator table. Specifying a height is mandatory.""",
    )

    _source = param.ClassSelector(
        class_=ColumnDataSource,
        doc="Used to transfer the `value` efficiently to frontend")
    _cell_change = param.Dict(
        doc=
        """Changed whenever the user updates a cell in the client. Sends something like
        {"c": "<COLUMN NAME>", "i": "<INDEX>", "v": "<NEW VALUE>"}. Used to transfer the change
        efficiently and update the DataFrame as I could not find a similar property/ event on the
        ColumnDataSource""")

    _rename = {
        "value": None,
        "selection": None,
        "_source": "source",
    }
    _widget_type = _BkTabulator

    def __init__(self, **params):
        """The Tabulator Pane wraps the [Tabulator](http://tabulator.info/) table to provide an
        awesome interative table.

You can
- Specify a `configuration` dictionary at instantation. See http://tabulator.info/.
- Provide an initial `value` as a Pandas DataFrame or Bokeh ColumnDataSource.
- `stream` (append) to the `value`.
- `patch` (update) the `value`.

Example: Data specified in configuration

>>> from awesome_panel_extensions.widgets.tabulator import Tabulator
>>> configuration = {
...     "layout": "fitColumns",
...     "data": [
...         {"x": [1], "y": 'a'},
...         {"x": [2], "y": 'b'}
...         ],
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> Tabulator(configuration=configuration)
Tabulator(...)

Example: Data specified as Pandas.DataFrame value

>>> import pandas as pd
>>> configuration = {
...     "layout": "fitColumns",
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> value = pd.DataFrame([
...     {"x": [1], "y": 'a'},
...     {"x": [2], "y": 'b'}
... ])
>>> Tabulator(configuration=configuration, value=value)
Tabulator(...)

Example: Data specified as Bokeh ColumnDataSource value

>>> configuration = {
...     "layout": "fitColumns",
...     "initialSort":[
...         {"column":"y", "dir":"desc"},
...     ],
...     "columns":[
...         {"title": "Value", "field":"x"},
...         {"title": "Item", "field":"y", "hozAlign":"right", "formatter":"money"}
...     ],
... }
>>> value = ColumnDataSource({"x": [1,2], "y": ["a", "b"]})
>>> Tabulator(configuration=configuration, value=value)
Tabulator(...)
"""
        if "configuration" not in params:
            params["configuration"] = _DEFAULT_CONFIGURATION.copy()
        if "selection" not in params:
            params["selection"] = []

        super().__init__(**params)

        self._pause_cds_updates = False
        self._update_column_data_source()

    @param.depends("value", watch=True)
    def _update_column_data_source(self, *_):
        if self._pause_cds_updates:
            return

        if self.value is None:
            self._source = ColumnDataSource({})
        elif isinstance(self.value, pd.DataFrame):
            if (not isinstance(self.value.index, pd.RangeIndex)
                    or self.value.index.start != 0
                    or self.value.index.step != 1):
                raise ValueError(
                    "Please provide a DataFrame with RangeIndex starting at 0 and with step 1"
                )

            if self._source:
                self._source.data = self.value
            else:
                self._source = ColumnDataSource(self.value)
        elif isinstance(self.value, ColumnDataSource):
            self._source = self.value
        else:
            raise ValueError("The `data` provided is not of a supported type!")

    @param.depends("_cell_change", watch=True)
    def _update_value_with_cell_change(self):
        if isinstance(self.value, pd.DataFrame):
            column = self._cell_change["c"]  # pylint: disable=unsubscriptable-object
            index = self._cell_change["i"]  # pylint: disable=unsubscriptable-object
            new_value = self._cell_change["v"]  # pylint: disable=unsubscriptable-object
            self._pause_cds_updates = True
            self.value.at[index, column] = new_value
            self.param.trigger("value")
            self._pause_cds_updates = False

    def stream(self,
               stream_value: Union[pd.DataFrame, pd.Series, Dict],
               reset_index: bool = True):
        """Streams (appends) the `stream_value` provided to the existing value in an efficient
        manner.

        Args:
            stream_value (Union[pd.DataFrame, pd.Series, Dict]): The new value(s) to append to the
                existing value.
            reset_index (bool, optional): If the stream_value is a DataFrame and `reset_index` is
                True then the index of it is reset if True. Helps to keep the index unique and
                named `index`. Defaults to True.

        Raises:
            ValueError: Raised if the stream_value is not a supported type.

        Example: Stream a Series to a DataFrame

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> stream_value = pd.Series({"x": 4, "y": "d"})
        >>> tabulator.stream(stream_value)
        >>> tabulator.value.to_dict("list")
        {'x': [1, 2, 4], 'y': ['a', 'b', 'd']}

        Example: Stream a Dataframe to a Dataframe

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> stream_value = pd.DataFrame({"x": [3, 4], "y": ["c", "d"]})
        >>> tabulator.stream(stream_value)
        >>> tabulator.value.to_dict("list")
        {'x': [1, 2, 3, 4], 'y': ['a', 'b', 'c', 'd']}


        Example: Stream a Dictionary row to a DataFrame

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> stream_value = {"x": 4, "y": "d"}
        >>> tabulator.stream(stream_value)
        >>> tabulator.value.to_dict("list")
        {'x': [1, 2, 4], 'y': ['a', 'b', 'd']}

         Example: Stream a Dictionary of Columns to a Dataframe

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> stream_value = {"x": [3, 4], "y": ["c", "d"]}
        >>> tabulator.stream(stream_value)
        >>> tabulator.value.to_dict("list")
        {'x': [1, 2, 3, 4], 'y': ['a', 'b', 'c', 'd']}
        """

        if isinstance(self.value, pd.DataFrame):
            value_index_start = self.value.index.max() + 1
            if isinstance(stream_value, pd.DataFrame):
                if reset_index:
                    stream_value = stream_value.reset_index(drop=True)
                    stream_value.index += value_index_start
                self._pause_cds_updates = True
                self.value = pd.concat([self.value, stream_value])
                self._source.stream(stream_value)
                self._pause_cds_updates = False
            elif isinstance(stream_value, pd.Series):
                self._pause_cds_updates = True
                self.value.loc[value_index_start] = stream_value
                self._source.stream(stream_value)
                self.param.trigger("value")
                self._pause_cds_updates = False
            elif isinstance(stream_value, dict):
                if stream_value:
                    try:
                        stream_value = pd.DataFrame(stream_value)
                    except ValueError:
                        stream_value = pd.Series(stream_value)
                    self.stream(stream_value)
            else:
                raise ValueError(
                    "The patch value provided is not a DataFrame, Series or Dict!"
                )
        else:
            self._pause_cds_updates = True
            self._source.stream(stream_value)
            self.param.trigger("value")
            self._pause_cds_updates = False

    def patch(self, patch_value: Union[pd.DataFrame, pd.Series, Dict]):
        """Patches (updates) the existing value with the `patch_value` in an efficient manner.

        Args:
            patch_value (Union[pd.DataFrame, pd.Series, Dict]): The value(s) to patch the
                existing value with.

        Raises:
            ValueError: Raised if the patch_value is not a supported type.



        Example: Patch a DataFrame with a Dictionary row.

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> patch_value = {"x": [(0, 3)]}
        >>> tabulator.patch(patch_value)
        >>> tabulator.value.to_dict("list")
        {'x': [3, 2], 'y': ['a', 'b']}

         Example: Patch a Dataframe with a Dictionary of Columns.

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> patch_value = {"x": [(slice(2), (3,4))], "y": [(1,'d')]}
        >>> tabulator.patch(patch_value)
        >>> tabulator.value.to_dict("list")
        {'x': [3, 4], 'y': ['a', 'd']}

        Example: Patch a DataFrame with a Series. Please note the index is used in the update

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> patch_value = pd.Series({"index": 1, "x": 4, "y": "d"})
        >>> tabulator.patch(patch_value)
        >>> tabulator.value.to_dict("list")
        {'x': [1, 4], 'y': ['a', 'd']}

        Example: Patch a Dataframe with a Dataframe. Please note the index is used in the update.

        >>> value = pd.DataFrame({"x": [1, 2], "y": ["a", "b"]})
        >>> tabulator = Tabulator(value=value)
        >>> patch_value = pd.DataFrame({"x": [3, 4], "y": ["c", "d"]})
        >>> tabulator.patch(patch_value)
        >>> tabulator.value.to_dict("list")
        {'x': [3, 4], 'y': ['c', 'd']}
        """
        if isinstance(self.value, pd.DataFrame):
            if isinstance(patch_value, pd.DataFrame):
                patch_value_dict = self._patch_dataframe_to_dict(patch_value)
                self.patch(patch_value_dict)
            elif isinstance(patch_value, pd.Series):
                patch_value_dict = self._patch_series_to_dict(patch_value)
                self.patch(patch_value_dict)
            elif isinstance(patch_value, dict):
                self._patch_from_dict(patch_value)
            else:
                raise ValueError(
                    f"""Patching a patch_value of type {type(patch_value)} is not supported.
                    Please provide a DataFrame, Series or Dict""")
        else:
            if isinstance(patch_value, dict):
                self._pause_cds_updates = True
                self._source.patch(patch_value)
                self.param.trigger("value")
                self._pause_cds_updates = False
            else:
                raise ValueError(
                    f"""Patching a patch_value of type {type(patch_value)} is not supported.
                    Please provide a dict""")

    @staticmethod
    def _patch_dataframe_to_dict(patch_value: pd.DataFrame) -> Dict:
        patch_value_dict: Dict[Any, Any] = {}
        for column in patch_value.columns:
            patch_value_dict[column] = []
            for index in patch_value.index:
                patch_value_dict[column].append(
                    (index, patch_value.loc[index, column]))

        return patch_value_dict

    @staticmethod
    def _patch_series_to_dict(patch_value: pd.Series) -> Dict:
        if "index" in patch_value:  # Series orient is row
            patch_value_dict = {
                k: [(patch_value["index"], v)]
                for k, v in patch_value.items()
            }
            patch_value_dict.pop("index")
        else:  # Series orient is column
            patch_value_dict = {patch_value.name: list(patch_value.items())}
        return patch_value_dict

    def _patch_from_dict(self, patch_value: Dict):
        self._pause_cds_updates = True
        for key, value in patch_value.items():
            for update in value:
                self.value.loc[update[0], key] = update[1]
        self._source.patch(patch_value)
        self.param.trigger("value")
        self._pause_cds_updates = False

    @classmethod
    def to_columns_configuration(
            cls, value: Union[pd.DataFrame,
                              ColumnDataSource]) -> List[Dict[str, str]]:
        """Returns a nice starter `columns` dictionary from the specified `value`.

        Args:
            value (Union[pd.DataFrame, ColumnDataSource]): The data source to transform.

        Returns:
            Dict: The columns configuration

        Example:

        >>> import pandas as pd
        >>> value = {"name": ["python", "panel"]}
        >>> df = pd.DataFrame(value)
        >>> Tabulator.to_columns_configuration(df)
        [{'title': 'Name', 'field': 'name', 'sorter': 'string', 'formatter': 'plaintext', \
'hozAlign': 'left'}]
        """
        col_conf = []
        for field in value.columns:
            dtype = str(value.dtypes[field])
            conf = cls._core(field=field, dtype=dtype)
            col_conf.append(conf)
        return col_conf

    @classmethod
    def _core(cls, field: str, dtype: str) -> Dict[str, str]:
        dtype_str = str(dtype)
        return {
            "title": cls._to_title(field),
            "field": field,
            "sorter": _SORTERS.get(dtype_str, "string"),
            "formatter": _FORMATTERS.get(dtype_str, "plaintext"),
            "hozAlign": _HOZ_ALIGNS.get(dtype_str, "left"),
        }

    @staticmethod
    def _to_title(field: str) -> str:
        return field.replace("_", " ").title()

    @staticmethod
    def config(css: Optional[str] = "default"):
        """Adds the specified css theme to pn.config.css_files

        Args:
            css (Optional[str], optional): [description]. Defaults to "default".
        """
        if css:
            href = CSS_HREFS[css]
            if href not in pn.config.css_files:
                pn.config.css_files.append(href)

    @property
    def selected_values(self) -> Union[pd.DataFrame, ColumnDataSource, None]:
        """Returns the selected rows of the data based

        Raises:
            ValueError: If the value is not of the supported type.

        Returns:
            Union[pd.DataFrame, ColumnDataSource, None]: The selected values of the same type as
                value. Based on the the current selection.
        """
        # Selection is a list of row indices. For example [0,2]
        if self.value is None:
            return None
        if isinstance(self.value, pd.DataFrame):
            return self.value.iloc[self.selection, ]
        if isinstance(self.value, ColumnDataSource):
            # I could not find a direct way to get a selected ColumnDataSource
            selected_data = self.value.to_df().iloc[self.selection, ]
            return ColumnDataSource(selected_data)
        raise ValueError("The value is not of a supported type!")

    @param.depends("selection", watch=True)
    def _update_source_selected_indices(self, *_):
        self._source.selected.indices = self.selection

    def _get_model(self, doc, root=None, parent=None, comm=None):
        model = super()._get_model(doc=doc,
                                   root=root,
                                   parent=parent,
                                   comm=comm)
        if root is None:
            root = model
        self._link_props(model.source.selected, ["indices"], doc, root, comm)
        return model

    def _process_events(self, events):
        if "indices" in events:
            self.selection = events.pop("indices")
        super()._process_events(events)
示例#5
0
class Plot:

    WIDTH = 800
    HEIGHT = 600

    def __init__(self, shifts, multiplicities, deviations):

        self.shifts = shifts
        self.multiplicities = multiplicities
        self.deviations = deviations

        xr = Range1d(start=220, end=-20)

        self.plot = figure(x_axis_label="ppm", x_range=xr, tools="save,reset", plot_width=self.WIDTH, plot_height=self.HEIGHT)

        self.lineSource = ColumnDataSource(data=dict(x=[0], y=[0]))
        self.plot.line('x', 'y', source=self.lineSource, line_width=2)

        # Remove grid from plot
        self.plot.xgrid.grid_line_color = None
        self.plot.ygrid.grid_line_color = None

        # Remove bokeh logo
        self.plot.toolbar.logo = None

        horizontalBoxZoomTool = HorizontalBoxZoomTool()
        self.plot.add_tools(horizontalBoxZoomTool)

        fixedZoomOutTool = FixedZoomOutTool(factor=0.4)
        self.plot.add_tools(fixedZoomOutTool)

        self.plot.extra_y_ranges['box'] = Range1d(start=0, end=0.1)
        self.selectionSource = ColumnDataSource(data=dict(left=[], right=[]), callback=CustomJS(code="""
            if ('data' in this.selected) {
                window.top.location.href = "http://localhost:8080?" + this.selected.data.query + "style=plot";
            }
        """))
        self.selectionSource.on_change('selected', lambda attr, old, new: self.delete(new['1d']['indices']))
        rect = HBar(
            left='left',
            right='right',
            y=0.5,
            height=1,
            line_alpha=0.2,
            fill_alpha=0.2,
            line_color="red",
            fill_color="red"
        )
        renderer = self.plot.add_glyph(self.selectionSource, rect)
        renderer.y_range_name = "box"

        self.labelSource = ColumnDataSource(data=dict(x=[], y=[], text=[]))
        label = Text(
            x='x',
            y='y',
            text='text',
            text_align='center',
            text_font_size="10pt"
        )
        renderer = self.plot.add_glyph(self.labelSource, label)
        renderer.y_range_name = "box"

        removeTool = CustomTapTool()
        self.plot.add_tools(removeTool)
        self.plot.toolbar.active_tap = None

        callback = CustomJS(args=dict(), code="""
            /// get BoxSelectTool dimensions from cb_data parameter of Callback
            var geometry = cb_data['geometry'];

            var shift = (geometry['x0'] + geometry['x1']) / 2;
            var deviation = Math.abs(geometry['x1'] - shift);
            var query = cb_data['query'] + "shift%5B%5D=" + shift + '&multiplicity%5B%5D=any&deviation%5B%5D=' + deviation + '&style=plot';

            window.top.location.href = "http://localhost:8080?" + query;
        """)
        selectTool = CustomBoxSelectTool(
            tool_name="Select Area",
            dimensions = "width",
            callback = callback,
            query=self.paramsToQuery()
        )
        self.plot.add_tools(selectTool)

        self.initSelect()

        self.drawButton = CustomButton(id='myButton')
        self.drawButton.on_click(self.drawPlot)

        curdoc().add_root(
            row(
                column(row(self.plot)),
                column(CustomRow(column(self.drawButton), hide=True))
            )
        )

    def paramsToQuery(self):
        query = ""
        for (shift, multiplicity, deviation) in zip(self.shifts, self.multiplicities, self.deviations):
            query += "shift%5B%5D={}&multiplicity%5B%5D={}&deviation%5B%5D={}&".format(shift, multiplicity, deviation)
        return query

    def initSelect(self):
        left, right = [], []
        labelX, labelY, labelText = [], [], []
        for (shift, multiplicity, deviation) in zip(self.shifts, self.multiplicities, self.deviations):
            left.append(shift - deviation)
            right.append(shift + deviation)

            labelX.append(shift)
            labelY.append(0.08)
            labelText.append(multiplicity)

        self.selectionSource.stream({
            'left':  left,
            'right': right
        })

        self.labelSource.stream({
            'x':    labelX,
            'y':    labelY,
            'text': labelText
        })

    def drawPlot(self, data):
        dic, _ = ng.bruker.read("../data/{}".format(data['id']))
        _, pdata = ng.bruker.read_pdata("../data/{}/pdata/1/".format(data['id']))

        udic = ng.bruker.guess_udic(dic, pdata)
        uc = ng.fileiobase.uc_from_udic(udic)
        ppmScale = uc.ppm_scale()

        self.plot.y_range = None
        self.lineSource.data = {
            'x': ppmScale,
            'y': pdata
        }

    def delete(self, ids):
        if ids:
            left = list(self.selectionSource.data['left'])
            right = list(self.selectionSource.data['right'])

            for i in ids:
                left.pop(i)
                right.pop(i)

                self.shifts.pop(i)
                self.multiplicities.pop(i)
                self.deviations.pop(i)

            self.selectionSource.data = {
                'left': left,
                'right': right
            }

            # Deselect all
            self.selectionSource.selected = {
                '0d': {'glyph': None, 'indices': []},
                '1d': {'indices': []},
                '2d': {'indices': {}},
                'data': {'query': self.paramsToQuery()}
            }

    def selectArea(self, dimensions):
        patch = {
            'left': [dimensions['x0']],
            'right': [dimensions['x1']]
        }
        self.selectionSource.stream(patch)
示例#6
0
class Controller:
    params = {}
    terminating = False

    def __init__(self, params={}) -> None:
        self.params = {}
        for param in PARAMETERS:
            self.update_parameter(param, PARAMETERS[param][1])

        for param in params:
            self.update_parameter(param, params[param])

        self.engine = self.make_engine()

        data = {}
        data['x'], data['y'], data['color'] = trisect(
            self.engine.agents)
        self.visualisation_source = ColumnDataSource(data)

        self.names = [status.name for status in AgentStatus][::-1]
        self.status_source = ColumnDataSource(pd.DataFrame(
            np.zeros((1, len(self.names))), columns=self.names))

        self.controls = Controls(self)

        self.start()

    def update_parameter(self, key, value):
        try:
            self.params[key] = cajole(
                float(value), PARAMETERS[key][0], PARAMETERS[key][2])
        except:
            self.params[key] = PARAMETERS[key][1]

        if key in ('agents', 'sickness_proximity', 'sickness_duration', 'quarantine_delay'):
            self.params[key] == int(self.params[key])
        elif key == 'initial_immunity':
            self.params[key] /= 100
        elif key == 'distancing_factor':
            self.params['distancing_factor'] /= 100
        elif key == 'quarantining':
            self.params['quarantining'] = self.params['quarantining'] == 1

        if 'quarantining' in self.params and not self.params['quarantining']:
            self.params['quarantine_delay'] = self.params['sickness_duration'] + 1

    def make_engine(self) -> Engine:
        return Engine(n=int(self.params['agents']), SICKNESS_PROXIMITY=int(self.params['sickness_proximity']),
                      SICKNESS_DURATION=int(self.params['sickness_duration']), DISTANCING_FACTOR=self.params['distancing_factor'],
                      QUARANTINE_DELAY=int(self.params['quarantine_delay']), INITIAL_IMMUNITY=self.params['initial_immunity'])

    def show(self) -> None:
        self.visualisation = get_visualisation(self.visualisation_source)
        self.population_health_graph = get_population_health_graph(
            self.names, self.status_source, self.params['agents'])

        curdoc().add_root(gridplot([
            [self.visualisation, self.population_health_graph],
            [self.controls.get_controls(), get_about_us()]
        ], toolbar_location="left", toolbar_options={'logo': None}))

    def start(self) -> None:
        self.update_callback = curdoc().add_periodic_callback(
            self.update, 1000 // TICKS_PER_SECOND)

    def update(self) -> None:
        self.engine.tick()

        s = slice(self.engine.agent_count)
        x, y, color = trisect(self.engine.agents)
        self.visualisation_source.patch({
            'x': [(s, x)],
            'y': [(s, y)],
            'color': [(s, color)]
        })

        self.status_source.stream({
            "index": [self.engine.ticks],
            AgentStatus.DEAD.name: [self.engine.stats.get(AgentStatus.DEAD.name, 0)],
            AgentStatus.IMMUNE.name: [self.engine.stats.get(AgentStatus.IMMUNE.name, 0)],
            AgentStatus.INFECTIOUS.name: [self.engine.stats.get(AgentStatus.INFECTIOUS.name, 0)],
            AgentStatus.SUSCEPTIBLE.name: [self.engine.stats.get(AgentStatus.SUSCEPTIBLE.name, 0)],
        })

        if self.engine.stats.get(AgentStatus.INFECTIOUS.name, 0) == 0 and not self.terminating:
            curdoc().add_timeout_callback(self.terminate, 8000)

    def terminate(self) -> None:
        if self.terminating:
            return

        self.terminating = True

        try:
            curdoc().remove_periodic_callback(self.update_callback)
        except ValueError:
            pass

    def reset(self) -> None:
        self.terminate()

        self.engine = self.make_engine()

        data = {}
        data['x'], data['y'], data['color'] = trisect(
            self.engine.agents)
        self.visualisation_source.data = data

        self.status_source.data = {
            "index": [],
            AgentStatus.DEAD.name: [],
            AgentStatus.IMMUNE.name: [],
            AgentStatus.INFECTIOUS.name: [],
            AgentStatus.SUSCEPTIBLE.name: [],
        }

        self.terminating = False

        curdoc().add_next_tick_callback(self.start)
示例#7
0
    def test_stream_bad_data(self):
        ds = ColumnDataSource(data=dict(a=[10], b=[20]))
        with pytest.raises(ValueError, match=r"Must stream updates to all existing columns \(missing: a, b\)"):
            ds.stream(dict())
        with pytest.raises(ValueError, match=r"Must stream updates to all existing columns \(missing: b\)"):
            ds.stream(dict(a=[10]))
        with pytest.raises(ValueError, match=r"Must stream updates to all existing columns \(extra: x\)"):
            ds.stream(dict(a=[10], b=[10], x=[10]))
        with pytest.raises(ValueError, match=r"Must stream updates to all existing columns \(missing: b, extra: x\)"):
            ds.stream(dict(a=[10], x=[10]))
        with pytest.raises(ValueError, match=r"All streaming column updates must be the same length"):
            ds.stream(dict(a=[10], b=[10, 20]))

        with pytest.raises(ValueError, match=r"stream\(...\) only supports 1d sequences, got ndarray with size \(.*"):
            ds.stream(dict(a=[10], b=np.ones((1,1))))
示例#8
0
class BokehVisuals:

    # todo takes helmet as input to define layout
    def __init__(self):
        # todo add port selector for real data

        # create charts for testing headset
        self.figs = [
            figure(plot_width=200, plot_height=125, toolbar_location=None)
            for i in range(8)
        ]
        for f in self.figs:
            f.extra_y_ranges = {"foo": Range1d(start=-100, end=100)}

        [
            f.add_layout(LinearAxis(y_range_name="foo"), 'right')
            for f in self.figs
        ]

        self.cds = ColumnDataSource(
            data={
                i: []
                for i in [
                    item for sublist in [['y' + str(i), 'f' + str(i)]
                                         for i in range(8)] for item in sublist
                ] + ['x']
            })

        [(self.figs[i].line('x', 'y' + str(i), source=self.cds,
                            line_alpha=0.3),
          self.figs[i].line('x',
                            'f' + str(i),
                            source=self.cds,
                            line_color='red',
                            y_range_name="foo",
                            line_width=1)) for i in range(8)]

        for f in self.figs:
            f.axis.visible = False
            f.xgrid.grid_line_color = None
            f.ygrid.grid_line_color = None

        # create charts for visualising features
        cds_feats_data = {'x': [], 'y': []}
        for i in range(8):
            cds_feats_data['value_' + str(i)] = []

        self.cds_feats = ColumnDataSource(data=cds_feats_data)

        colors = [
            "#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce", "#ddb7b1",
            "#cc7878", "#933b41", "#550b1d"
        ]
        mapper = LinearColorMapper(palette=colors, low=10, high=70)

        self.figs_feats = [
            figure(plot_width=400, plot_height=120, toolbar_location=None)
            for i in range(8)
        ]
        for f in self.figs_feats:
            f.axis.visible = False
            f.xgrid.grid_line_color = None
            f.ygrid.grid_line_color = None

        [
            self.figs_feats[i].rect(x="x",
                                    y="y",
                                    width=1,
                                    height=1,
                                    source=self.cds_feats,
                                    line_color=None,
                                    fill_color={
                                        'field': 'value_' + str(i),
                                        'transform': mapper
                                    }) for i in range(8)
        ]

        # create figure for forecast and status
        self.forecast_status_cds = ColumnDataSource(data={
            'x': [],
            'stage': [],
            'prediction': []
        })
        self.forecast_status_figure = figure(plot_width=400,
                                             plot_height=100,
                                             toolbar_location=None)
        self.forecast_status_figure.line('x',
                                         'stage',
                                         source=self.forecast_status_cds,
                                         line_color='red',
                                         line_alpha=0.5)
        self.forecast_status_figure.line('x',
                                         'prediction',
                                         source=self.forecast_status_cds,
                                         line_color='black')
        self.forecast_status_figure.axis.visible = False
        self.forecast_status_figure.xgrid.grid_line_color = None
        self.forecast_status_figure.ygrid.grid_line_color = None

        # figure for status
        self.status_text_cds = ColumnDataSource(data={
            'x': [0],
            'y': [0],
            'text': ['Calibration']
        })
        self.status_text_figure = figure(plot_width=200,
                                         plot_height=100,
                                         toolbar_location=None)
        self.status_text_figure.axis.visible = False
        self.status_text_figure.xgrid.grid_line_color = None
        self.status_text_figure.ygrid.grid_line_color = None
        status_text = Text(x="x", y="y", text="text", text_color='black')
        self.status_text_figure.add_glyph(self.status_text_cds, status_text)

        self.update_button = Button(label="Start session")

        self.doc = curdoc()

    def button_link(self, button_f):
        # todo add stop button
        self.update_button.on_click(button_f)

    def add_layout(self):
        layout = column(
            row(self.figs[0:2] + self.figs_feats[0:2]),
            row(self.figs[2:4] + self.figs_feats[2:4]),
            row(self.figs[4:6] + self.figs_feats[4:6]),
            row(self.figs[6:] + self.figs_feats[6:]),
            row(widgetbox([self.update_button], width=200),
                self.status_text_figure, self.forecast_status_figure))
        self.doc.add_root(layout)

    # todo updates
    # self.visuals.update_tuning(new_data, new_filtered_data, self.filtered_data)
    # self.visuals.update_protocol(self.new_features_data)
    def update_tuning(self, new_data, new_filtered_data, filtered_data):
        lsd = len(filtered_data)
        update_data = {'x': np.arange(lsd - len(new_data[:, 0]), lsd, 1)}

        for i in range(8):
            update_data['f' + str(i)] = new_filtered_data[:, i]
            update_data['y' + str(i)] = new_data[:, i]

        self.cds.stream(update_data, 1000)

    def update_protocol(self, new_features_data, current_state,
                        current_prediction):
        update_data = {
            'x': new_features_data.index.get_level_values(0).values,
            'y': new_features_data.index.get_level_values(1).values
        }

        for i in range(8):
            update_data['value_' + str(i)] = new_features_data[i].values

        self.cds_feats.stream(update_data,
                              len(new_features_data.columns) * 8 * 1000)

        if current_state[0] == 'target':
            stage = 1
        else:
            stage = 0

        self.forecast_status_cds.stream(
            {
                'x': [int(time.time()) - 1],
                'stage': [stage],
                'prediction': [current_prediction]
            }, 1000)

        if current_state[1] == 'calibration':
            self.status_text_cds.stream(
                {
                    'x': [0],
                    'y': [0],
                    'text': ['calibration']
                }, 1)
        else:
            self.status_text_cds.stream(
                {
                    'x': [0],
                    'y': [0],
                    'text': ['feedback']
                }, 1)
示例#9
0
class Visualizer():
    def __init__(self, best_ind, avg_ind):
        self.p = figure(title="evolution",
                        x_axis_label="gen",
                        y_axis_label="energy",
                        plot_width=400,
                        plot_height=400)
        self.bar = figure(title="popul",
                          x_axis_label="inds",
                          y_axis_label="energy",
                          plot_width=400,
                          plot_height=400)
        best_energies = [best_ind]
        avg_energies = [avg_ind]
        index = [0]
        inds = [0 for x in range(0, 1)]
        index = [0]
        self.inds_source = ColumnDataSource(data=dict(x=index, y=inds))
        self.bar.vbar(x="x", top="y", source=self.inds_source, width=0.9)
        data = {'x': index, 'y': best_energies, 'y2': avg_energies}
        self.source = ColumnDataSource(data)
        self.p.line(x='x', y='y', color="red", source=self.source)
        self.p.line(x='x', y='y2', color="green", source=self.source)
        self.doc = curdoc()
        self.layout = row(self.p, self.bar)
        #curdoc().add_periodic_callback(update, 50)
        self.doc.add_root(self.layout)

    @gen.coroutine
    def update(self, x, y, y2, inds):
        if (x == 1):
            self.source.data = dict(x=x, y=y, y2=y2)
        else:
            self.source.stream(dict(x=x, y=y, y2=y2))
        index = list(range(0, len(inds)))
        self.inds_source.data = dict(x=index, y=inds)

    def render_gen(self, gen, popul):
        scores = [x.score for x in popul]
        individuals = [x.genotype for x in popul]
        best_ind = np.min(scores)
        avg_ind = np.average(scores)
        new_list = [best_ind]
        new_avg_list = [avg_ind]
        data = {'x': [gen], 'y': new_list, 'y2': new_avg_list}
        self.doc.add_next_tick_callback(
            partial(self.update,
                    x=data['x'],
                    y=data['y'],
                    y2=data['y2'],
                    inds=scores))

    def update_gen(self, gen, best_ind, avg_ind, popul):
        new_list = [best_ind]
        new_avg_list = [avg_ind]
        data = {'x': [gen], 'y': new_list, 'y2': new_avg_list}
        self.doc.add_next_tick_callback(
            partial(self.update,
                    x=data['x'],
                    y=data['y'],
                    y2=data['y2'],
                    inds=popul))

    def test_run(self, source):
        init_b_values = source.data["y"]
        init_avg_values = source.data["y2"]
        while True:
            new_list = [init_b_values[-1] - 1]
            new_avg_list = [init_avg_values[-1] - 1]
            data = {
                'x': [len(init_b_values) + 1],
                'y': new_list,
                'y2': new_avg_list
            }
            self.doc.add_next_tick_callback(
                partial(self.update, x=data['x'], y=data['y'], y2=data['y2']))
            init_b_values = init_b_values + new_list
            init_avg_values = init_avg_values + new_avg_list
            time.sleep(5)
futures = [c.submit(rosenbrock, (x, y)) for x, y in zip(startx, starty)]
iterator = as_completed(futures)

# TODO(holden): non-blocking?
for res in iterator:
    # take a completed point, is it an improvement?
    point, score = res.result()
    if score < best_score:
        best_score, best_point = score, point
        print(score, point)

    x, y = best_point
    newx, newy = (x + uniform(-scale, scale), y + uniform(-scale, scale))

    # update plot
    source.stream({'x': [newx], 'y': [newy], 'c': ['grey']}, rollover=20)
    push_notebook(document=t)

    # add new point, dynamically, to work on the cluster
    new_point = c.submit(rosenbrock, (newx, newy))
    iterator.add(new_point)  # Start tracking new task as well

    # Narrow search and consider stopping
    scale *= 0.99
    if scale < 0.001:
        break
point


# In[37]: