I am trying to write the selected datapoints from a Bokeh plot. The idea is to access ColumnDataSource
selected
property to get the selected data points whenever the Button
is clicked.
Below goes a model of the functionality I'm trying to achieve.
Expectation: after clicking the 'Selected points' button, a file /tmp/datapoints.json
would be created containing the list of points selected (if any).
Reality: no /tmp/datapoints.json
.
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.io import show
from bokeh.models import ColumnDataSource, Button
from bokeh.layouts import column
# setup plot
fig = figure(title='Select points',
plot_width=300, plot_height=200)
import numpy as np
x = np.linspace(0,10,100)
y = np.random.random(100) + x
import pandas as pd
data = pd.DataFrame(dict(x=x, y=y))
# define data source
src = ColumnDataSource(data)
# define plot
fig.circle(x='x', y='y', source=src)
# define interaction
def print_datapoints(attr, old, new):
with open('/tmp/datapoints.json', 'w') as f:
import json
json.dump(src.selected, f)
btn = Button(label='Selected points', button_type='success')
btn.on_click(print_datapoints)
curdoc().add_root(column(btn,fig))
What am I missing?
Answer
The problem you're facing is related to how the selection data is stored in Bokeh's ColumnDataSource
and how you're trying to access it. The selected
property of the ColumnDataSource
doesn't directly contain the data points themselves but rather information about the selected indices and geometry. In your code, you're trying to write the src.selected
object directly to a file, which will not give you the expected result since it contains more than just the data points.
Solution:
You need to extract the actual data points (x and y values) from the selected
indices and then save them to the file. You can do this by accessing the indices of the selected points, and then using these indices to retrieve the corresponding x
and y
values from the ColumnDataSource
.
Here’s an updated version of your code that works as expected:
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Button
from bokeh.layouts import column
import numpy as np
import pandas as pd
import json
# Setup plot
fig = figure(title='Select points',
plot_width=300, plot_height=200)
x = np.linspace(0, 10, 100)
y = np.random.random(100) + x
# Create the data
data = pd.DataFrame(dict(x=x, y=y))
# Define data source
src = ColumnDataSource(data)
# Define plot
fig.circle(x='x', y='y', source=src)
# Define interaction
def print_datapoints(attr, old, new):
# Extract selected indices
selected_indices = src.selected.indices
# Get the x and y values of the selected points
selected_data = {
'x': [src.data['x'][i] for i in selected_indices],
'y': [src.data['y'][i] for i in selected_indices],
}
# Write selected points to a JSON file
with open('/tmp/datapoints.json', 'w') as f:
json.dump(selected_data, f)
print(f"Selected points saved to /tmp/datapoints.json")
# Create button to trigger selection logging
btn = Button(label='Selected points', button_type='success')
btn.on_click(print_datapoints)
# Add button and plot to the document
curdoc().add_root(column(btn, fig))
Key Changes:
-
Accessing Selected Indices:
The selection is stored insrc.selected.indices
, which gives you a list of indices of the selected data points. These indices correspond to positions in yourColumnDataSource
's data arrays (x
andy
). -
Extracting the Data:
We use the selected indices to extract the correspondingx
andy
values from theColumnDataSource
usingsrc.data['x']
andsrc.data['y']
. -
Saving to JSON:
After extracting thex
andy
values of the selected points, we save them as a dictionary in a JSON file (/tmp/datapoints.json
).
What this does:
- When you select data points on the Bokeh plot and click the "Selected points" button, the selected points (with their
x
andy
values) will be saved to the/tmp/datapoints.json
file in JSON format.
Now, when you interact with the plot, you'll get the expected output in /tmp/datapoints.json
, and it should look something like this (depending on the selected points):
{
"x": [1.2, 3.5, 5.8],
"y": [2.3, 4.5, 6.7]
}