I am using Bokeh 1.0.1. I am unable to update the data source in the Update method i.e src.data.update(new_src.data) doesn't seem to work. Below is the full code.
def modify_doc(doc):
def create_dataset(df, resample='D'):
# Resample the data
src = df.resample(resample).mean()
# Reset index for hovering
src.reset_index(inplace=True)
return ColumnDataSource(src)
def create_plot(src):
# Blank plot with correct labels
p = figure(plot_width=700, plot_height=300, x_axis_type="datetime",
title = 'Variation of Pollution',
x_axis_label = 'Time', y_axis_label = 'Pollution (µg/m³)')
p.line(source=src, x='Date & Time', y='pm2.5', line_width=2,
color='firebrick', line_alpha=0.5, legend='Actual')
hover = HoverTool(tooltips=[('Pollution', '#{pm2.5} µg/m³'),
('Air Temp', '#{Air Temp.} °C'),
('Temp', '(#{Min. Temp.}{0.2f}, #{Max. Temp.}{0.2f}) °C'),
('Dew Pt.', '#{Dew Pt.} °C'),
('Rainfall', '#Rainfall mm'),
('Wind Dir.', '#{Wind Dir.} °'),
('Wind Speed', '#{Wind Speed} km/hr'),
('Relative humidity', '(#{Min. RH}{0.2f}, #{Max. RH}{0.2f}) %')],
mode='vline')
p.add_tools(hover)
p.legend.click_policy = 'hide'
return p
# Update function takes three default parameters
def update(attr, old, new):
# Resampling list
re_list = ['D', 'W', 'M', 'A']
# Make a new dataset based on the selected carriers and the
# make_dataset function defined earlier
new_src = create_dataset(df,
resample = re_list[resample_button_group.active])
# Update the source used the quad glpyhs
src.data.update(new_src.data)
resample_button_group = RadioButtonGroup(labels=["Day", "Week", "Month", "Year"], active=1)
resample_button_group.on_change('active', update)
controls = WidgetBox(resample_button_group)
# Initial Plot
src = create_dataset(df)
p = create_plot(src.data)
layout = row(controls, p)
doc.add_root(layout)
# Set up an application
handler = FunctionHandler(modify_doc)
app = Application(handler)
You should be able to update the line glyph directly.
First, modify your plotting code to assign a name to the line glyph:
pm_line = p.line(
source=src,
x='Date & Time',
y='pm2.5',
line_width=2,
color='firebrick',
line_alpha=0.5,
legend='Actual',
name='pm_line' # Add this!
)
Then in your update function, replace your existing update line with the following:
pm_line = p.select_one({'name':'pm_line'})
pm_line.data_source.data = new_src.data
Related
I am new to Python and have limited coding experience, so any input and advice is deeply appreciated.
I have created a dynamic choropleth map which includes a scatter_geo plot that overlays the relevant areas.
I am trying create a hover callback so that when I hover over one of these points, a dataframe appears that is indexed according to the point id (the first column in the defined dataframe). Essentially, it is a choropleth map equivalent of this example: https://plotly.com/python/v3/cars-exploration/ but without using FigureWidget.
I keep getting stuck on the hover callback function; no dataframe displays when I hover. Below is the code I have so far.
license_df1 = pd.read_excel(lic, "Primary Holdings by License", dtype = "str").fillna('')
license_df2 = pd.read_excel(lic, "Secondary Holdings by License", dtype = "str").fillna('')
### CREATE PLOTTING FEATURES ###
app = dash.Dash(__name__, suppress_callback_exceptions = True)
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = html.Div([
html.P("Spectrum Band:"), # Create Toggle Items between spectrum bands
dcc.RadioItems(id = "Band", options=[{'value': x, 'label':x} for x in df1_band], value = df1_band[0]),
dcc.Graph(id = "choropleth"),
dash_table.DataTable(id = "table")])
#app.callback(
Output("choropleth", "figure"),
[Input("Band", "value")])
def build_graph(value):
if value == '600 MHz':
df1_600 = df1[(df1["Band"] == "600 MHz")]
fig1 = px.choropleth(df1_600, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_data = {'PEA # ': False}, scope = "usa")
# Overlay Geographic Scatter Plot for interactive functionality
fig1b = px.scatter_geo(df1_600, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_name = 'Market', scope = "usa")
fig1.add_trace(fig1b.data[0])
fig1.update_traces(showlegend = False)
fig1.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
return fig1
elif value == '3.7 GHz':
df1_3700 = df1[(df1["Band"] == "3.7 GHz")]
fig2 = px.choropleth(df1_3700, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_data = {'PEA # ': False}, scope = "usa")
# Overlay Geographic Scatter Plot for interactive functionality
fig2b = px.scatter_geo(df1_3700, geojson = PEAs, featureidkey = "properties.PEA_Num",
locations = 'PEA # ', hover_name = 'Market', scope = "usa")
fig2.add_trace(fig2b.data[0])
fig2.update_traces(showlegend = False)
fig2.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
return fig2
#app.callback(
Output("table", "data"),
[Input("fig1", "hover_data")]) # WHERE I AM HAVING TROUBLE
def disp_license1(hover_data):
table_vals = license_df1.iloc[points.point_inds[0]].to_frame().to_html()
return table_vals
app.run_server(debug = True)
I am having a Pandas DataFrame with a TimeSeries Index. The idea is to use the bokeh server in order to have a DateSlider. This means the values that I pass over the function are dates. However, I think my problem is the data is not updated when I am running the bokeh server. The Slider appears but the data is not updating while moving the cursor from the Slider. I would like to have your view. This is my code:
#The file was already imported and cleaned as DF: trt_file
day_one = str(min(trt_file.index)) #Checking point file
day_last = str(max(trt_file.index)) #Checking point file
# Creating the data source which will be selected from the whole file
source = ColumnDataSource(data={
"x" : trt_file.loc[day_one]["Depth [m]"],
"y" : trt_file.loc[day_one]["Temperature"],
"Temperature" : trt_file.loc[day_one]["Temperature"],
"Depth" : trt_file.loc[day_one]["Depth [m]"]
})
#Minimum values for axis generation
xmin, xmax = min(trt_file["Depth [m]"]), max(trt_file["Depth [m]"])
ymin, ymax = min(trt_file["Temperature"]), max(trt_file["Temperature"])
#Set the figure fopr the initial draw
p = figure(title="2018-07-09", x_axis_label="Depth [m]", y_axis_label="Temperature", plot_height=300, plot_width=1500, x_range=(xmin, xmax), y_range=(ymin, ymax),tools=[HoverTool(tooltips=[("Temperature", "#Temperature"), ("Depth [m]", "#Depth")])] )
#Draw the values from the source we defined first
p.circle(x="x", y="y", fill_alpha=0.5, source=source)
#Set the axis names
p.xaxis.axis_label = "Kable lenght [m]"
p.yaxis.axis_label = "Temperature [C]"
#Creating the DateTime for the Slider
day_one_dt = (min(trt_file.index)) #Checking point file
day_last_dt = (max(trt_file.index))
#Slider
slider = DateSlider(start=day_one, end=day_last, value=day_one, step=1, title="TRT Day")
#Creating the bokeh interactive figure
def update_plot(attr, old, new): #attribute to change, old_val, new_val
#set day for the slider value
day_start = (str(slider.value)) #Value from the slider
new_data = {
"x" : trt_file.iloc[day_start]["Depth [m]"],
"y" : trt_file.iloc[day_start]["Temperature"],
"Temperature": trt_file.iloc[day_start]["Temperature"],
"Depth" : trt_file.iloc[day_start]["Depth [m]"]
}
source.trt_file = new_data
p.title.text = "Day TRT"
slider.on_change("value", update_plot)
layout = row(widgetbox(slider), p)
curdoc().title = "Temp"
curdoc().add_root(layout)
Bokeh knows nothing at all about a "trt file" so setting source.trt_file is not something Bokeh will notice or do anything with. The property Bokeh knows about and responds to is data:
source.data = new_data
I'm new to Bokeh and not sure how to get my plot data and how to update it.
My code is following these instructions:
https://github.com/WillKoehrsen/Bokeh-Python-Visualization/blob/master/interactive/exploration/interactive_development.ipynb
But unfurtunately the update method doesn't seem to work for me. i was trying to find documentation regarding this method but couldn't find any. can anyone assist?
basically i've generated a pandas dataframe from my data and converted it to ColumnDataSource. now i want to add or subtract data from it but the new ColumnDataSource does not update the old one.
Any help would be most appreciated! This is my code as of now, it still won't update properly:
def update(attr, old, new):
stations_to_plot = [int(station_selection.labels[i]) for i in station_selection.active]
by_station = pd.DataFrame(data=no_bikes.loc[stations_to_plot,:'23 PM'].values,index=list(map(str,initial_stations))
,columns=no_bikes.loc[:,:'23 PM'].columns.tolist())
new_src = ColumnDataSource(by_station.T)
r.data_source.data.update(new_src.data)
stations=list(map(str,no_bikes.loc[:,'station_avg'].nlargest(10).index.tolist()))
station_selection = CheckboxGroup(labels=stations, active = [0,1,3])
station_selection.on_change('active', update)
initial_stations = [int(station_selection.labels[i]) for i in station_selection.active]
range_select = RangeSlider(start = 0, end = 23, value = (0, 23),step = 1, title = 'Hours to plot')
range_select.on_change('value', update)
by_station = pd.DataFrame(data=no_bikes.loc[initial_stations,:'23 PM'].values,index=list(map(str,initial_stations))
,columns=no_bikes.loc[:,:'23 PM'].columns.tolist())
src = ColumnDataSource(by_station.T)
p = figure(plot_width = 500, plot_height = 500, title = 'Chances not to find bikes',
x_axis_label = 'Hour of the day', y_axis_label = 'Proportion',x_range=src.data['index'])
for i,station in enumerate(src.data.keys()):
if station in list(map(str, initial_stations)):
r=p.line(x='index',y=station,source =src, legend='Station N.'+station, color=Category20_16[i], line_width=5)
controls = WidgetBox(station_selection,range_select)
layout = row(controls, p)
curdoc().add_root(layout)
I'm trying to create slider that as you drag the slider, the portion of the graph that is shown is only what is on the slider. For example, if you look at my graph below, if the slider was set to 1990, you would only see the lines from 1990 to 2016. I found a working example with plotly but I wanted to see if it could be done with Bokeh.
This is my code so far:
p = figure(width = 900, height = 450)
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Aggregated Number of Degrees in Education'
source = ColumnDataSource(df)
fill_source = ColumnDataSource(data=dict(x=[],y=[]))
# Create objects for each line that will be plotted
stem = p.line('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
stem = p.circle('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
sped = p.line('year', 'sped', line_color='#fdb462', line_width=3, source=source)
elem = p.line('year', 'elem', line_color='#bebada', line_width=3, source=source)
elem = p.square('year', 'elem', line_color='#bebada', line_width=3, source=source)
other = p.line('year', 'other', line_color='#fb8072', line_width=4, source=source)
aggtotal = p.line('year', 'aggtotal', line_dash=[4,4,], line_color='#80b1d3', line_width=3, source=source)
yaxis = p.select(dict(type=Axis, layout="left"))[0]
yaxis.formatter.use_scientific = False
legend = Legend(items=[("STEM", [stem])
,("SPED" , [sped])
,("Elementary", [elem])
,("Other", [other])
,("Total Education Graduates", [aggtotal])], location=(0, 0))
p.add_tools(HoverTool(tooltips=[("Date", "#year")]))
p.add_layout(legend, 'right')
callback_test = CustomJS(args=dict(source=source,fill_source=fill_source), code="""
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (i = 0; i < s_val; i++) {
fill_data['y'][i].push(data['y'][i]);
fill_data['x'][i].push(data['x'][i]);
}
fill_source.trigger('change');
""")
sped_slider = Slider(start=1984, end= 2016, value=1, step=1,title="Year",callback=callback_test)
callback_test.args["sped"] = sped_slider
layout = row(p,widgetbox(sped_slider))
This renders a slider but it doesn't do anything and I'm not sure where to go from here.
There are some issues with your callback code. For example:
you loop i from 0 to s_val (which may be 1990) which is not consistent with the length of your arrays.
your glyphs references columns 'stem', etc... but the fill_source has columns 'x' and 'y'
your glyphs reference source as a source but you change and trigger event on fill_source.
All that could probably be fixed but there's a much easier way, adjust the range in the callback. E.g. replace your callback by this:
x_range = p.x_range
callback_test = CustomJS(args=dict(x_range=x_range), code="""
var start = cb_obj.value;
x_range.start = start;
x_range.change.emit();
""")
Note the change to the event trigger. Your version would work but I think it's going to be deprecated.
Also:
this line callback_test.args["sped"] = sped_slider is not necessary
you could add toolbar_location='above' in figure(...) to avoid rendering conflict with the legend
you're still going to have a layout problem between the slider and the legend which can be fixed in different ways (slider under or put the slider and the legend in a column before adding to the right of the plot, etc...)
I have the following bokeh code how do you add a horizontal line or glyph to the chart dynamically do I need a callback?
In the documentation http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html it says "Custom callbacks like these can be set using a CustomJS object and passing it as the callback argument to a Widget object."
So do I set the call back before or after the update...
source = ColumnDataSource(dict(
time=[], average=[], low=[], high=[], open=[], close=[],
ma=[], macd=[], macd9=[], macdh=[], color=[]
))
#main chart
p = figure(plot_height=500, tools="xpan,xwheel_zoom,xbox_zoom,reset,crosshair,hover", x_axis_type=None, y_axis_location="right")
p.x_range.follow = "end"
p.x_range.follow_interval = 100
p.x_range.range_padding = 0
p.axis.minor_tick_in = -2
p.axis.minor_tick_out = 5
p.segment(x0='time', y0='low', x1='time', y1='high', line_width=2, color='black', source=source)
p.segment(x0='time', y0='open', x1='time', y1='close', line_width=8, color='color', source=source)
...
#count()
def update(t):
open, high, low, close, average = _get_prices(t)
color = "green" if open < close else "red"
new_data = dict(
time=[t],
open=[open],
high=[high],
low=[low],
close=[close],
color=[color],
)
print(open)
source.stream(new_data, 300)
output_notebook()
curdoc().add_periodic_callback(update, 50)
curdoc().title = "OHLC"