Esempio n. 1
0
 def proofValidity(self, val=False, desc="valid"):
     validity= widgets.Valid(
             value=val,
             description=str(desc),
             style = self.style
             )
     return validity
    def on_field_filled(self, *args):

        for i, value in enumerate(self.part_info.participant_info_name):
            if self.part_info.participant_info_name.get(
                    self.participant[i].description
            )[0] > self.participant[i].value:
                widgets.Valid(value=False,
                              description='Der eingegebene Wert ist zu tief',
                              style={'description_width': 'initial'})
            elif self.part_info.participant_info_name.get(
                    self.participant[i].description
            )[1] < self.participant[i].value:
                widgets.Valid(value=False,
                              description='Der eingegebene Wert ist zu hoch',
                              style={'description_width': 'initial'})
            else:
                widgets.Valid(value=True,
                              description='',
                              style={'description_width': 'initial'})
Esempio n. 3
0
def valid(boolVal):
    # If the boolean is True
    if boolVal == True:
        valid_widget = widgets.Valid(
            value=boolVal,
            description='Valid!',
        )
        display(valid_widget)
        print("Congrats!!")
        return True

    # If the boolean is false
    elif boolVal == False:
        valid_widget = widgets.Valid(
            value=boolVal,
            description='',
        )

        display(valid_widget)
        print("Try Again!")
    def __init__(self, *args):
        """
        Constructs a Trial object.
        """

        inc = args[0].validate()
        ex = args[1].validate()
        wizard_page = args[3]

        ## Validate In-/Exclusion criteria ##
        if wizard_page == 4:
            if inc and ex == True:
                self.validator = widgets.Valid(
                    value=True,
                    description='Subject ist geeignet für die Studie',
                    style={'description_width': 'initial'})
            else:
                self.validator = widgets.Valid(
                    value=False,
                    description='Subject ist nicht geeignet für die Studie',
                    style={'description_width': 'initial'})
                self.wizard_page = 100
Esempio n. 5
0
    def __init__(self, on_true=None, on_false=None, on_true_output=None, on_false_output=None, \
                 on_true_overwrite_previous_output=True, on_false_overwrite_previous_output=True, on_true_feedback=False, \
                 on_false_feedback=False, run=True, layout={'width':'max-content'}, indent=False, on_true_action_kws={}, on_false_action_kws={}, *args, **kwargs):
        super().__init__(on_true=on_true, on_false=on_false, on_true_output=on_true_output, on_false_output=on_false_output, \
                 on_true_overwrite_previous_output=on_true_overwrite_previous_output, on_true_action_kws=on_true_action_kws, on_false_overwrite_previous_output=on_false_overwrite_previous_output, on_true_feedback=on_true_feedback, \
                 on_false_feedback=on_false_feedback, on_false_action_kws=on_false_action_kws)

        self.widget = widgets.Valid(layout=layout,
                                    indent=indent,
                                    *args,
                                    **kwargs)

        if run:
            self.run()
Esempio n. 6
0
    def old_initiate(self):

        tab_children = []
        ###########################
        # data 1 box
        d1_vbox_childs = []
        ##
        ###
        d1_button_next = widgets.Button(description='next measurement')
        d1_button_prev = widgets.Button(description='prev measurement')

        d1_button_next.on_click(self.on_d1_botton_next)
        d1_button_prev.on_click(self.on_d1_botton_prev)

        d1_box_h_1 = widgets.HBox([d1_button_prev, d1_button_next])
        ###
        d1_vbox_childs.append(d1_box_h_1)

        ##
        ###
        d1_text_path = widgets.Text(placeholder='path name', disabled=False)
        self.d1_text_path = d1_text_path
        d1_vbox_childs.append(d1_text_path)

        ##
        d1_vbox = widgets.VBox(d1_vbox_childs)
        tab_children.append({'element': d1_vbox, 'title': 'iMet'})

        ############################
        # data 2 box
        d2_vbox_childs = []
        ##
        ###
        d2_button_next = widgets.Button(description='next measurement')
        d2_button_prev = widgets.Button(description='prev measurement')

        self.d2_dropdown_fnames = widgets.Dropdown(
            options=[
                1
            ],  #[i.name for i in self.controller.data.dataset2.path2data_list],
            value=1,  #self.controller.data.dataset2.path2active.name,
            #     description='N',
            disabled=False,
        )

        d2_button_next.on_click(self.on_d2_botton_next)
        d2_button_prev.on_click(self.on_d2_botton_prev)
        self.d2_dropdown_fnames.observe(self.on_change_d2_dropdown_fnames)

        d2_box_h_1 = widgets.HBox(
            [d2_button_prev, d2_button_next, self.d2_dropdown_fnames])
        ###
        d2_vbox_childs.append(d2_box_h_1)

        ##
        ###
        # text field showing the path
        d2_text_path = widgets.Text(placeholder='path name', disabled=False)
        self.d2_text_path = d2_text_path

        d2_vbox_childs.append(d2_text_path)

        ##
        d2_vbox = widgets.VBox(d2_vbox_childs)
        tab_children.append({'element': d2_vbox, 'title': 'POPS'})

        # others box

        # Tab
        tab = widgets.Tab([child['element'] for child in tab_children])
        for e, child in enumerate(tab_children):
            tab.set_title(e, child['title'])

        # accordeon

        self.accordeon_assigned = widgets.Valid(
            value=False,
            description='bound?',
        )

        self.dropdown_popssn = widgets.Dropdown(
            options=['00', '14', '18'],
            # value='2',
            description='popssn',
            disabled=False,
        )

        self.inttext_deltat = widgets.IntText(value=0,
                                              description='deltat',
                                              disabled=False)
        self.inttext_deltat.observe(self.on_inttext_deltat)

        self.dropdown_gps_bar_bad = widgets.Dropdown(
            options=[
                'gps', 'baro', 'bad', 'bad_but_usable_gps',
                'bad_but_usable_baro'
            ],
            value='gps',
            description='which alt to use:',
            disabled=False,
        )

        self.button_bind_measurements = widgets.ToggleButton(
            description='bind/unbind measurements')
        # button_bind_measurements.on_click(self.deprecated_on_button_bind_measurements)
        self.button_bind_measurements.observe(self.on_button_bind_measurements)

        accordon_box = widgets.VBox([
            self.accordeon_assigned, self.dropdown_popssn, self.inttext_deltat,
            self.dropdown_gps_bar_bad, self.button_bind_measurements
        ])
        accordion_children = [accordon_box]
        accordion = widgets.Accordion(children=accordion_children)
        accordion.set_title(0, 'do_stuff')

        # messages
        self.messages = widgets.Textarea('\n'.join(self.controller._message),
                                         layout={'width': '100%'})
        # message_box = widgets.HBox([self.messages])
        # OverVbox

        overVbox = widgets.VBox([tab, accordion, self.messages])
        display(overVbox)
        ####################
        self.update_d1()
        self.update_d2()
        self.update_accordeon()
Esempio n. 7
0
def plot_interactive_mapper_graph(pipeline, data, layout='kamada_kawai',
                                  layout_dim=2, color_variable=None,
                                  node_color_statistic=None,
                                  color_by_columns_dropdown=False,
                                  plotly_kwargs=None):
    """Plotting function for interactive Mapper graphs.

    Parameters
    ----------
    pipeline : :class:`~gtda.mapper.pipeline.MapperPipeline` object
        Mapper pipeline to act on to data.

    data : array-like of shape (n_samples, n_features)
        Data used to generate the Mapper graph. Can be a pandas dataframe.

    layout : None, str or callable, optional, default: ``'kamada-kawai'``
        Layout algorithm for the graph. Can be any accepted value for the
        ``layout`` parameter in the :meth:`layout` method of
        :class:`igraph.Graph`. [1]_

    layout_dim : int, default: ``2``
        The number of dimensions for the layout. Can be 2 or 3.

    color_variable : object or None, optional, default: ``None``
        Specifies which quantity is to be used for node coloring.

            1. If a numpy ndarray or pandas dataframe, `color_variable`
               must have the same length as `data` and is interpreted as
               a quantity of interest according to which node of the Mapper
               graph is to be colored (see `node_color_statistic`).
            2. If ``None`` then equivalent to passing `data`.
            3. If an object implementing :meth:`transform` or
               :meth:`fit_transform`, e.g. a scikit-learn estimator or
               pipeline, it is applied to `data` to generate the quantity
               of interest.
            4. If an index or string, or list of indices / strings, equivalent
               to selecting a column or subset of columns from `data`.

    node_color_statistic :None, callable, or ndarray of shape (n_nodes,) or \
        (n_nodes, 1), optional, default: ``None``
        Specifies how to determine the colors of each node. If a
        numpy array, it must have the same length as the number of nodes in
        the Mapper graph, and its values are used directly for node
        coloring, ignoring `color_variable`. Otherwise, it can be a
        callable object which is used to obtain a summary statistic, within
        each Mapper node, of the quantity specified by `color_variable`. The
        default value ``None`` is equivalent to passing ``numpy.mean``.

    color_by_columns_dropdown : bool, optional, default: ``False``
        If ``True``, a dropdown widget is generated which allows the user to
        color Mapper nodes according to any column in `data`.

    plotly_kwargs : dict, optional, default: ``None``
        Keyword arguments to configure the Plotly Figure.

    Returns
    -------
    box : :class:`ipywidgets.VBox` object
    A box containing the following widgets: parameters of the clustering
    algorithm, parameters for the covering scheme, a Mapper graph arising
    from those parameters, a validation box, and logs.

    References
    ----------
    .. [1] `igraph.Graph.layout
            <https://igraph.org/python/doc/igraph.Graph-class.html#layout>`_
            documentation.

    """

    # clone pipeline to avoid side effects from in-place parameter changes
    pipe = clone(pipeline)

    if node_color_statistic is not None:
        _node_color_statistic = node_color_statistic
    else:
        _node_color_statistic = np.mean

    def get_widgets_per_param(param, value):
        if isinstance(value, float):
            return (param, widgets.FloatText(
                value=value,
                step=0.05,
                description=param.split('__')[1],
                continuous_update=False,
                disabled=False
            ))
        elif isinstance(value, int):
            return (param, widgets.IntText(
                value=value,
                step=1,
                description=param.split('__')[1],
                continuous_update=False,
                disabled=False
            ))
        elif isinstance(value, str):
            return (param, widgets.Text(
                value=value,
                description=param.split('__')[1],
                continuous_update=False,
                disabled=False
            ))
        else:
            return None

    def update_figure(figure, edge_trace, node_trace, layout_dim):
        figure.data[0].x = edge_trace.x
        figure.data[0].y = edge_trace.y
        figure.data[1].x = node_trace.x
        figure.data[1].y = node_trace.y

        if layout_dim == 3:
            figure.data[0].z = edge_trace.z
            figure.data[1].z = node_trace.z

        figure.data[1].marker.size = node_trace.marker.size
        figure.data[1].marker.color = node_trace.marker.color
        figure.data[1].marker.cmin = node_trace.marker.cmin
        figure.data[1].marker.cmax = node_trace.marker.cmax
        figure.data[1].marker.sizeref = node_trace.marker.sizeref
        figure.data[1].hoverlabel = node_trace.hoverlabel
        figure.data[1].hovertext = node_trace.hovertext

    def on_parameter_change(change):
        handler.clear_logs()
        try:
            for param, value in cover_params.items():
                if isinstance(value, (int, float, str)):
                    pipe.set_params(
                        **{param: cover_params_widgets[param].value})
            for param, value in cluster_params.items():
                if isinstance(value, (int, float, str)):
                    pipe.set_params(
                        **{param: cluster_params_widgets[param].value})

            logger.info("Updating figure ...")
            with fig.batch_update():
                (node_trace, edge_trace, node_elements, node_colors,
                 plot_options) = _calculate_graph_data(
                    pipe, data, layout, layout_dim,
                    color_variable, _node_color_statistic, plotly_kwargs
                )
                update_figure(fig, edge_trace, node_trace, layout_dim)

                # Update color by column buttons
                is_data_dataframe = hasattr(data, 'columns')
                if color_by_columns_dropdown:
                    column_color_buttons = _get_column_color_buttons(
                        data, is_data_dataframe, node_elements, node_colors,
                        plot_options['node_trace_marker_colorscale'])
                else:
                    column_color_buttons = None

                button_height = 1.1
                fig.update_layout(
                    updatemenus=[
                        go.layout.Updatemenu(
                            buttons=column_color_buttons,
                            direction="down",
                            pad={"r": 10, "t": 10},
                            showactive=True,
                            x=0.11,
                            xanchor='left',
                            y=button_height,
                            yanchor="top"
                        ),
                    ])

            valid.value = True
        except Exception:
            exception_data = traceback.format_exc().splitlines()
            logger.exception(exception_data[-1])
            valid.value = False

    def observe_widgets(params, widgets):
        for param, value in params.items():
            if isinstance(value, (int, float, str)):
                widgets[param].observe(on_parameter_change, names='value')

    # define output widget to capture logs
    out = widgets.Output()

    @out.capture()
    def click_box(change):
        if logs_box.value:
            out.clear_output()
            handler.show_logs()
        else:
            out.clear_output()

    # initialise logging
    logger = logging.getLogger(__name__)
    handler = OutputWidgetHandler()
    handler.setFormatter(logging.Formatter(
        '%(asctime)s - [%(levelname)s] %(message)s'))
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)

    # initialise cover and cluster dictionaries of parameters and widgets
    cover_params = dict(filter(lambda x: x[0].startswith('cover'),
                               pipe.get_mapper_params().items()))
    cover_params_widgets = dict(
        filter(None, map(lambda x: get_widgets_per_param(*x),
                         cover_params.items())))
    cluster_params = dict(filter(lambda x: x[0].startswith('clusterer'),
                                 pipe.get_mapper_params().items()))
    cluster_params_widgets = dict(
        filter(None, map(lambda x: get_widgets_per_param(*x),
                         cluster_params.items())))

    # initialise widgets for validating input parameters of pipeline
    valid = widgets.Valid(
        value=True,
        description='Valid parameters',
        style={'description_width': '100px'},
    )

    # initialise widget for showing the logs
    logs_box = widgets.Checkbox(
        description='Show logs: ',
        value=False,
        indent=False
    )

    # initialise figure with initial pipeline and config
    if plotly_kwargs is None:
        plotly_kwargs = dict()

    fig = plot_static_mapper_graph(
        pipe, data, layout, layout_dim, color_variable, _node_color_statistic,
        color_by_columns_dropdown, plotly_kwargs, clone_pipeline=False)

    observe_widgets(cover_params, cover_params_widgets)
    observe_widgets(cluster_params, cluster_params_widgets)

    logs_box.observe(click_box, names='value')

    # define containers for input widgets
    container_cover = widgets.HBox(
        children=list(cover_params_widgets.values()))

    container_cluster_layout = Layout(display='flex', flex_flow='row wrap')

    container_cluster = widgets.HBox(
        children=list(cluster_params_widgets.values()),
        layout=container_cluster_layout)

    box = widgets.VBox(
        [container_cover, container_cluster, fig, valid, logs_box, out])
    return box
 def _create(self):
     default_layout = widgets.Layout(width='auto', height='auto')
     self.wg['organization'] = widgets.Text(value='',
                                            description='Organization',
                                            layout=default_layout)
     self.wg['filter'] = widgets.Text(
         description='Filter',
         placeholder='Repository names must contain',
         layout=default_layout)
     self.wg['filename'] = widgets.Text(value='exercice.py',
                                        description='Filename',
                                        layout=default_layout)
     self.wg['request_url'] = widgets.Text(
         description='Request URL',
         value=
         f'https://raw.githubusercontent.com/{self.wg["organization"].value}/%RepositoryName%/master/{self.wg["filename"].value}',
         layout=default_layout,
         disabled=True)
     self.wg['get_files'] = widgets.Button(description='Fetch submissions')
     self.wg['get_files_status'] = widgets.Valid(value=True,
                                                 description='Ready',
                                                 layout=default_layout)
     self.wg['previous_button'] = widgets.Button(description='Previous')
     self.wg['next_button'] = widgets.Button(description='Next')
     self.wg['open_in_browser'] = widgets.Button(
         description='Open in GitHub', layout=default_layout)
     self.wg['open_file'] = widgets.Checkbox(description='File only',
                                             layout=default_layout)
     self.wg['repository_select'] = widgets.Dropdown(
         description='Select', layout=widgets.Layout(width='600px'))
     self.wg['max_preview_lines'] = widgets.IntText(
         value=100, disabled=False, layout=widgets.Layout(width='50px'))
     self.wg['preview_lines_range'] = widgets.IntRangeSlider(
         value=[0, 20],
         min=0,
         max=self.wg['max_preview_lines'].value,
         step=1,
         description='Lines range:',
         continuous_update=True,
         orientation='horizontal',
         readout=True,
         readout_format='d',
         layout=widgets.Layout(width='500px'))
     self.wg['repository_grading'] = widgets.HTML(
         layout=widgets.Layout(width='auto',
                               height='auto',
                               border='solid 1px',
                               padding='2px 10px 2px 10px'))
     html_layout = widgets.Layout(width='auto',
                                  height='auto',
                                  padding='20px 100px 0px 20px')
     self.wg['file_preview_stats'] = widgets.HTML(layout=html_layout)
     self.wg['file_preview'] = widgets.HTML(layout=html_layout)
     self.wg['file_view_stats'] = widgets.HTML(layout=html_layout)
     self.wg['file_view'] = widgets.HTML(layout=html_layout)
     file_preview_box = widgets.HBox(
         (self.wg['file_preview'], self.wg['file_preview_stats']))
     file_view_box = widgets.HBox(
         (self.wg['file_view'], self.wg['file_view_stats']))
     lines_range_box = widgets.HBox(
         (self.wg['preview_lines_range'], self.wg['max_preview_lines']))
     self.wg['accordion'] = widgets.Accordion(children=[
         widgets.VBox((lines_range_box, file_preview_box)), file_view_box
     ])
     self.wg['accordion'].set_title(0, 'Preview')
     self.wg['accordion'].set_title(1, 'File')
Esempio n. 9
0
    def plot(self,
             color_data=None,
             color_features=None,
             node_color_statistic=None,
             layout="kamada_kawai",
             layout_dim=2,
             n_sig_figs=3,
             node_scale=12,
             plotly_params=None):
        """ Produce the interactive Mapper widget.

        Parameters
        ----------
        color_data : array-like of length n_samples, or None, optional, \
            default: ``None``
            Data to be used to construct node colors in the Mapper graph
            (according to `color_features` and `node_color_statistic`). Must
            have the same length as `data`. ``None`` is the same as passing
            ``numpy.arange(len(data))``.

        color_features : object or None, optional, default: ``None``
            Specifies one or more feature of interest from `color_data` to be
            used, together with `node_color_statistic`, to determine node
            colors.

                1. ``None`` is equivalent to passing `color_data`.
                2. If an object implementing :meth:`transform` or
                   :meth:`fit_transform`, or a callable, it is applied to
                   `color_data` to generate the features of interest.
                3. If an index or string, or list of indices/strings, it is
                   equivalent to selecting a column or subset of columns from
                   `color_data`.

        node_color_statistic : None or callable, optional, default: ``None``
            If a callable, node colors will be computed as summary statistics
            from the feature array ``y`` determined by `color_data` and
            `color_features`. Let ``y`` have ``n`` columns (note: 1d feature
            arrays are converted to column vectors). Then, for a node
            representing a list ``I`` of row indices, there will be ``n``
            colors, each computed as ``node_color_statistic(y[I, i])`` for
            ``i`` between ``0`` and ``n``.

        layout : None, str or callable, optional, default: ``"kamada-kawai"``
            Layout algorithm for the graph. Can be any accepted value for the
            ``layout`` parameter in the :meth:`layout` method of
            :class:`igraph.Graph` [1]_.

        layout_dim : int, default: ``2``
            The number of dimensions for the layout. Can be 2 or 3.

        n_sig_figs : int or None, optional, default: ``3``
           If not ``None``, number of significant figures to which to round
           node summary statistics. If ``None``, no rounding is performed.

        node_scale : int or float, optional, default: ``12``
            Sets the scale factor used to determine the rendered size of the
            nodes. Increase for larger nodes. Implements a formula in the
            `Plotly documentation \
            <plotly.com/python/bubble-charts/#scaling-the-size-of-bubble-charts>`_.

        plotly_params : dict or None, optional, default: ``None``
            Custom parameters to configure the plotly figure. Allowed keys are
            ``"node_trace"``, ``"edge_trace"`` and ``"layout"``, and the
            corresponding values should be dictionaries containing keyword
            arguments as would be fed to the :meth:`update_traces` and
            :meth:`update_layout` methods of
            :class:`plotly.graph_objects.Figure`.

        Returns
        -------
        box : :class:`ipywidgets.VBox` object
            A box containing the following widgets: parameters of the
            clustering algorithm, parameters for the covering scheme, a Mapper
            graph arising from those parameters, a validation box, and logs.

        """
        # Clone pipeline to avoid side effects from in-place parameter changes
        if self.clone_pipeline:
            self._pipeline = clone(self.pipeline)
        else:
            self._pipeline = self.pipeline

        def get_widgets_per_param(params):
            for key, value in params.items():
                style = {'description_width': 'initial'}
                description = key.split("__")[1] if "__" in key else key
                if isinstance(value, float):
                    yield (key,
                           widgets.FloatText(value=value,
                                             step=0.05,
                                             description=description,
                                             continuous_update=False,
                                             disabled=False,
                                             layout=Layout(width="90%"),
                                             style=style))
                elif isinstance(value, bool):
                    yield (key,
                           widgets.ToggleButton(value=value,
                                                description=description,
                                                disabled=False,
                                                layout=Layout(width="90%"),
                                                style=style))
                elif isinstance(value, int):
                    yield (key,
                           widgets.IntText(value=value,
                                           step=1,
                                           description=description,
                                           continuous_update=False,
                                           disabled=False,
                                           layout=Layout(width="90%"),
                                           style=style))
                elif isinstance(value, str):
                    yield (key,
                           widgets.Text(value=value,
                                        description=description,
                                        continuous_update=False,
                                        disabled=False,
                                        layout=Layout(width="90%"),
                                        style=style))

        def on_parameter_change(change):
            handler.clear_logs()
            try:
                for param, value in cover_params.items():
                    if isinstance(value, (int, float, str)):
                        self._pipeline.set_params(
                            **{param: cover_params_widgets[param].value})
                for param, value in cluster_params.items():
                    if isinstance(value, (int, float, str)):
                        self._pipeline.set_params(
                            **{param: cluster_params_widgets[param].value})
                for param, value in nerve_params.items():
                    if isinstance(value, (int, bool)):
                        self._pipeline.set_params(
                            **{param: nerve_params_widgets[param].value})

                logger.info("Updating figure...")
                with self._figure.batch_update():
                    self._graph = self._pipeline.fit_transform(self.data)
                    (edge_trace, node_trace,
                     self._node_colors_color_features) = \
                        _calculate_graph_data(
                            self._graph, self._color_data_transformed,
                            node_color_statistic, layout, layout_dim,
                            n_sig_figs, node_scale
                        )
                    if colorscale_for_hoverlabel is not None:
                        min_col, max_col = \
                            np.min(self._node_colors_color_features[:, 0]), \
                            np.max(self._node_colors_color_features[:, 0])
                        hoverlabel_bgcolor = _get_colors_for_vals(
                            self._node_colors_color_features[:, 0], min_col,
                            max_col, colorscale_for_hoverlabel)
                        self._figure.update_traces(
                            hoverlabel_bgcolor=hoverlabel_bgcolor,
                            selector={"name": "node_trace"})

                    self._figure.update_traces(
                        x=node_trace.x,
                        y=node_trace.y,
                        marker_color=node_trace.marker.color,
                        marker_size=node_trace.marker.size,
                        marker_sizeref=node_trace.marker.sizeref,
                        hovertext=node_trace.hovertext,
                        **({
                            "z": node_trace.z
                        } if layout_dim == 3 else dict()),
                        selector={"name": "node_trace"})
                    self._figure.update_traces(
                        x=edge_trace.x,
                        y=edge_trace.y,
                        **({
                            "z": edge_trace.z
                        } if layout_dim == 3 else dict()),
                        selector={"name": "edge_trace"})

                    # Update color by column buttons if relevant
                    if self._node_colors_color_features.shape[1] > 1:
                        hovertext_color_features = node_trace.hovertext
                        column_color_buttons = _get_column_color_buttons(
                            self._node_colors_color_features,
                            hovertext_color_features,
                            colorscale_for_hoverlabel, n_sig_figs,
                            column_names_dropdown)

                        button_height = 1.1
                        self._figure.update_layout(updatemenus=[
                            go.layout.Updatemenu(buttons=column_color_buttons,
                                                 direction="down",
                                                 pad={
                                                     "r": 10,
                                                     "t": 10
                                                 },
                                                 showactive=True,
                                                 x=0.11,
                                                 xanchor="left",
                                                 y=button_height,
                                                 yanchor="top")
                        ])

                valid.value = True
            except Exception:
                exception_data = traceback.format_exc().splitlines()
                logger.exception(exception_data[-1])
                valid.value = False

        def observe_widgets(params, widgets):
            for param, value in params.items():
                if isinstance(value, (int, float, str)):
                    widgets[param].observe(on_parameter_change, names="value")

        # Define output widget to capture logs
        out = widgets.Output()

        @out.capture()
        def click_box(change):
            if logs_box.value:
                out.clear_output()
                handler.show_logs()
            else:
                out.clear_output()

        # Initialise logging
        logger = logging.getLogger(__name__)
        handler = OutputWidgetHandler()
        handler.setFormatter(
            logging.Formatter("%(asctime)s - [%(levelname)s] %(message)s"))
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)

        # Initialise cover, cluster and nerve dictionaries of parameters and
        # widgets
        mapper_params_items = self._pipeline.get_mapper_params().items()
        cover_params = {
            key: value
            for key, value in mapper_params_items if key.startswith("cover__")
        }
        cover_params_widgets = dict(get_widgets_per_param(cover_params))
        cluster_params = {
            key: value
            for key, value in mapper_params_items
            if key.startswith("clusterer__")
        }
        cluster_params_widgets = dict(get_widgets_per_param(cluster_params))
        nerve_params = {
            key: value
            for key, value in mapper_params_items
            if key in ["min_intersection", "contract_nodes"]
        }
        nerve_params_widgets = dict(get_widgets_per_param(nerve_params))

        # Initialise widgets for validating input parameters of pipeline
        valid = widgets.Valid(
            value=True,
            description="Valid parameters",
            style={"description_width": "100px"},
        )

        # Initialise widget for showing the logs
        logs_box = widgets.Checkbox(description="Show logs: ",
                                    value=False,
                                    indent=False)

        # Initialise figure with initial pipeline and config
        self._graph = self._pipeline.fit_transform(self.data)
        (self._color_data_transformed, column_names_dropdown,
         node_color_statistic) = \
            _validate_color_kwargs(self._graph, self.data, color_data,
                                   color_features, node_color_statistic,
                                   interactive=True)
        edge_trace, node_trace, self._node_colors_color_features = \
            _calculate_graph_data(
                self._graph, self._color_data_transformed,
                node_color_statistic, layout, layout_dim, n_sig_figs,
                node_scale
            )

        self._figure = _produce_static_figure(edge_trace, node_trace,
                                              self._node_colors_color_features,
                                              column_names_dropdown,
                                              layout_dim, n_sig_figs,
                                              plotly_params)

        colorscale_for_hoverlabel = None
        if layout_dim == 3:
            # In plot_static_mapper_graph, hoverlabel bgcolors are set to white
            # if something goes wrong in computing them according to the
            # colorscale.
            is_bgcolor_not_white = \
                self._figure.data[1].hoverlabel.bgcolor != "white"
            user_hoverlabel_bgcolor = False
            if plotly_params:
                if "node_trace" in plotly_params:
                    if "hoverlabel_bgcolor" in plotly_params["node_trace"]:
                        user_hoverlabel_bgcolor = True
            if is_bgcolor_not_white and not user_hoverlabel_bgcolor:
                colorscale_for_hoverlabel = \
                    self._figure.data[1].marker.colorscale

        observe_widgets(cover_params, cover_params_widgets)
        observe_widgets(cluster_params, cluster_params_widgets)
        observe_widgets(nerve_params, nerve_params_widgets)

        logs_box.observe(click_box, names="value")

        # Define containers for input widgets
        cover_title = HTML(value="<b>Cover parameters</b>")
        container_cover = widgets.VBox(children=[cover_title] +
                                       list(cover_params_widgets.values()))
        container_cover.layout.align_items = 'center'

        cluster_title = HTML(value="<b>Clusterer parameters</b>")
        container_cluster = widgets.VBox(
            children=[cluster_title] + list(cluster_params_widgets.values()), )
        container_cluster.layout.align_items = 'center'

        nerve_title = HTML(value="<b>Nerve parameters</b>")
        container_nerve = widgets.VBox(children=[nerve_title] +
                                       list(nerve_params_widgets.values()), )
        container_nerve.layout.align_items = 'center'

        container_parameters = widgets.HBox(
            children=[container_cover, container_cluster, container_nerve])

        box = widgets.VBox(
            [container_parameters, self._figure, valid, logs_box, out])

        return box
Esempio n. 10
0
def create_interactive_network(pipeline, data, node_pos=None, node_color=None,
                               columns_to_color=None, plotly_kwargs=None,
                               summary_stat=np.mean):
    """
    Parameters
    ----------
    pipeline : MapperPipeline
        The nerve of the refined pullback cover. Nodes correspond to cluster
        sets, and an edge exists between two nodes if they share at least one
        point in common.

    data : ndarray, shape (n_samples, n_features)
        The point cloud data used to generate the nerve.

    node_pos : igraph.Graph.layout or ndarray, shape (n_nodes, n_dims)
        Encodes the layout of the graph according to a layout algorithm or
        pre-defined array of coordinates in an n-dimensional space.

    node_color : ndarray, shape (n_nodes,)
        The numerical values to color each node of the graph by.

    columns_to_color : dict, optional, default: ``None``
        Key-value pairs (column_name, column_index) to specify which columns of
        :attr:`data` to color the graph by. Generates a dropdown widget to
        select the columns to color by.

    plotly_kwargs : dict, optional, default: ``None``
        Keyword arguments to configure the Plotly Figure.

    summary_stat : callable, default ``np.mean``
        Summary statistic to apply to the elements in each node of the
        topological graph.
    """
    # TODO could abstract away common patterns in get_cover_params_widgets and
    #  get_cluster_params_widgets

    # TODO allow dimension to be passed as either 2 or 3 as an arg or kwarg

    # clone input pipeline to catch scenarios where user selects invalid
    # configuration of parameters and re-executes cell in Jupyter notebook
    pipe = clone(pipeline)

    def get_cover_params_widgets(param, value):
        if isinstance(value, float):
            return (param, widgets.FloatSlider(
                value=value,
                step=0.05,
                min=0.05,
                max=1.0,
                description=param.split('__')[1],
                disabled=False
            ))
        elif isinstance(value, int):
            return (param, widgets.IntSlider(
                value=value,
                min=1,
                max=100,
                step=1,
                description=param.split('__')[1],
                disabled=False
            ))
        else:
            return None

    def get_cluster_params_widgets(param, value):
        if isinstance(value, float):
            return (param, widgets.FloatText(
                    value=value,
                    step=0.1,
                    description=param.split('__')[1],
                    disabled=False
                    ))
        elif isinstance(value, int):
            return (param, widgets.IntText(
                value=value,
                step=1,
                description=param.split('__')[1],
                disabled=False
            ))
        elif isinstance(value, str):
            return (param, widgets.Text(
                value=value,
                description=param.split('__')[1],
                disabled=False
            ))
        else:
            return None

    def update_figure(old_figure, new_figure):
        # TODO could this be abstracted to node and edge traces and metadata
        #  information without need for creating a full new figure object
        old_figure.data[0].x = new_figure.data[0].x
        old_figure.data[0].y = new_figure.data[0].y
        old_figure.data[1].x = new_figure.data[1].x
        old_figure.data[1].y = new_figure.data[1].y
        old_figure.data[1].marker.size = new_figure.data[1].marker.size
        old_figure.data[1].marker.color = new_figure.data[1].marker.color
        old_figure.data[1].marker.sizeref = new_figure.data[1].marker.sizeref

    def get_figure(pipe, data, node_pos, node_color, columns_to_color,
                   summary_stat):
        graph = pipe.fit_transform(data)
        node_elements = graph['node_metadata']['node_elements']
        if node_pos is None:
            node_pos = graph.layout('kamada_kawai')

        if node_color is None:
            node_color = get_node_summary(node_elements, data,
                                          summary_stat=summary_stat)

        return create_network_2d(graph, data, node_pos, node_color,
                                 columns_to_color, plotly_kwargs)

    def response_numeric(change):
        # TODO: remove hardcoding of keys and mimic what is done with clusterer
        handler.clear_logs()
        try:
            pipe.set_mapper_params(
                cover__n_intervals=cover_params_widgets['cover__n_intervals']
                .value)
            pipe.set_mapper_params(
                cover__overlap_frac=cover_params_widgets['cover__overlap_frac']
                .value)

            for param, value in cluster_params.items():
                if isinstance(value, (int, float)):
                    pipe.set_mapper_params(
                        **{param: cluster_params_widgets[param].value}
                    )

            # TODO check this alternative:
            #
            # num_params = {param: value for param, value in
            #               cluster_params.items()
            #               if isinstance(value, (int, float))}
            #
            # pipe.set_mapper_params(
            #     **{param: cluster_params_widgets[param].value for param in
            #        num_params}
            # )

            new_fig = get_figure(pipe, data, node_pos,
                                 node_color, columns_to_color, summary_stat)

            logger.info("Updating figure ...")
            with fig.batch_update():
                update_figure(fig, new_fig)
            valid.value = True
        except Exception:
            exception_data = traceback.format_exc().splitlines()
            logger.exception(exception_data[-1])
            valid.value = False

    def response_text(text):
        handler.clear_logs()
        try:
            for param, value in cluster_params.items():
                if isinstance(value, str):
                    pipe.set_mapper_params(
                        **{param: cluster_params_widgets[param].value}
                    )

            new_fig = get_figure(pipe, data, node_pos,
                                 node_color, columns_to_color, summary_stat)

            logger.info("Updating figure ...")
            with fig.batch_update():
                update_figure(fig, new_fig)
            valid.value = True
        except Exception:
            exception_data = traceback.format_exc().splitlines()
            logger.exception(exception_data[-1])
            valid.value = False

    def observe_numeric_widgets(params, widgets):
        for param, value in params.items():
            if isinstance(value, (int, float)):
                widgets[param].observe(response_numeric, names='value')

    # define output widget to capture logs
    out = widgets.Output()

    @out.capture()
    def click_box(change):
        if logs_box.value:
            out.clear_output()
            handler.show_logs()
        else:
            out.clear_output()

    # initialise logging
    logger = logging.getLogger(__name__)
    handler = OutputWidgetHandler()
    handler.setFormatter(logging.Formatter(
        '%(asctime)s  - [%(levelname)s] %(message)s'))
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)

    # initialise cover and cluster dictionaries of parameters and widgets
    cover_params = dict(filter(lambda x: x[0].startswith('cover'),
                               pipe.get_mapper_params().items()))
    cover_params_widgets = dict(
        filter(None, map(lambda x: get_cover_params_widgets(*x),
                         cover_params.items())))
    cluster_params = dict(filter(lambda x: x[0].startswith('clusterer'),
                                 pipe.get_mapper_params().items()))
    cluster_params_widgets = dict(
        filter(None, map(lambda x: get_cluster_params_widgets(*x),
                         cluster_params.items())))

    # create button for text inputs
    submit_button = widgets.Button(description="Submit")
    submit_button.on_click(response_text)

    # initialise widgets for validating input parameters of pipeline
    valid = widgets.Valid(
        value=True,
        description='Valid parameters',
        style={'description_width': '100px'},
    )

    # initialise widget for showing the logs
    logs_box = widgets.Checkbox(
        description='Show logs: ',
        value=False,
        indent=False
    )

    # initialise figure with initial pipeline and config
    if plotly_kwargs is None:
        plotly_kwargs = dict()

    fig = get_figure(pipe, data, node_pos, node_color,
                     columns_to_color, summary_stat)

    observe_numeric_widgets(cover_params, cover_params_widgets)
    observe_numeric_widgets(cluster_params, cluster_params_widgets)

    logs_box.observe(click_box, names='value')

    # define containers for input widgets
    container_cover = widgets.HBox(children=list(
        cover_params_widgets.values()))

    container_cluster_text = widgets.HBox(
        children=list(v for k, v in cluster_params_widgets.items()
                      if isinstance(cluster_params[k], str)) + [submit_button])

    container_cluster_layout = Layout(display='flex', flex_flow='row wrap')

    container_cluster_numeric = widgets.HBox(
        children=list(v for k, v in cluster_params_widgets.items()
                      if isinstance(cluster_params[k], (int, float))
                      ), layout=container_cluster_layout
    )

    box = widgets.VBox([container_cover, container_cluster_text,
                        container_cluster_numeric, fig,
                        valid, logs_box])
    display(box, out)
Esempio n. 11
0
def plot_interactive_mapper_graph(pipeline,
                                  data,
                                  layout="kamada_kawai",
                                  layout_dim=2,
                                  color_variable=None,
                                  node_color_statistic=None,
                                  clone_pipeline=True,
                                  color_by_columns_dropdown=False,
                                  n_sig_figs=3,
                                  node_scale=12,
                                  plotly_params=None):
    """Plotting function for interactive Mapper graphs.

    Provides functionality to interactively update parameters from the cover
    and clustering steps defined in `pipeline`. Nodes are colored according to
    `color_variable` and `node_color_statistic`. By default, the hovertext on
    each node displays a globally unique ID for the node, the number of data
    points associated with the node, and the summary statistic which determines
    its color.

    Parameters
    ----------
    pipeline : :class:`~gtda.mapper.pipeline.MapperPipeline` object
        Mapper pipeline to act on to data.

    data : array-like of shape (n_samples, n_features)
        Data used to generate the Mapper graph. Can be a pandas dataframe.

    layout : None, str or callable, optional, default: ``"kamada-kawai"``
        Layout algorithm for the graph. Can be any accepted value for the
        ``layout`` parameter in the :meth:`layout` method of
        :class:`igraph.Graph`. [1]_

    layout_dim : int, default: ``2``
        The number of dimensions for the layout. Can be 2 or 3.

    color_variable : object or None, optional, default: ``None``
        Specifies a feature of interest to be used, together with
        `node_color_statistic`, to determine node colors.

            1. If a numpy array or pandas dataframe, it must have the same
               length as `data`.
            2. ``None`` is equivalent to passing `data`.
            3. If an object implementing :meth:`transform` or
               :meth:`fit_transform`, it is applied to `data` to generate the
               feature of interest.
            4. If an index or string, or list of indices/strings, it is
               equivalent to selecting a column or subset of columns from
               `data`.

    node_color_statistic : callable or None, optional, default: ``None``
        If a callable, node colors will be computed as summary statistics from
        the feature array ``Y`` determined by `color_variable` – specifically,
        the color of a node representing the entries of `data` whose row
        indices are in ``I`` will be ``node_color_statistic(Y[I])``. ``None``
        is equivalent to passing :func:`numpy.mean`.

    color_by_columns_dropdown : bool, optional, default: ``False``
        If ``True``, a dropdown widget is generated which allows the user to
        color Mapper nodes according to any column in `data` (still using
        `node_color_statistic`) in addition to `color_variable`.

    clone_pipeline : bool, optional, default: ``True``
        If ``True``, the input `pipeline` is cloned before computing the
        Mapper graph to prevent unexpected side effects from in-place
        parameter updates.

    n_sig_figs : int or None, optional, default: ``3``
       If not ``None``, number of significant figures to which to round node
       summary statistics. If ``None``, no rounding is performed.

    node_scale : int or float, optional, default: ``12``
        Sets the scale factor used to determine the rendered size of the
        nodes. Increase for larger nodes. Implements a formula in the
        `Plotly documentation \
        <plotly.com/python/bubble-charts/#scaling-the-size-of-bubble-charts>`_.

    plotly_params : dict or None, optional, default: ``None``
        Custom parameters to configure the plotly figure. Allowed keys are
        ``"node_trace"``, ``"edge_trace"`` and ``"layout"``, and the
        corresponding values should be dictionaries containing keyword
        arguments as would be fed to the :meth:`update_traces` and
        :meth:`update_layout` methods of :class:`plotly.graph_objects.Figure`.

    Returns
    -------
    box : :class:`ipywidgets.VBox` object
        A box containing the following widgets: parameters of the clustering
        algorithm, parameters for the covering scheme, a Mapper graph arising
        from those parameters, a validation box, and logs.

    See also
    --------
    :func:`~gtda.mapper.visualization.plot_static_mapper_graph`,
    :func:`~gtda.mapper.pipeline.make_mapper_pipeline`

    References
    ----------
    .. [1] `igraph.Graph.layout
            <https://igraph.org/python/doc/igraph.Graph-class.html#layout>`_
            documentation.

    """

    # Clone pipeline to avoid side effects from in-place parameter changes
    _pipeline = clone(pipeline) if clone_pipeline else pipeline

    _node_color_statistic = node_color_statistic or np.mean

    def get_widgets_per_param(param, value):
        if isinstance(value, float):
            return (param,
                    widgets.FloatText(value=value,
                                      step=0.05,
                                      description=param.split("__")[1],
                                      continuous_update=False,
                                      disabled=False))
        elif isinstance(value, int):
            return (param,
                    widgets.IntText(value=value,
                                    step=1,
                                    description=param.split("__")[1],
                                    continuous_update=False,
                                    disabled=False))
        elif isinstance(value, str):
            return (param,
                    widgets.Text(value=value,
                                 description=param.split("__")[1],
                                 continuous_update=False,
                                 disabled=False))
        else:
            return None

    def on_parameter_change(change):
        handler.clear_logs()
        try:
            for param, value in cover_params.items():
                if isinstance(value, (int, float, str)):
                    _pipeline.set_params(
                        **{param: cover_params_widgets[param].value})
            for param, value in cluster_params.items():
                if isinstance(value, (int, float, str)):
                    _pipeline.set_params(
                        **{param: cluster_params_widgets[param].value})

            logger.info("Updating figure...")
            with fig.batch_update():
                (edge_trace, node_trace, node_elements,
                 node_colors_color_variable) = _calculate_graph_data(
                     _pipeline, data, is_data_dataframe, layout, layout_dim,
                     color_variable, _node_color_statistic, n_sig_figs,
                     node_scale)
                if colorscale_for_hoverlabel is not None:
                    node_colors_color_variable = np.asarray(
                        node_colors_color_variable)
                    min_col = np.min(node_colors_color_variable)
                    max_col = np.max(node_colors_color_variable)
                    hoverlabel_bgcolor = _get_colors_for_vals(
                        node_colors_color_variable, min_col, max_col,
                        colorscale_for_hoverlabel)
                    fig.update_traces(hoverlabel_bgcolor=hoverlabel_bgcolor,
                                      selector={"name": "node_trace"})

                fig.update_traces(x=node_trace.x,
                                  y=node_trace.y,
                                  marker_color=node_trace.marker.color,
                                  marker_size=node_trace.marker.size,
                                  marker_sizeref=node_trace.marker.sizeref,
                                  hovertext=node_trace.hovertext,
                                  **({
                                      "z": node_trace.z
                                  } if layout_dim == 3 else dict()),
                                  selector={"name": "node_trace"})
                fig.update_traces(x=edge_trace.x,
                                  y=edge_trace.y,
                                  **({
                                      "z": edge_trace.z
                                  } if layout_dim == 3 else dict()),
                                  selector={"name": "edge_trace"})

                # Update color by column buttons
                if color_by_columns_dropdown:
                    hovertext_color_variable = node_trace.hovertext
                    column_color_buttons = _get_column_color_buttons(
                        data, is_data_dataframe, node_elements,
                        node_colors_color_variable, _node_color_statistic,
                        hovertext_color_variable, colorscale_for_hoverlabel,
                        n_sig_figs)
                    # Avoid recomputing hoverlabel bgcolor for top button
                    if colorscale_for_hoverlabel is not None:
                        column_color_buttons[0]["args"][0][
                            "hoverlabel.bgcolor"] = [None, hoverlabel_bgcolor]
                else:
                    column_color_buttons = None

                button_height = 1.1
                fig.update_layout(updatemenus=[
                    go.layout.Updatemenu(buttons=column_color_buttons,
                                         direction="down",
                                         pad={
                                             "r": 10,
                                             "t": 10
                                         },
                                         showactive=True,
                                         x=0.11,
                                         xanchor="left",
                                         y=button_height,
                                         yanchor="top"),
                ])

            valid.value = True
        except Exception:
            exception_data = traceback.format_exc().splitlines()
            logger.exception(exception_data[-1])
            valid.value = False

    def observe_widgets(params, widgets):
        for param, value in params.items():
            if isinstance(value, (int, float, str)):
                widgets[param].observe(on_parameter_change, names="value")

    # Define output widget to capture logs
    out = widgets.Output()

    @out.capture()
    def click_box(change):
        if logs_box.value:
            out.clear_output()
            handler.show_logs()
        else:
            out.clear_output()

    # Initialise logging
    logger = logging.getLogger(__name__)
    handler = OutputWidgetHandler()
    handler.setFormatter(
        logging.Formatter("%(asctime)s - [%(levelname)s] %(message)s"))
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)

    # Initialise cover and cluster dictionaries of parameters and widgets
    cover_params = dict(
        filter(lambda x: x[0].startswith("cover"),
               _pipeline.get_mapper_params().items()))
    cover_params_widgets = dict(
        filter(None,
               map(lambda x: get_widgets_per_param(*x), cover_params.items())))
    cluster_params = dict(
        filter(lambda x: x[0].startswith("clusterer"),
               _pipeline.get_mapper_params().items()))
    cluster_params_widgets = dict(
        filter(
            None,
            map(lambda x: get_widgets_per_param(*x), cluster_params.items())))

    # Initialise widgets for validating input parameters of pipeline
    valid = widgets.Valid(
        value=True,
        description="Valid parameters",
        style={"description_width": "100px"},
    )

    # Initialise widget for showing the logs
    logs_box = widgets.Checkbox(description="Show logs: ",
                                value=False,
                                indent=False)

    # Initialise figure with initial pipeline and config
    fig = plot_static_mapper_graph(
        _pipeline,
        data,
        layout=layout,
        layout_dim=layout_dim,
        color_variable=color_variable,
        node_color_statistic=_node_color_statistic,
        color_by_columns_dropdown=color_by_columns_dropdown,
        clone_pipeline=False,
        n_sig_figs=n_sig_figs,
        node_scale=node_scale,
        plotly_params=plotly_params)

    # Store variables for later updates
    is_data_dataframe = hasattr(data, "columns")

    colorscale_for_hoverlabel = None
    if layout_dim == 3:
        # In plot_static_mapper_graph, hoverlabel bgcolors are set to white if
        # something goes wrong computing them according to the colorscale.
        is_bgcolor_not_white = fig.data[1].hoverlabel.bgcolor != "white"
        user_hoverlabel_bgcolor = False
        if plotly_params:
            if "node_trace" in plotly_params:
                if "hoverlabel_bgcolor" in plotly_params["node_trace"]:
                    user_hoverlabel_bgcolor = True
        if is_bgcolor_not_white and not user_hoverlabel_bgcolor:
            colorscale_for_hoverlabel = fig.data[1].marker.colorscale

    observe_widgets(cover_params, cover_params_widgets)
    observe_widgets(cluster_params, cluster_params_widgets)

    logs_box.observe(click_box, names="value")

    # Define containers for input widgets
    container_cover = widgets.HBox(
        children=list(cover_params_widgets.values()))

    container_cluster_layout = Layout(display="flex", flex_flow="row wrap")

    container_cluster = widgets.HBox(children=list(
        cluster_params_widgets.values()),
                                     layout=container_cluster_layout)

    box = widgets.VBox(
        [container_cover, container_cluster, fig, valid, logs_box, out])
    return box
Esempio n. 12
0
 def addValidMark(self, name, value):
     self.pushControl(name, widgets.Valid(
         value=value,
         description=name,
     ))