Esempio n. 1
0
def link_plot(adata, key, genes=None, basis=['umap', 'pca'], components=[1, 2],
             subsample=None, steps=[40, 40], sample_size=500,
             distance=2, cutoff=True, highlight_only=None, palette=None,
             show_legend=False, legend_loc='top_right', plot_width=None, plot_height=None, save=None):
    """
    Display the distances of cells from currently highlighted cell.

    Params
    --------
    adata: AnnData
        annotated data object
    key: str 
        key in `adata.obs_keys()` to color the static plot
    genes: list(str), optional (default: `None`)
        list of genes in `adata.var_names`,
        which are used to compute the distance;
        if None, take all the genes
    basis: list(str), optional (default:`['umap', 'pca']`)
        list of basis to use when plotting;
        only the first plot is hoverable
    components: list(int); list(list(int)), optional (default: `[1, 2]`)
        list of components for each basis
    subsample: str, optional (default: `None`)
        subsample strategy to use when there are too many cells
        possible values are: `"density"`, `"uniform"`, `None`
    steps: int; list(int), optional (default: `[40, 40]`)
        number of steps in each direction when using `subsample="uniform"`
    sample_size: int, optional (default: `500`)
        number of cells to sample based on their density in the respective embedding
        when using `subsample="density"`; should be < `1000`
    distance: int; str, optional (default: `2`)
        for integers, use p-norm,
        for strings, only `'dpt'` is available
    cutoff: bool, optional (default: `True`)
        if `True`, do not color cells whose distance is further away
        than the threshold specified by the slider
    highlight_only: 'str', optional (default: `None`)
        key in `adata.obs_keys()`, which makes highlighting
        work only on clusters specified by this parameter
    palette: matplotlib.colors.Colormap; list(str), optional (default: `None`)
        colormap to use, if None, use plt.cm.RdYlBu 
    show_legend: bool, optional (default: `False`)
        display the legend also in the linked plot
    legend_loc: str, optional (default `'top_right'`)
        location of the legend
    seed: int, optional (default: `None`)
        seed when `subsample='density'`
    plot_width: int, optional (default: `None`)
        width of the plot
    plot_height: int, optional (default: `None`)
        height of the plot
    save: Union[os.PathLike, Str, NoneType], optional (default: `None`)
        path where to save the plot

    Returns
    --------
    None
    """

    assert key in adata.obs.keys(), f'`{key}` not found in `adata.obs`.'

    if subsample == 'uniform':
        adata, _ = sample_unif(adata, steps, basis[0])
    elif subsample == 'density':
        adata, _ = sample_density(adata, sample_size, basis[0], seed=seed)
    elif subsample is not None:
        raise ValueError(f'Unknown subsample strategy: `{subsample}`.')

    palette = cm.RdYlBu if palette is None else palette
    if isinstance(palette, matplotlib.colors.Colormap):
        palette = to_hex_palette(palette(range(palette.N), 1., bytes=True))

    if not isinstance(components[0], list):
        components = [components]

    if len(components) != len(basis):
        assert len(basis) % len(components) == 0 and len(basis) >= len(components)
        components = components * (len(basis) // len(components))

    if not isinstance(components, np.ndarray):
        components = np.asarray(components)

    if highlight_only is not None:
        assert highlight_only in adata.obs_keys(), f'`{highlight_only}` is not in adata.obs_keys().'

    genes = adata.var_names if genes is None else genes 
    gene_subset = np.in1d(adata.var_names, genes)

    if distance != 'dpt':
        d = adata.X[:, gene_subset]
        if issparse(d):
            d = d.A
        dmat = distance_matrix(d, d, p=distance)
    else:
        if not all(gene_subset):
            warnings.warn('`genes` is not None, are you sure this is what you want when using `dpt` distance?')

        dmat = []
        ad_tmp = adata.copy()
        ad_tmp = ad_tmp[:, gene_subset]
        for i in range(ad_tmp.n_obs):
            ad_tmp.uns['iroot'] = i
            sc.tl.dpt(ad_tmp)
            dmat.append(list(ad_tmp.obs['dpt_pseudotime'].replace([np.nan, np.inf], [0, 1])))

    dmat = pd.DataFrame(dmat, columns=list(map(str, range(adata.n_obs))))
    df = pd.concat([pd.DataFrame(adata.obsm[f'X_{bs}'][:, comp - (bs != 'diffmap')], columns=[f'x{i}', f'y{i}'])
                    for i, (bs, comp) in enumerate(zip(basis, components))] + [dmat], axis=1)
    df['hl_color'] = np.nan
    df['index'] = range(len(df))
    df['hl_key'] = list(adata.obs[highlight_only]) if highlight_only is not None else 0
    df[key] = list(map(str, adata.obs[key]))

    start_ix = '0'  # our root cell
    ds = ColumnDataSource(df)
    mapper = linear_cmap(field_name='hl_color', palette=palette,
                         low=df[start_ix].min(), high=df[start_ix].max())
    static_fig_mapper = _create_mapper(adata, key)

    static_figs = []
    figs, renderers = [], []
    for i, bs in enumerate(basis):
        # linked plots
        fig = figure(tools='pan, reset, save, ' + ('zoom_in, zoom_out' if i == 0 else 'wheel_zoom'),
                     title=bs, plot_width=400, plot_height=400)
        _set_plot_wh(fig, plot_width, plot_height)

        kwargs = {}
        if show_legend and legend_loc is not None:
            kwargs['legend_group'] = 'hl_key' if highlight_only is not None else key

        scatter = fig.scatter(f'x{i}', f'y{i}', source=ds, line_color=mapper, color=mapper,
                              hover_color='black', size=8, line_width=8, line_alpha=0, **kwargs)
        if show_legend and legend_loc is not None:
            fig.legend.location = legend_loc

        figs.append(fig)
        renderers.append(scatter)
    
        # static plots
        fig = figure(title=bs, plot_width=400, plot_height=400)

        fig.scatter(f'x{i}', f'y{i}', source=ds, size=8,
                    color={'field': key, 'transform': static_fig_mapper}, **kwargs)

        if legend_loc is not None:
            fig.legend.location = legend_loc
    
        static_figs.append(fig)

    fig = figs[0]

    end = dmat[~np.isinf(dmat)].max().max() if distance != 'dpt' else 1.0
    slider = Slider(start=0, end=end, value=end / 2, step=end / 1000,
                    title='Distance ' + '(dpt)' if distance == 'dpt' else f'({distance}-norm)')
    col_ds = ColumnDataSource(dict(value=[start_ix]))
    update_color_code = f'''
        source.data['hl_color'] = source.data[first].map(
            (x, i) => {{ return isNaN(x) ||
                        {'x > slider.value || ' if cutoff else ''}
                        source.data['hl_key'][first] != source.data['hl_key'][i]  ? NaN : x; }}
        );
    '''
    slider.callback = CustomJS(args={'slider': slider, 'mapper': mapper['transform'], 'source': ds, 'col': col_ds}, code=f'''
        mapper.high = slider.value;
        var first = col.data['value'];
        {update_color_code}
        source.change.emit();
    ''')

    h_tool = HoverTool(renderers=renderers, tooltips=[], show_arrow=False)
    h_tool.callback = CustomJS(args=dict(source=ds, slider=slider, col=col_ds), code=f'''
        var indices = cb_data.index['1d'].indices;
        if (indices.length == 0) {{
            source.data['hl_color'] = source.data['hl_color'];
        }} else {{
            var first = indices[0];
            source.data['hl_color'] = source.data[first];
            {update_color_code}
            col.data['value'] = first;
            col.change.emit();
        }}
        source.change.emit();
    ''')
    fig.add_tools(h_tool)

    color_bar = ColorBar(color_mapper=mapper['transform'], width=12, location=(0,0))
    fig.add_layout(color_bar, 'left')

    fig.add_tools(h_tool)
    plot = column(slider, row(*static_figs), row(*figs))

    if save is not None:
        save = save if str(save).endswith('.html') else str(save) + '.html'
        bokeh_save(plot, save)
    else:
        show(plot)
Esempio n. 2
0
    plot.select_one('save_confirmation').update(text=_text)

    if g.BOKEH_DEV:
        print "data_save:", data_save, "\n"
        print "req.content:", req.content[:20], "\n"


def clear_message(attr, old, new):
    plot.select_one('save_confirmation').update(text="")


source.on_change('data', clear_message)

hover.callback = CustomJS(args=dict(cb=checkbox),
                          code="""
        if(!cb.active.includes(%d)) {
            document.getElementsByClassName('bk-tooltip')[%d].style.display = 'none';
        }
    """ % (1, 0))

checkbox.callback = CustomJS(args=dict(
    p0=circle,
    p1=line_regr,
    p2=circle_fit,
),
                             code="""
        //Toggle glyph visibility based on checkbox status
        p0.visible = cb_obj.active.includes(0);
        p1.visible = cb_obj.active.includes(1);
        p2.visible = cb_obj.active.includes(2);
    """)
Esempio n. 3
0
    def _init_figure(self):
        '''
        Initializes the figure
        '''
        ftype = self.get_type()
        if ftype == FigureType.IND:
            aspectratio = self._scheme.ind_aspectratio
        elif ftype == FigureType.OBS:
            aspectratio = self._scheme.obs_aspectratio
        elif ftype == FigureType.VOL:
            aspectratio = self._scheme.vol_aspectratio
        elif ftype == FigureType.DATA:
            aspectratio = self._scheme.data_aspectratio
        else:
            raise Exception(f'Unknown type "{ftype}"')

        f = figure(
            width=1000,
            tools=Figure._tools,
            x_axis_type='linear',
            # backend webgl removed due to this bug:
            # https://github.com/bokeh/bokeh/issues/7568
            # also line styles do not work with webgl
            # output_backend='webgl',
            aspect_ratio=aspectratio)

        f.y_range.range_padding = self._scheme.y_range_padding
        # remove any spacing if there is no title, so there is no spacing
        # between plots
        if not self._scheme.plot_title:
            f.min_border_bottom = 0
            f.min_border_top = 0

        f.border_fill_color = convert_color(self._scheme.border_fill)

        f.xaxis.axis_line_color = convert_color(self._scheme.axis_line_color)
        f.yaxis.axis_line_color = convert_color(self._scheme.axis_line_color)
        f.xaxis.minor_tick_line_color = convert_color(
            self._scheme.tick_line_color)
        f.yaxis.minor_tick_line_color = convert_color(
            self._scheme.tick_line_color)
        f.xaxis.major_tick_line_color = convert_color(
            self._scheme.tick_line_color)
        f.yaxis.major_tick_line_color = convert_color(
            self._scheme.tick_line_color)

        f.xaxis.major_label_text_color = convert_color(
            self._scheme.axis_label_text_color)
        f.yaxis.major_label_text_color = convert_color(
            self._scheme.axis_label_text_color)

        f.xgrid.grid_line_color = convert_color(self._scheme.grid_line_color)
        f.ygrid.grid_line_color = convert_color(self._scheme.grid_line_color)
        f.title.text_color = convert_color(self._scheme.plot_title_text_color)

        f.left[0].formatter.use_scientific = False
        f.background_fill_color = convert_color(self._scheme.background_fill)

        # mechanism for proper date axis without gaps, thanks!
        # https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/t3HkalO4TGA
        formatter_code = pkgutil.get_data(
            __name__, 'templates/js/tick_formatter.js').decode()
        f.xaxis.formatter = FuncTickFormatter(args=dict(
            axis=f.xaxis[0],
            formatter=DatetimeTickFormatter(
                microseconds=['%fus'],
                milliseconds=['%3Nms', '%S.%3Ns'],
                seconds=[self._scheme.axis_tickformat_seconds],
                minsec=[self._scheme.axis_tickformat_minsec],
                minutes=[self._scheme.axis_tickformat_minutes],
                hourmin=[self._scheme.axis_tickformat_hourmin],
                hours=[self._scheme.axis_tickformat_hours],
                days=[self._scheme.axis_tickformat_days],
                months=[self._scheme.axis_tickformat_months],
                years=[self._scheme.axis_tickformat_years]),
            source=self._fp.cds,
        ),
                                              code=formatter_code)

        hover_code = pkgutil.get_data(
            __name__, 'templates/js/hover_tooltips.js').decode()
        h = HoverTool(
            tooltips=[('Time',
                       f'@datetime{{{self._scheme.hovertool_timeformat}}}')],
            mode='vline',
            formatters={'@datetime': 'datetime'},
        )
        callback = CustomJS(args=dict(source=self.cds, hover=h),
                            code=hover_code)
        h.callback = callback
        f.tools.append(h)
        self._hover = h

        # set figure
        self.figure = f
Esempio n. 4
0
    for (var i = 0, len = tooltips.length; i < len; i ++) {
        tooltips[i].style.top = ""; // unset what bokeh.js sets
        tooltips[i].style.right = "";
        tooltips[i].style.bottom = "";
        tooltips[i].style.left = "";
        tooltips[i].style.bottom = "0px";
        tooltips[i].style.right = "-150px";
    }
    """)

# bind widget callbacks
color_class_select.on_change('value', update_color_class)
toggle_class_select.on_change('value', update_toggle_class)
class_toggle_multi_select.on_change('value', update_class_selection)
file_select_dropdown.on_click(file_select_handler)
hover_tip_tool.callback = tooltip_fix_callback

update_toggle_class('value', '', features_list[0])

control_panel = column([
    file_select_dropdown, color_class_select, toggle_class_select,
    class_toggle_multi_select
],
                       sizing_mode="stretch_height",
                       width=200)
p.add_tools(wheel_zoom_tool)
p.add_tools(hover_tip_tool)
p.toolbar.active_scroll = wheel_zoom_tool
curdoc().add_root(row(control_panel, p, sizing_mode='stretch_both'))
'''
filelist_refresh_button = Button(label="Refresh data list", button_type="success")