3

I have a dataframe of multiple columns. First two columns are x and y coordinates and the rest columns are different property values for (x,y) pairs.

import pandas as pd
import numpy as np
df = pd.DataFrame()
df['x'] = np.random.randint(1,1000,100)
df['y'] = np.random.randint(1,1000,100)
df['val1'] = np.random.randint(1,1000,100)
df['val2'] = np.random.randint(1,1000,100)
df['val3'] = np.random.randint(1,1000,100)

print df.head()

     x    y  val1  val2  val3
0  337  794   449   969   933
1   19  563   592   677   886
2  512  467   664   160    16
3   36  112    91   230   910
4  972  572   336   879   860

Using customJS in Bokeh, I would like to change the value of color in 2-D heatmap by providing a drop down menu.

from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.models import LinearColorMapper
from bokeh.palettes import RdYlBu11 as palette

p = figure()
source =   ColumnDataSource(df)
color_mapper = LinearColorMapper(palette=palette)
p.patches('x', 'y', source=source,\
          fill_color={'field': 'val1', 'transform':color_mapper})

show(p)

The above commands plot a color map whose color is determined by the column 'val1'. I would like to plot different columns (either val1, val2, or val3) based on whatever is selected in the drop down menu.

I can create a drop down widget in bokeh by doing

 from bokeh.models.widgets import Select
 select = Select(title="Option:", value="val1", options=["val1","val2","val3"])

But, I'm not quite sure how I can use the selected value to update the plot by using callback.

Could someone give me a guideline here?

Thanks.

user4279562
  • 669
  • 12
  • 25

1 Answers1

7

I have included an example with comments inline with the code. The main important steps are to write the javascript code that is executed each time the selected option on the widget changes. The code simply needs to just re-assign which of the columns is set to the values for the 'y' column of the datasource.

The other issue is that your data is just x and y points. The patches glyph will require a different data structure which defines the boundaries of the patches. I believe there are better ways to make a heatmap in bokeh, there should be numerous examples on stack overflow and the bokeh docs.

import pandas as pd
import numpy as np

from bokeh.io import show
from bokeh.layouts import widgetbox,row
from bokeh.models import ColumnDataSource, CustomJS

df = pd.DataFrame()
df['x'] = np.random.randint(1,1000,1000)
df['y'] = np.random.randint(1,1000,1000)
df['val1'] = np.random.randint(1,1000,1000)
df['val2'] = np.random.randint(1,1000,1000)
df['val3'] = np.random.randint(1,1000,1000)

from bokeh.plotting import figure
from bokeh.models import LinearColorMapper
from bokeh.palettes import RdYlBu11 as palette

p = figure(x_range=(0,1000),y_range=(0,1000))
source = ColumnDataSource(df)
source_orig = ColumnDataSource(df)
color_mapper = LinearColorMapper(palette=palette)
p.rect('x', 'y', source=source,width=4,height=4,
          color={'field': 'val1', 'transform':color_mapper})

from bokeh.models.widgets import Select
select = Select(title="Option:", value="val1", options=["val1","val2","val3"])

callback = CustomJS(args={'source':source},code="""
        // print the selectd value of the select widget - 
        // this is printed in the browser console.
        // cb_obj is the callback object, in this case the select 
        // widget. cb_obj.value is the selected value.
        console.log(' changed selected option', cb_obj.value);

        // create a new variable for the data of the column data source
        // this is linked to the plot
        var data = source.data;

        // allocate the selected column to the field for the y values
        data['y'] = data[cb_obj.value];

        // register the change - this is required to process the change in 
        // the y values
        source.change.emit();
""")

# Add the callback to the select widget. 
# This executes each time the selected option changes
select.callback = callback
show(row(p,select))
Anthonydouc
  • 3,334
  • 1
  • 16
  • 29
  • Thanks for the quick answer. Could you explain why you update 'y'? Isn't that y-coordinate value in the data frame df? – user4279562 Sep 18 '17 at 14:52
  • In your question you stated you wanted to plot different columns, so I just updated y as a demonstration. You can obviously update either x or y. If your intention is to update the column which changes the colorscale, I would create a seperate column, and then change the values of that in the callback if that makes sense – Anthonydouc Sep 18 '17 at 14:57
  • I see. That makes sense. I was trying to update the field parameter value, but just creating a separate column in source and keep the structure as it is can be a way. Thank you. This really helps. – user4279562 Sep 18 '17 at 15:12
  • No problem, please mark the answer as correct if that has achieved what you need. – Anthonydouc Sep 18 '17 at 15:16
  • What does `source_orig = ColumnDataSource(df)` do in your code? I removed the line, but it doesn't seem to change the output? – Josmoor98 Oct 25 '19 at 16:56
  • @Anthonydouc Hi man, your code works well, one issue that I cant figure it out how to fix, the fact that the column that I select to allocate the y axis cant be called again. Do you know why? – ReinholdN Jun 29 '20 at 13:37
  • I figured it out. I just duplicated the column I wanted to allocated the start of the plot and didnt pass it to Select options. – ReinholdN Jun 29 '20 at 15:40