Get selected point labels from embedded Bokeh graph in Flask

Hi Everyone,

I have successfully embedded a Bokeh script in my Flask framework (based on the wonderful example) but am looking to add some functionality via forms and URL-args.

Specifically, I want to be able to select points to be “removed” from the dataset. Right now I have achieved this by having a form field which requires manual input of sample names, then submit the names (along with other form data) as URL.

What I would really like, however, is this form field to updated automatically, such that selecting points will send their names as URL-args back to the server (where they can be removed).

Does anyone know how this would be accomplished? Perhaps through a JS updated form (or is this what Callbacks are for?)

Thank you!

And here is the source for my view. It really is just a viewer for principal component analyses.

Cheers,

-daniel

def flask_pca(filename=None):
    # Get all the form arguments
    if filename is None:
        args = flask.request.args
        remove = getitem(args, 'remove', '')
        filename = getitem(args, 'filename', '')
    else:
        remove = ''

    if len(remove) > 1:
        remove = remove.split(';')
        remove = [item for item in remove if item != u'']

    # load our data from a saved file
    file_path_name = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], filename)
    df = pd.read_csv(file_path_name, index_col=0)
    # generate a boolean index to keep items we don't want to remove
    bool_key = [item not in remove for item in df.index]
    df = df.loc[bool_key, :]
    data = df.values

    #PCA calculations
    sklearn_pca = sklearnPCA()
    Y_sklearn = sklearn_pca.fit_transform(data)

    hover = HoverTool(
        tooltips=[
            ("Sample Name", "@desc")

        ]
    )

    TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,previewsave,box_select"

    source = ColumnDataSource(
        data=dict(
            x=Y_sklearn[:, 0],
            y=Y_sklearn[:, 1],
            desc=list(df.index)
        )
    )

    fig = figure(title="PCA", tools=[TOOLS, hover])
    fig.circle('x', 'y', source=source, fill_alpha=0.2, size=10)

    js_resources = INLINE.render_js()
    css_resources = INLINE.render_css()
    script, div = components(fig, INLINE)

    remove_str = ""
    for item in remove:
        remove_str += item + ";"

    html = render_template(
        'embed.html',
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources,
        filename=filename,
        remove=remove_str
    )
    return html

Seems to me like you’d have a lot easier time if
you just used bokeh server. Any reason you’re not?

···

On 7/19/16 1:45 PM, Daniel Hitchcock
wrote:

Hi Everyone,

      I have successfully embedded a Bokeh script in my Flask

framework (based on the wonderful example) but am looking to
add some functionality via forms and URL-args.

      Specifically, I want to be able to select points to be

“removed” from the dataset. Right now I have achieved this by
having a form field which requires manual input of sample
names, then submit the names (along with other form data) as
URL.

      What I would really like, however, is this form field to

updated automatically, such that selecting points will send
their names as URL-args back to the server (where they can be
removed).

      Does anyone know how this would be accomplished? Perhaps

through a JS updated form (or is this what Callbacks are for?)

Thank you!

      And here is the source for my view. It really is just a

viewer for principal component analyses.

Cheers,

-daniel

def flask_pca(filename=None    ):
# Get all the form arguments
    if filename is None        :
args = flask.request.args
remove = getitem(args, 'remove', ''        )
filename = getitem(args, 'filename', ''    )
else        :
remove = ''

    if len(remove) > 1        :
remove = remove.split(';'        )
remove = [item for item in remove if item != u''
    ]
# load our data from a saved file
    file_path_name = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'],     filename)
df = pd.read_csv(file_path_name, index_col=0    )
# generate a boolean index to keep items we don't want to remove
    bool_key = [item not in remove for item in     df.index]
df = df.loc[bool_key,     :]
data = df.values
#PCA calculations
        sklearn_pca = sklearnPCA()
Y_sklearn = sklearn_pca.fit_transform(data)
hover = HoverTool(
tooltips            =[
("Sample Name", "@desc"
        )
]
)
TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,previewsave,box_select"

            source = ColumnDataSource(
data=dict            (
x=Y_sklearn[:, 0],
            y=Y_sklearn[:, 1],
            desc=list        (df.index)
)
)
fig = figure(title="PCA", tools=[TOOLS,     hover])
fig.circle('x', 'y', source=source, fill_alpha=0.2, size=10
    )
js_resources = INLINE.render_js()
css_resources = INLINE.render_css()
script, div = components(fig,
    INLINE)
remove_str = ""
    for item in         remove:
remove_str += item + ";"

            html = render_template(
'embed.html',
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources,
        filename=filename,
        remove    =remove_str
)
return html

  You received this message because you are subscribed to the Google

Groups “Bokeh Discussion - Public” group.

  To unsubscribe from this group and stop receiving emails from it,

send an email to [email protected].

  To post to this group, send email to [email protected].

  To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io?utm_medium=email&utm_source=footer).

  For more options, visit [https://groups.google.com/a/continuum.io/d/optout](https://groups.google.com/a/continuum.io/d/optout).


Sarah Bird
Developer, Bokeh

    [
      ![Continuum Analytics](http://docs.continuum.io/_static/img/ContinuumWordmark.png)
    ](http://continuum.io)

Hi Sarah,

Thanks for your response-- and that might be the case.

What’s prevented me setting up a Bokeh server thus far is that I am hoping to integrate the chart into my existing apache/flask framework, which is mounted on an RHEL virtual machine. My ‘website’ is actually a bunch of tools (python/R scripts) for metabolomics data processing in our research group. Most of my labmates have no scripting experience (and I got tired of running scripts for them) thus I constructed a flask web interface for them to upload their datasets, have it automatically processed with their selected options, and download the processed data. If you would like to see the source or an example, let me know.

In one script I am working on, I was hoping to incorporate a manual QC step of the data. In this step, the user would upload their dataset, select points on the Bokeh Chart which they wish to remove from the dataset, and then have the data processed without those data points. This can be accomplished if I can parse the selected points to a form field.

One of the appeals of the Bokeh chart was how easy it was to integrate with what I have; it took less than an hour to learn the basics and to follow the example for embedding it. I’m not a web developer (hardly a computational biologist tbh), so learning a new API (tornado/bokeh server), porting all my scripts and troubleshooting it is a daunting task for me. Unless there is an easier way?

Please let me know your thoughts.

Cheers,

-daniel

···

On Wednesday, July 20, 2016 at 7:38:05 PM UTC-4, Sarah Bird wrote:

Seems to me like you’d have a lot easier time if
you just used bokeh server. Any reason you’re not?

  On 7/19/16 1:45 PM, Daniel Hitchcock > wrote:

Hi Everyone,

      I have successfully embedded a Bokeh script in my Flask

framework (based on the wonderful example) but am looking to
add some functionality via forms and URL-args.

      Specifically, I want to be able to select points to be

“removed” from the dataset. Right now I have achieved this by
having a form field which requires manual input of sample
names, then submit the names (along with other form data) as
URL.

      What I would really like, however, is this form field to

updated automatically, such that selecting points will send
their names as URL-args back to the server (where they can be
removed).

      Does anyone know how this would be accomplished? Perhaps

through a JS updated form (or is this what Callbacks are for?)

Thank you!

      And here is the source for my view. It really is just a

viewer for principal component analyses.

Cheers,

-daniel

def flask_pca(filename=None    ):
# Get all the form arguments
    if filename is None        :
args = flask.request.args
remove = getitem(args, 'remove', ''        )
filename = getitem(args, 'filename', ''    )
else        :
remove = ''

    if len(remove) > 1        :
remove = remove.split(';'        )
remove = [item for item in remove if item != u''
    ]
# load our data from a saved file
    file_path_name = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'],     filename)
df = pd.read_csv(file_path_name, index_col=0    )
# generate a boolean index to keep items we don't want to remove
    bool_key = [item not in remove for item in     df.index]
df = df.loc[bool_key,     :]
data = df.values
#PCA calculations
        sklearn_pca = sklearnPCA()
Y_sklearn = sklearn_pca.fit_transform(
    data)
hover = HoverTool(
tooltips            =[
("Sample Name", "@desc"
        )
]
)
TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,previewsave,box_select"

            source = ColumnDataSource(
data=dict            (
x=Y_sklearn[:, 0],
            y=Y_sklearn[:, 1],
            desc=list        (df.index)
)
)
fig = figure(title="PCA", tools=[TOOLS,     hover])
fig.circle('x', 'y', source=source, fill_alpha=0.2, size=10
    )
js_resources = INLINE.render_js()
css_resources = INLINE.render_css()
script, div = components(fig,
    INLINE)
remove_str = ""
    for item in         remove:
remove_str += item + ";"

            html = render_template(
'embed.html',
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources,
        filename=filename,
        remove    =remove_str
)
return html

  You received this message because you are subscribed to the Google

Groups “Bokeh Discussion - Public” group.

  To unsubscribe from this group and stop receiving emails from it,

send an email to [email protected].

  To post to this group, send email to [email protected].

  To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io?utm_medium=email&utm_source=footer).

  For more options, visit [https://groups.google.com/a/continuum.io/d/optout](https://groups.google.com/a/continuum.io/d/optout).


Sarah Bird
Developer, Bokeh

    [
      <img alt="Continuum Analytics" src="https://lh6.googleusercontent.com/proxy/VYgVjggTk1hCXSN9wFkffE3I6kxTvJ51tT4KvDXOuKbs1WyFG66k7kt2-vkDimbyxfWtP-d1paJmstMYhPPnDYSUF4rLPoYM2GM2QFM=w5000-h5000" height="30px" width="150px">
    ](http://continuum.io)

Hi Daniel,

I’m also trying to do QC with a flask-bokeh app.

As a contingency, you could have some partial success to this topic by using a DataTable.

In the table you could setup a new column to add a quality flag, and by using the editable property of DataTable, plus the editor in that column, mark the values you want to avoid.

Something like:

flags=[‘good’,‘bad’]
columns=[
TableColumn(field=‘param’,title=‘param’),

TableColumn(field=‘param_flag’,title=‘param_flag’, editor=SelectEditor(options=flags))

]
dt = DataTable(source=self.source, columns=columns, fit_columns=True, sortable=False, selectable=True, editable=True)

``

Right now, the main problem is that DataTables are only editables by using 0.11.1, as there is a reported issue with 0.12. Another drawback is that you have to modify the values one-by-one in the table, i.e. you can not modify the values of all selected points in a graph in batch.

···

El jueves, 21 de julio de 2016, 15:48:34 (UTC+2), Daniel Hitchcock escribió:

Hi Sarah,

Thanks for your response-- and that might be the case.

What’s prevented me setting up a Bokeh server thus far is that I am hoping to integrate the chart into my existing apache/flask framework, which is mounted on an RHEL virtual machine. My ‘website’ is actually a bunch of tools (python/R scripts) for metabolomics data processing in our research group. Most of my labmates have no scripting experience (and I got tired of running scripts for them) thus I constructed a flask web interface for them to upload their datasets, have it automatically processed with their selected options, and download the processed data. If you would like to see the source or an example, let me know.

In one script I am working on, I was hoping to incorporate a manual QC step of the data. In this step, the user would upload their dataset, select points on the Bokeh Chart which they wish to remove from the dataset, and then have the data processed without those data points. This can be accomplished if I can parse the selected points to a form field.

One of the appeals of the Bokeh chart was how easy it was to integrate with what I have; it took less than an hour to learn the basics and to follow the example for embedding it. I’m not a web developer (hardly a computational biologist tbh), so learning a new API (tornado/bokeh server), porting all my scripts and troubleshooting it is a daunting task for me. Unless there is an easier way?

Please let me know your thoughts.

Cheers,

-daniel

On Wednesday, July 20, 2016 at 7:38:05 PM UTC-4, Sarah Bird wrote:

Seems to me like you’d have a lot easier time if
you just used bokeh server. Any reason you’re not?

  On 7/19/16 1:45 PM, Daniel Hitchcock > > wrote:

Hi Everyone,

      I have successfully embedded a Bokeh script in my Flask

framework (based on the wonderful example) but am looking to
add some functionality via forms and URL-args.

      Specifically, I want to be able to select points to be

“removed” from the dataset. Right now I have achieved this by
having a form field which requires manual input of sample
names, then submit the names (along with other form data) as
URL.

      What I would really like, however, is this form field to

updated automatically, such that selecting points will send
their names as URL-args back to the server (where they can be
removed).

      Does anyone know how this would be accomplished? Perhaps

through a JS updated form (or is this what Callbacks are for?)

Thank you!

      And here is the source for my view. It really is just a

viewer for principal component analyses.

Cheers,

-daniel

def flask_pca(filename=None    ):
# Get all the form arguments
    if filename is None        :
args = flask.request.args
remove = getitem(args, 'remove', ''        )
filename = getitem(args, 'filename', ''    )
else        :
remove = ''

    if len(remove) > 1        :
remove = remove.split(';'        )
remove = [item for item in remove if item != u''
    ]
# load our data from a saved file
    file_path_name = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'],     filename)
df = pd.read_csv(file_path_name, index_col=0    )
# generate a boolean index to keep items we don't want to remove
    bool_key = [item not in remove for item in     df.index]
df = df.loc[bool_key,     :]
data = df.values
#PCA calculations
        sklearn_pca = sklearnPCA()
Y_sklearn = sklearn_pca.fit_transform(
    data)
hover = HoverTool(
tooltips            =[
("Sample Name", "@desc"
        )
]
)
TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,previewsave,box_select"

            source = ColumnDataSource(
data=dict            (
x=Y_sklearn[:, 0],
            y=Y_sklearn[:, 1],
            desc=list        (df.index)
)
)
fig = figure(title="PCA", tools=[TOOLS,     hover])
fig.circle('x', 'y', source=source, fill_alpha=0.2, size=10
    )
js_resources = INLINE.render_js()
css_resources = INLINE.render_css()
script, div = components(fig,
    INLINE)
remove_str = ""
    for item in         remove:
remove_str += item + ";"

            html = render_template(
'embed.html',
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources,
        filename=filename,
        remove    =remove_str
)
return html

  You received this message because you are subscribed to the Google

Groups “Bokeh Discussion - Public” group.

  To unsubscribe from this group and stop receiving emails from it,

send an email to [email protected].

  To post to this group, send email to [email protected].

  To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io?utm_medium=email&utm_source=footer).

  For more options, visit [https://groups.google.com/a/continuum.io/d/optout](https://groups.google.com/a/continuum.io/d/optout).


Sarah Bird
Developer, Bokeh

    [
      <img alt="Continuum Analytics" src="https://lh6.googleusercontent.com/proxy/VYgVjggTk1hCXSN9wFkffE3I6kxTvJ51tT4KvDXOuKbs1WyFG66k7kt2-vkDimbyxfWtP-d1paJmstMYhPPnDYSUF4rLPoYM2GM2QFM=w5000-h5000" height="30px" width="150px">
    ](http://continuum.io)

Hi Avelo,
Thanks for the suggestion. And in fact, using the table and index number might be more intuitive than actually selecting the data.

Here’s a screenshot of the chart in its current incarnation. (Now I just need to figure out how to have them on the same line…)

I think what I would like to see with Bokeh charts would be a bit more interaction with form data, and maybe a small Javascript cookbook to use this input to modify the dataset.

Cheers,

-daniel

···

On Friday, July 22, 2016 at 3:02:35 AM UTC-4, avelo wrote:

Hi Daniel,

I’m also trying to do QC with a flask-bokeh app.

As a contingency, you could have some partial success to this topic by using a DataTable.

In the table you could setup a new column to add a quality flag, and by using the editable property of DataTable, plus the editor in that column, mark the values you want to avoid.

Something like:

flags=[‘good’,‘bad’]
columns=[
TableColumn(field=‘param’,title=‘param’),

TableColumn(field=‘param_flag’,title=‘param_flag’, editor=SelectEditor(options=flags))

]
dt = DataTable(source=self.source, columns=columns, fit_columns=True, sortable=False, selectable=True, editable=True)

``

Right now, the main problem is that DataTables are only editables by using 0.11.1, as there is a reported issue with 0.12. Another drawback is that you have to modify the values one-by-one in the table, i.e. you can not modify the values of all selected points in a graph in batch.

El jueves, 21 de julio de 2016, 15:48:34 (UTC+2), Daniel Hitchcock escribió:

Hi Sarah,

Thanks for your response-- and that might be the case.

What’s prevented me setting up a Bokeh server thus far is that I am hoping to integrate the chart into my existing apache/flask framework, which is mounted on an RHEL virtual machine. My ‘website’ is actually a bunch of tools (python/R scripts) for metabolomics data processing in our research group. Most of my labmates have no scripting experience (and I got tired of running scripts for them) thus I constructed a flask web interface for them to upload their datasets, have it automatically processed with their selected options, and download the processed data. If you would like to see the source or an example, let me know.

In one script I am working on, I was hoping to incorporate a manual QC step of the data. In this step, the user would upload their dataset, select points on the Bokeh Chart which they wish to remove from the dataset, and then have the data processed without those data points. This can be accomplished if I can parse the selected points to a form field.

One of the appeals of the Bokeh chart was how easy it was to integrate with what I have; it took less than an hour to learn the basics and to follow the example for embedding it. I’m not a web developer (hardly a computational biologist tbh), so learning a new API (tornado/bokeh server), porting all my scripts and troubleshooting it is a daunting task for me. Unless there is an easier way?

Please let me know your thoughts.

Cheers,

-daniel

On Wednesday, July 20, 2016 at 7:38:05 PM UTC-4, Sarah Bird wrote:

Seems to me like you’d have a lot easier time if
you just used bokeh server. Any reason you’re not?

  On 7/19/16 1:45 PM, Daniel Hitchcock > > > wrote:

Hi Everyone,

      I have successfully embedded a Bokeh script in my Flask

framework (based on the wonderful example) but am looking to
add some functionality via forms and URL-args.

      Specifically, I want to be able to select points to be

“removed” from the dataset. Right now I have achieved this by
having a form field which requires manual input of sample
names, then submit the names (along with other form data) as
URL.

      What I would really like, however, is this form field to

updated automatically, such that selecting points will send
their names as URL-args back to the server (where they can be
removed).

      Does anyone know how this would be accomplished? Perhaps

through a JS updated form (or is this what Callbacks are for?)

Thank you!

      And here is the source for my view. It really is just a

viewer for principal component analyses.

Cheers,

-daniel

def flask_pca(filename=None    ):
# Get all the form arguments
    if filename is None        :
args = flask.request.args
remove = getitem(args, 'remove', ''        )
filename = getitem(args, 'filename', ''    )
else        :
remove = ''

    if len(remove) > 1        :
remove = remove.split(';'        )
remove = [item for item in remove if item != u''
    ]
# load our data from a saved file
    file_path_name = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'],     filename)
df = pd.read_csv(file_path_name, index_col=0    )
# generate a boolean index to keep items we don't want to remove
    bool_key = [item not in remove for item in     df.index]
df = df.loc[bool_key,     :]
data = df.values
#PCA calculations
        sklearn_pca = sklearnPCA()
Y_sklearn = sklearn_pca.fit_transform(
    data)
hover = HoverTool(
tooltips            =[
("Sample Name", "@desc"
        )
]
)
TOOLS = "crosshair,pan,wheel_zoom,box_zoom,reset,previewsave,box_select"

            source = ColumnDataSource(
data=dict            (
x=Y_sklearn[:, 0],
            y=Y_sklearn[:, 1],
            desc=list        (df.index)
)
)
fig = figure(title="PCA", tools=[TOOLS,     hover])
fig.circle('x', 'y', source=source, fill_alpha=0.2, size=10
    )
js_resources = INLINE.render_js()
css_resources = INLINE.render_css()
script, div = components(fig,
    INLINE)
remove_str = ""
    for item in         remove:
remove_str += item + ";"

            html = render_template(
'embed.html',
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources,
        filename=filename,
        remove    =remove_str
)
return html

  You received this message because you are subscribed to the Google

Groups “Bokeh Discussion - Public” group.

  To unsubscribe from this group and stop receiving emails from it,

send an email to [email protected].

  To post to this group, send email to [email protected].

  To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/a3027c5b-6cab-4f5e-bf2a-870ed756ab07%40continuum.io?utm_medium=email&utm_source=footer).

  For more options, visit [https://groups.google.com/a/continuum.io/d/optout](https://groups.google.com/a/continuum.io/d/optout).


Sarah Bird
Developer, Bokeh

    [
      <img alt="Continuum Analytics" src="https://lh6.googleusercontent.com/proxy/VYgVjggTk1hCXSN9wFkffE3I6kxTvJ51tT4KvDXOuKbs1WyFG66k7kt2-vkDimbyxfWtP-d1paJmstMYhPPnDYSUF4rLPoYM2GM2QFM=w5000-h5000" height="30px" width="150px">
    ](http://continuum.io)