I'm attempting to put together a Jupyter Notebook that allows a user to input a state in a dropdown, which would change the result in the second dropdown to only show cities from that state. Once the city is selected, a button could be pressed to refresh the graph (I realize there's you can use manual with interact, but was unable to get this to function properly) and show the resultant data (covid cases over time). I've had some success making the widgets, but I can't get the data to plot correctly.
This is my first time using widgets in Jupyter and I'm a bit lost what with interact, interactivity, display, and observe (not to mention the deprecated on_trait_change). Here's what I have so far...
from ipywidgets import interact, widgets
from bqplot import pyplot as plt
import pandas as pd
#make example DF
date_list = ['2020-02-01','2020-02-01','2020-02-01','2020-02-01','2020-02-02','2020-02-02','2020-02-02','2020-02-02']
state_list = ['CA','NY','CA','NY','CA','NY','CA','NY']
city_list = ['San Fran','NYC','LA','Albany','San Fran','NYC','LA','Albany']
cases_list = [0,0,0,0,2,6,4,8]
df = pd.DataFrame(index=date_list)
df['state'] = state_list
df['city'] = city_list
df['cases'] = cases_list
#getting unique state and city list sorted in alphabetical order
state_unique = df['state'].unique()
state_unique.sort()
state = widgets.Dropdown(
options=['All'] + list(state_unique),
value='CA', #indicates default starting value
description='State:', #this is the label for the dropdown
)
city = widgets.Dropdown(
description='City:',
)
# function that updates 'city' dropdown depending on value in 'state' dropdown
def list_cities(x):
if state.value == 'All':
city.options = ['']
return city_list
else:
temp = ['All'] + sorted(df.loc[df['state'].eq(state.value),'city'].unique())
city.options = temp
state.observe(list_cities, names = 'value') #the names part tells the observe the change name to look for, this is only looking for value changes
b_refresh = widgets.Button(
description='Refresh',
icon='fa-refresh',
button_style='warning',
layout=widgets.Layout(width='100px')
)
# plotting function
def set_plot(x_data,y_data):
plt.plot(x_data, y_data)
plt.show()
x = list(df.index.unique())
y = df.loc[\
(df['city']==city.value)&\
(df['state']==state.value),'cases']
b_refresh.on_click(set_plot(x, y))
display (state,city,b_refresh)
from ipywidgets.widgets import Dropdown, Button
from ipywidgets.widgets import Layout, HBox, VBox
import bqplot as bq
import pandas as pd
#make example DF
date_list = ['2020-02-01','2020-02-01','2020-02-01','2020-02-01','2020-02-07','2020-02-07','2020-02-07','2020-02-07']
state_list = ['CA','NY','CA','NY','CA','NY','CA','NY']
city_list = ['San Fran','NYC','LA','Albany','San Fran','NYC','LA','Albany']
cases_list = [0,10,0,12,2,6,4,8]
df = pd.DataFrame(index=date_list)
df['state'] = state_list
df['city'] = city_list
df['cases'] = cases_list
df.index = pd.to_datetime(df.index)
df.rename_axis("date", axis='index', inplace=True)
#getting unique state list sorted in alphabetical order
state_unique = df['state'].unique()
state_unique.sort()
#make graphical parts - 2 dropdowns and refresh button
state = Dropdown(
options=['All'] + list(state_unique),
value='All', #indicates default starting value
description='State:', #this is the label for the dropdown
layout=Layout(width='200px',
margin = '0px 0px 0px 0px')
)
city = Dropdown(
description='City:',
layout=Layout(width='200px',
margin = '0px 0px 0px 0px')
)
b_refresh = Button(
description='Refresh',
icon='fa-refresh',
button_style='warning',
layout=Layout(width='100px',
margin = '0px 0px 0px 60px')
)
#make graph parts
x = list(df.index.unique())
y = list(df.groupby('date')['cases'].sum())
xs = bq.DateScale()
ys = bq.LinearScale(min=0)
line = bq.Lines(
x=x,
y=y,
scales={'x': xs, 'y': ys}
)
x_ax = bq.Axis(
scale=xs,
label='Dates',
grid_lines='solid',
tick_format='%m-%d',
tick_rotate=-0,
label_location ='middle',
label_offset = "40px"
)
y_ax = bq.Axis(
scale=ys,
orientation='vertical',
tick_format=',d',
label='FillInLater',
grid_lines='solid',
label_offset = "60px"
)
# function that updates 'city' dropdown depending on value in 'state' dropdown
def list_cities(x):
if state.value == 'All':
global y
city.options = ['']
y = list(df.groupby('date')['cases'].sum())
else:
temp = ['All'] + sorted(df.loc[df['state'].eq(state.value),'city'].unique())
city.options = temp
update_city(x)
# function that updates graph's y values depending on value in 'city' dropdown
def update_city(x):
global y
if city.value == 'All':
matching_cities = df.loc[(df['state']==state.value),'cases'].to_frame(name='cases')
y = list(matching_cities.groupby('date')['cases'].sum())
else:
y = list(df.loc[\
(df['city']==city.value)&\
(df['state']==state.value),'cases'])
# update plot function
def set_plot(b):
global y
line.y = [y]
#assign widget actions to functions
state.observe(list_cities, names = 'value') #the names part tells .observe the change name to look for, this is only looking for value changes
city.observe(update_city, names = 'value')
b_refresh.on_click(set_plot)
#display all
fig = bq.Figure(
layout=Layout(width='95%', height='400px', border ='solid 1px gray'),
axes=[x_ax, y_ax],
marks=[line],
fig_margin=dict(top=10, bottom=80, left=80, right=20),
animation_duration=500
)
box = HBox(
children=(state, city, b_refresh),
layout=Layout(margin = '10px 0px 10px 0px')
)
vert_box = VBox(
children=(box, fig),
layout=Layout(border='solid 1px gray', margin = '0 0 0 0')
)
display (vert_box)
Related
I created a form with two drop down menu's:
My goal is to make one dropdown dependent on the other dropdown.
This picture illustrates my goal and the current situation.
The sample code below can be run in Google Colab or Jupyter notebook to replicate the current situation.
##title
import ipywidgets as widgets
from ipywidgets import HBox, Label
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
import time
import pandas as pd
#Create DF
df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
df
# Layout
form_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between',
)
button_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='center',
padding = '5%'
)
# Independent dropdown item
drop_down_input = 'Dropdown_input_1'
drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
drop_down.observe(dropdown_handler, names='value')
# Dependent drop down
# Dependent drop down elements
dependent_drop_down_elements = {}
dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E']
# Define dependent drop down
dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
drop_down.observe(dropdown_handler, names='value')
# Button
button = widgets.Button(description='Add row to dataframe')
out = widgets.Output()
def on_button_clicked(b):
global df
button.description = 'Row added'
time.sleep(1)
with out:
new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
df = df.append(new_row, ignore_index=True)
button.description = 'Add row to dataframe'
out.clear_output()
display(df)
button.on_click(on_button_clicked)
# Form items
form_items = [
Box([Label(value='Independent dropdown'),
drop_down], layout=form_item_layout),
Box([Label(value='Dependent dropdown'),
dependent_drop_down], layout=form_item_layout)
]
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 1px',
align_items='stretch',
width='30%',
padding = '1%'
))
display(form)
display(out)
Is this possible? If so, can you explain how?
You can do this by setting a if statement in your dropdown_handler function that checks if the value chosen in your independent dropdown is Dropdown_input_1 or Dropdown_input2. Still in the dropdown_handler, you can then modify accordingly the options arguments to either dependent_drop_down_elements['Dropdown_input_1'] or dependent_drop_down_elements['Dropdown_input_2'].
See code below:
import ipywidgets as widgets
from ipywidgets import HBox, Label
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
import time
import pandas as pd
#Create DF
df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
df
# Layout
form_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between',
)
button_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='center',
padding = '5%'
)
# Independent dropdown item
drop_down_input = 'Dropdown_input_1'
drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))
# Dependent drop down
# Dependent drop down elements
dependent_drop_down_elements = {}
dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E']
# Define dependent drop down
dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
#If statement checking on dropdown value and changing options of the dependent dropdown accordingly
if change.new=='Dropdown_input_2':
dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_2']
elif change.new=='Dropdown_input_1':
dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_1']
drop_down.observe(dropdown_handler, names='value')
# Button
button = widgets.Button(description='Add row to dataframe')
out = widgets.Output()
def on_button_clicked(b):
global df
button.description = 'Row added'
time.sleep(1)
with out:
new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
df = df.append(new_row, ignore_index=True)
button.description = 'Add row to dataframe'
out.clear_output()
display(df)
button.on_click(on_button_clicked)
# Form items
form_items = [
Box([Label(value='Independent dropdown'),
drop_down], layout=form_item_layout),
Box([Label(value='Dependent dropdown'),
dependent_drop_down], layout=form_item_layout)
]
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 1px',
align_items='stretch',
width='30%',
padding = '1%'
))
display(form)
display(out)
As an example, this is what the output looks like when you pick Dropdown_input_2 on the first widget:
Plotly: How to display and filter a dataframe with multiple dropdowns?
dataset = https://community.tableau.com/s/question/0D54T00000CWeX8SAL/sample-superstore-sales-excelxls
Hey, I am new to plotly as well, Following up on this question. My case is similar but with extra conditions. I need to plot the total sales for every quarter in every region filtered by "year" and "categories". I was able to reproduce the line chart in the article. But failed to reproduce a bar chart. There are 4 quarters in a year and 4 regions. Thus, at least 16 bars must be present at all times.
This is what I am trying to build:
enter image description here
import plotly.graph_objs as go
import pandas as pd
import numpy as np
file = pd.read_excel(r"Sample - Superstore.xlsx")
sales = file[['Sales','Region', 'Order Date','Category', 'State']]
sales["Quarters"] = sales['Order Date'].apply(lambda x: x.quarter)
sales["Years"] = sales['Order Date'].apply(lambda x: x.year)
df = sales.groupby(['Years','Quarters', 'Region', 'Category'], as_index = False).sum()
df_input = df.copy()
years = df['Years'].unique().tolist()
categories = df['Category'].unique().tolist()
regions = df['Region'].unique().tolist()
quarters = df['Quarters'].unique().tolist()
dfs = {}
for year in years:
dfs[year]=pd.pivot_table(df[df['Years']==year],
values='Sales',
index=['Quarters','Region'],
columns=['Category'],
aggfunc=np.sum)
# find row and column unions
common_cols = []
common_rows = []
for df in dfs.keys():
common_cols = sorted(list(set().union(common_cols,list(dfs[df]))))
common_rows = sorted(list(set().union(common_rows,list(dfs[df].index))))
df_common = pd.DataFrame(np.nan, index=common_rows, columns=common_cols)
# reshape each dfs[df] into common dimensions
dfc={}
for df_item in dfs:
#print(dfs[unshaped])
df1 = dfs[df_item].copy()
s=df_common.combine_first(df1)
df_reshaped = df1.reindex_like(s)
dfc[df_item]=df_reshaped
# plotly start
fig = go.Figure()
# for year in all_years:
# df2 = group_sales.loc[group_sales["Years"] == year]
# all_quarters =list(sorted(set(df2["Quarters"].astype(str))))
# all_regions =list(sorted(set(df2["Region"].astype(str))))
# fig.add_trace(go.Bar(x= all_quarters, y=df2.loc[df2["Region"] == all_regions[0]]["Sales"], name=all_regions[0],marker_color='blue', visible=(year== default_year)))
# fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[1]]["Sales"], name=all_regions[1], marker_color='lightblue', visible=( year == default_year)))
# fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[2]]["Sales"],name= all_regions[2],marker_color='grey', visible=(year== default_year)))
# fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[3]]["Sales"],name=all_regions[3], marker_color='red', visible=(year== default_year)))
# year_plot_names.extend([year]*4)
print(common_cols)
for col in common_cols:
# fig.add_trace(go.Bar(x= all_quarters, y=df2.loc[df2["Region"] == all_regions[0]]["Sales"], name=all_regions[0],marker_color='blue', visible=(year== default_year)))
fig.add_trace(go.Bar(x= quarters, name= regions[0],marker_color='blue', visible= True))
fig.add_trace(go.Bar(x= quarters, name= regions[1],marker_color='lightblue', visible= True))
fig.add_trace(go.Bar(x= quarters, name= regions[2],marker_color='grey', visible= True))
fig.add_trace(go.Bar(x= quarters, name= regions[3],marker_color='red', visible= True))
# fig.add_trace(go.Bar(x= regions,marker_color='blue', visible= True))
# fig.add_trace(go.Scatter(x=regions,
# visible=True,
# marker=dict(size=12, line=dict(width=2)),
# marker_symbol = 'diamond',name=col
# )
# )
fig.show()
# menu setup
updatemenu= []
# buttons for menu 1, names
buttons=[]
# create traces for each color:
# build argVals for buttons and create buttons
for df in dfc.keys():
argList = []
for col in dfc[df]:
temp = []
j = 0
# for i in range(0,4):
# temp2 = []
# for i in range(0,4):
# temp2.append(dfc[df][col].values[j])
# j+=1
# temp.append(temp2)
# argList.append(temp)
print(dfc[df][col])
argList.append(dfc[df][col].values)
argVals = [ {'y':argList}]
buttons.append(dict(method='update',
label=df,
visible=True,
args=argVals))
print(buttons)
# buttons for menu 2, colors
b2_labels = common_cols
# matrix to feed all visible arguments for all traces
# so that they can be shown or hidden by choice
b2_show = [list(b) for b in [e==1 for e in np.eye(len(b2_labels))]]
buttons2=[]
buttons2.append({'method': 'update',
'label': 'All',
'args': [{'visible': [True]*len(common_cols)}]})
# create buttons to show or hide
for i in range(0, len(b2_labels)):
buttons2.append(dict(method='update',
label=b2_labels[i],
args=[{'visible':b2_show[i]}]
)
)
# add option for button two to hide all
buttons2.append(dict(method='update',
label='None',
args=[{'visible':[False]*len(common_cols)}]
)
)
# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
your_menu2=dict()
updatemenu.append(your_menu2)
updatemenu[1]
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
updatemenu[1]['buttons']=buttons2
updatemenu[1]['y']=0.6
fig.update_layout(showlegend=False, updatemenus=updatemenu)
fig.update_layout(yaxis=dict(range=[0,df_input['Sales'].max()+0.4]))
# title
fig.update_layout(
title=dict(
text= "<i>Filtering with multiple dropdown buttons</i>",
font={'size':18},
y=0.9,
x=0.5,
xanchor= 'center',
yanchor= 'top'))
# button annotations
fig.update_layout(
annotations=[
dict(text="<i>Year</i>", x=-0.4, xref="paper", y=1.1, yref="paper",
align="left", showarrow=False, font = dict(size=16, color = 'steelblue')),
dict(text="<i>Category</i>", x=-0.4, xref="paper", y=0.7, yref="paper",
align="left", showarrow=False, font = dict(size=16, color = 'steelblue')
)
])
fig.show()
I tried to do this with plotly buttons however getting two buttons to work together with a statically defined menu is challenging
it becomes very simple if dash is used with plotly as now there is callback capability that makes requirement simple.
import pandas as pd
import itertools
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# Load Data
dfraw = pd.read_excel("Sample - Superstore.xls")
df = (dfraw.loc[:,['Sales','Region', 'Order Date','Category', 'State']]
.assign(Quarters=dfraw["Order Date"].dt.quarter,
Years=dfraw["Order Date"].dt.year)
.groupby(['Years','Quarters', 'Region', 'Category'], as_index=False).sum()
)
# add in totals with category "All"
df = (pd.concat([df, df.groupby(["Years","Quarters","Region"], as_index=False).agg({"Sales":"sum"}).assign(Category="All")])
.set_index(['Years', 'Region', 'Category','Quarters'])
.unstack("Region")
.droplevel(0, axis=1)
)
# colors...
config = {'Central':"blue", 'East':"lightblue", 'South':"grey", 'West':"red"}
fig = go.Figure()
# create a trace for every year, category & region
for y,cat in itertools.product(df.index.get_level_values("Years").unique(),df.index.get_level_values("Category").unique()):
dff = df.loc[(y,cat)]
for region in dff.columns:
fig.add_trace(go.Bar(x=dff.index, y=dff[region], name=region, meta=f"{y}{cat}", marker_color=config[region],
visible=(y==2014 and cat=="All"),
text=dff[region].apply(lambda v: f"{v/10**3:.0f}k"),textposition="outside")
)
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
dcc.Graph(id='graph'),
html.Label(["Year",dcc.Dropdown(id='year-dropdown', clearable=False,
value='2014', options=[{'label': year, 'value': year}
for year in df.index.get_level_values("Years").unique()])
]),
html.Label([
"Category",dcc.Dropdown(id='cat-dropdown', clearable=False, value='All',
options=[{'label': year, 'value': year}
for year in df.index.get_level_values("Category").unique()])
]),
])
# Define callback to update graph
#app.callback(
Output('graph', 'figure'),
[Input("year-dropdown", "value"),Input("cat-dropdown", "value")]
)
def update_figure(year, cat):
return fig.update_traces(visible=False).update_traces(visible=True, selector={"meta":f"{year}{cat}"}).update_layout(title=f"{year} {cat}")
# Run app and display result inline in the notebook
app.run_server(mode='inline')
import pandas as pd
import numpy as np
from bokeh.io import show, output_notebook, push_notebook
from bokeh.plotting import figure
from bokeh.models import CategoricalColorMapper, HoverTool, ColumnDataSource, Panel
from bokeh.models.widgets import CheckboxGroup, Slider, RangeSlider, Tabs
from bokeh.layouts import column, row, WidgetBox
from bokeh.palettes import Category20_16
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
output_notebook()
def histogram_tab(webs):
def make_dataset(params_list, range_start = 0.0, range_end = 1, bin_width = 0.005):
#check to make sure the start is less than the end
assert range_start < range_end, "Start must be less than end!"
#by_params = pd.DataFrame(columns=[ ,'Max', 'Avarage', 'Min','color'])
by_params = pd.DataFrame(columns=[ 'left','right', 'proportion', 'p_proportion','p_interval', 'name', 'w_name','color'])
#
range_extent = range_end - range_start
values = ['Min', "Avarage", 'Max']
# Iterate through all the parameters
for i, para_name in enumerate(params_list):
#print para_name
# Subset to the parameter
subset = webs[para_name]
# note: subset have to be a list of values
# [webs.columns[i%6]]
# Create a histogram with specified bins and range
arr_hist, edges = np.histogram(subset,
bins = int(range_extent / bin_width),
range = [range_start, range_end])
# Divide the counts by the total to get a proportion and create df
arr_df= pd.DataFrame({'proportion': arr_hist ,
'left': edges[:-1], 'right': edges[1:]}) #/ np.sum(arr_hist)
# Format the proportion
arr_df['p_proportion'] = ['%0.00005f' % proportion for proportion in arr_df['proportion']]
# Format the interval
arr_df['p_interval'] = ['%d to %d scale' % (left, right) for left,
right in zip(arr_df['left'], arr_df['right'])]
# Assign the parameter for labels
arr_df['name'] = para_name
arr_df['w_name'] = webs['Site name']
# Color each parametr differently
arr_df['color'] = Category20_16[i]
# Add to the overall dataframe
by_params = by_params.append(arr_df)
# Overall dataframe
by_params = by_params.sort_values(['name','left'])
return ColumnDataSource(by_params)
def style(p):
# Title
p.title.align = 'center'
p.title.text_font_size ='20pt'
p.title.text_font = 'serif'
# Axis titles
p.xaxis.axis_label_text_font_size = '14pt'
p.xaxis.axis_label_text_font_style = 'bold'
p.yaxis.axis_label_text_font_size = '14pt'
p.yaxis.axis_label_text_font_style = 'bold'
# Tick labels
p.xaxis.major_label_text_font_size = '12pt'
p.yaxis.major_label_text_font_size = '12pt'
return p
def make_plot(src):
# Blank plot with correct labels
p = figure(plot_width = 700, plot_height = 700,
title = "Histogram of Parametes for the websites",
x_axis_label = 'parameters', y_axis_label = "values")
# Quad glyphs to create a histogram
p.quad(source=src, bottom =0,left = 'left', right = 'right', color ='color', top= 'proportion',fill_alpha = 0.7, hover_fill_color = 'color', legend = 'name',
hover_fill_alpha = 1.0, line_color = 'white') #top='proportion',
# Hover tool with vline mode
hover = HoverTool(tooltips=[('Parameter','#name'),
('Website','#w_name'),
('Proportion','p_proportion')
],
mode='vline')
p.add_tools(hover)
# Stypling
p = style(p)
return p
# Update function takes three default parameters
def update(attr, old, new):
# Get the list of parameter for the graph
parameter_to_plot = [para_selection.labels[i] for i in para_selection.active]
# Make a new dataset based on the selected parameter and the
# make_dataset function defined earlier
new_src = make_dataset(parameter_to_plot, range_start = 0, range_end = 1, bin_width = 0.005) # note range are not specified
# Convert dataframe to column data source
new_src = ColumnDataSource(new_src)
# Update the source used the quad glpyhs
src.data.update(new_src.data)
list_of_params = list(webs.columns[1:].unique())
list_of_params.sort()
para_selection = CheckboxGroup(labels=list_of_params, active = [0,1])
para_selection.on_change('active',update)
binwidth_select = Slider(start =0, end = 1,
step = 0.00025, value = 0.0005,
title = 'Change in parameter')
binwidth_select.on_change('value', update)
range_select = RangeSlider(start=0, end=1, value =(0,1),
step=0.00025, title = 'Change in range')
range_select.on_change('value', update)
initial_params = [para_selection.labels[i] for i in para_selection.active]
src = make_dataset(initial_params,
range_start = range_select.value[0],
range_end = range_select.value[1],
bin_width = binwidth_select.value)
p = make_plot(src)
#show(p)
# Put controls in a single element
controls = WidgetBox(para_selection, binwidth_select, range_select)
# Create a row layout
layout = row(controls, p)
# Make a tab with the layout
tab = Panel(child = layout, title = 'Histogram')
#return tab
tabs = Tabs(tabs=[tab])
webs.add_root(tabs)
# Set up an application
handler = FunctionHandler(histogram_tab(webs))
app = Application(handler)
add_root is a method on Document, you are trying to call it on a DataFrame called webs, apparently, which is why you get that message. The structure of a Bokeh app in a notebook should look like this:
# create a function to define the app, must accept "doc" as the parameter
def myfunc(doc):
# make Bokeh objects
# add stuff to doc
doc.add_root(stuff)
# pass the function, but *don't* execute it
handler = FunctionHandler(myfunc)
app = Application(handler)
Note that the last two lines are not necessary in recent version of Bokeh, you can just call:
show(myfunc)
directly. There is a full example in the repo:
https://github.com/bokeh/bokeh/blob/master/examples/howto/server_embed/notebook_embed.ipynb
Your code should be structured very similarly to that.
I have been trying to create a Geoscatter Plot with Plotly where the marker size should indicate the number of customers (row items) in one city (zip_city). I based my code on two templates from the Plotly documentation: United States Bubble Map and the aggregation part Mapping with Aggregates.
I managed to put together a code that does what I want, except for one drawback: when I hover over a bubble, I would like to see the name of the city plus number of customers (the result from the aggregation), so something like Aguadilla: 2. Can you help me on how to do this?
Here is my code (as a beginner with plotly, I am also open to code improvements):
import plotly.offline as pyo
import pandas as pd
df = pd.DataFrame.from_dict({'Customer': [111, 222, 555, 666],
'zip_city': ['Aguadilla', 'Aguadilla', 'Arecibo', 'Wrangell'],
'zip_latitude':[18.498987, 18.498987, 18.449732,56.409507],
'zip_longitude':[-67.13699,-67.13699,-66.69879,-132.33822]})
data = [dict(
type = 'scattergeo',
locationmode = 'USA-states',
lon = df['zip_longitude'],
lat = df['zip_latitude'],
text = df['Customer'],
marker = dict(
size = df['Customer'],
line = dict(width=0.5, color='rgb(40,40,40)'),
sizemode = 'area'
),
transforms = [dict(
type = 'aggregate',
groups = df['zip_city'],
aggregations = [dict(target = df['Customer'], func = 'count', enabled = True)]
)]
)]
layout = dict(title = 'Customers per US City')
fig = dict( data=data, layout=layout )
pyo.plot( fig, validate=False)
Update:
Can I access the result of the transforms argument directly in the data argument to show the number of customers per city?
You can create a list, that will contains what you want and then set text=list in data. Also do not forget specify hoverinfo='text'.
I am updated your code, so try this:
import pandas as pd
import plotly.offline as pyo
df = pd.DataFrame.from_dict({'Customer': [111, 222, 555, 666],
'zip_city': ['Aguadilla', 'Aguadilla', 'Arecibo', 'Wrangell'],
'zip_latitude':[18.498987, 18.498987, 18.449732,56.409507],
'zip_longitude':[-67.13699,-67.13699,-66.69879,-132.33822]})
customer = df['Customer'].tolist()
zipcity = df['zip_city'].tolist()
list = []
for i in range(len(customer)):
k = str(zipcity[i]) + ':' + str(customer[i])
list.append(k)
data = [dict(
type = 'scattergeo',
locationmode = 'USA-states',
lon = df['zip_longitude'],
lat = df['zip_latitude'],
text = list,
hoverinfo = 'text',
marker = dict(
size = df['Customer'],
line = dict(width=0.5, color='rgb(40,40,40)'),
sizemode = 'area'
),
transforms = [dict(
type = 'aggregate',
groups = df['zip_city'],
aggregations = [dict(target = df['Customer'], func = 'count', enabled = True)]
)]
)]
layout = dict(title = 'Customers per US City')
fig = dict(data=data, layout=layout)
pyo.plot(fig, validate=False)
Hi I'm fairly new to Python, Plotly and Jupyter Notebook. I would like to use a slider to select the number of days as the range in a query to which a graph is created from. My only issue is that I want the graph to automatically update on interaction with the slider, without having to re-run the query and graph creation. My code is below:
slider = widgets.IntSlider()
display(slider)
sliderVal = slider.value
df = pd.read_sql(f"""
SELECT CASE WHEN SiteID LIKE 3 THEN 'BLAH'
WHEN SiteID LIKE 4 THEN 'BLAHBLAH'
END AS Website,
COUNT(1) AS Count
FROM viewName
WHERE (TimeStamp > DATEADD(DAY, -{sliderVal}, GETDATE()))
GROUP BY SiteId
ORDER BY Count DESC
""", conn)
data = [go.Bar(x=df.Website, y=df.Count)]
layout = go.Layout(
xaxis=dict(
title='Website'),
yaxis=dict(
title='Exception count'),
title=f'Number of exceptions per user in the last {sliderVal} days')
chart = go.Figure(data=data, layout=layout, )
py.iplot(chart, filename='WebExceptions')
Thanks in advance!
If you do not want to rerun the query, then your data frame df must contain the results for all the values that you want the intslider widget to take, the function linked to the widget will then simply filter the data and redraw the graph with the new filtered data.
Here's an example with some dummy data:
import ipywidgets as widgets
import plotly.offline as py
import plotly.graph_objs as go
import pandas as pd
py.init_notebook_mode(connected = True)
# Dummy data, to be replaced with your query result for the range of sliderVal
df = pd.DataFrame({'Days': [1] * 3 + [2] * 4 + [3] * 5,
'Website': [1,2,3, 4,5,6,7, 8,9,10,11,12],
'Count': [10,5,30, 15,20,25,12, 18,17,30,23,27]})
def update_plot(sliderVal):
filtered_df = df.query('Days== ' + str(sliderVal))
data = [go.Bar(x = filtered_df.Website,
y = filtered_df.Count)]
layout = go.Layout(
xaxis = dict(title = 'Website'),
yaxis = dict(title = 'Exception count'),
title = f'Number of exceptions per user in the last {sliderVal} days')
chart = go.Figure(data = data, layout = layout, )
py.iplot(chart, filename = 'WebExceptions')
# links an IntSlider taking values between 1 and 3 to the update_plot function
widgets.interact(update_plot, sliderVal = (1, 3))
and here is the result with sliderVal = 2: