dash dynamically generating graphs in tabs - python

I have a dropdown list of Indian states. Upon selecting one state, different graphs should be generated under different tabs. I want to generate a total of 6 graphs and 3 tabs namely, 'convicts', 'under_trial', 'detenues'. Each of these tabs will contain 2 graphs each.
The graphs are gender and caste distribution of convicts gender and caste distribution of under_trial gender and caste distribution of detenues based on the dropdown value of the Indian state selected.
I want to create something like this.
Here is the dataset used.
Libraries imported
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import plotly.express as px
Dash web app initialization
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
Pandas dataframe loading
df = pd.read_csv("Caste.csv")
Defined dropdown and graph functions
def dropdown():
return html.Div([dcc.Dropdown(id='dropdown',
options=[{'label': i, 'value': i} for i in df["state_name"].unique()],
value="Gujarat")]
,className="dropdown")
def graph1_convicts():
return dcc.Graph(id="graph1_convicts")
def graph2_convicts():
return dcc.Graph(id="graph2_convicts")
def graph1_under_trial():
return dcc.Graph(id="graph1_under_trial")
def graph2_under_trial():
return dcc.Graph(id="graph2_under_trial")
def graph1_detenues():
return dcc.Graph(id="graph1_detenues")
def graph2_detenues():
return dcc.Graph(id="graph2_detenues")
Layout
app.layout = dbc.Container([
dbc.Row([dbc.Col([html.Div("Indian Prison Stats between 2001-2013", className="heading")])]),
dbc.Row([dbc.Col(dropdown())]),
dcc.Tabs(id="tabs-selector",
value="tab-1",
className="custom-tabs-container",
children=[
dcc.Tab(label="Convicts",
value="tab-1",
className="custom-tab",
children=[html.Div([graph1_convicts(),
html.Br(),
graph2_convicts()]),
]),
dcc.Tab(label="under_trial",
value="tab-2",
className="custom-tab",
children=[html.Div([graph1_under_trial(),
html.Br(),
graph2_under_trial()]),
]),
dcc.Tab(label="detenues",
value="tab-3",
className="custom-tab",
children=[html.Div([graph1_detenues(),
html.Br(),
graph2_detenues()]),
]),
]),
])
Callback
#app.callback([Output('graph1_convicts', 'figure'),
Output('graph2_convicts', 'figure'),
Output("graph1_under_trial", "figure"),
Output("graph2_under_trial", "figure"),
Output("graph1_detenues", "figure"),
Output("graph2_detenues", "figure")],
[Input('dropdown', 'value'),
Input("tabs-selector", "value")])
def update_graph(dropdown, tab):
# df1_convicts has only convicts column kept, rest all removed, and groupby is done on gender column
df1_convicts = df[df["state_name"]==dropdown]
df1_convicts = df1_convicts.drop(["is_state", "caste", "under_trial", "detenues", "others"], axis=1)
df1_convicts = df1_convicts.groupby(['state_name', "year", "gender"])['convicts'].sum().reset_index()
# df2_convicts has only convicts column kept, rest all removed, and groupby is done on caste column
df2_convicts = df[df["state_name"]==dropdown]
df2_convicts = df2_convicts.drop(["is_state", "gender", "under_trial", "detenues", "others"], axis=1)
df2_convicts = df2_convicts.groupby(["state_name", "year", "caste"])["convicts"].sum().reset_index()
# df1_under_trial has only under_trial column kept, rest all removed, and groupby is done on gender column
df1_under_trial = df[df["state_name"]==dropdown]
df1_under_trial = df1_under_trial.drop(["is_state", "caste", "convicts", "detenues", "others"], axis=1)
df1_under_trial = df1_under_trial.groupby(['state_name', "year", "gender"])['under_trial'].sum().reset_index()
# df2_under_trial has only under_trial column kept, rest all removed, and groupby is done on caste column
df2_under_trial = df[df["state_name"]==dropdown]
df2_under_trial = df2_under_trial.drop(["is_state", "gender", "convicts", "detenues", "others"], axis=1)
df2_under_trial = df2_under_trial.groupby(["state_name", "year", "caste"])["under_trial"].sum().reset_index()
# df1_detenues has only detenues column kept, rest all removed, and groupby is done on gender column
df1_detenues = df[df["state_name"]==dropdown]
df1_detenues = df1_detenues.drop(["is_state", "caste", "convicts", "under_trial", "others"], axis=1)
df1_detenues = df1_detenues.groupby(['state_name', "year", "gender"])['detenues'].sum().reset_index()
# df2_under_trial has only under_trial column kept, rest all removed, and groupby is done on caste column
df2_detenues = df[df["state_name"]==dropdown]
df2_detenues = df2_detenues.drop(["is_state", "gender", "convicts", "under_trial", "others"], axis=1)
df2_detenues = df2_detenues.groupby(["state_name", "year", "caste"])["detenues"].sum().reset_index()
fig1_convicts = px.bar(df1_convicts, x="year", y="convicts", color="gender", title="gender distribution of convicts",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_convicts = px.bar(df2_convicts, x="year", y="convicts", color="caste", title="caste distribution of convicts",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig1_under_trial = px.bar(df1_under_trial, x="year", y="under_trial", color="gender", title="gender distribution of under_trial",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_under_trial = px.bar(df2_under_trial, x="year", y="under_trial", color="caste", title="caste distribution of under_trial",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig1_detenues = px.bar(df1_detenues, x="year", y="detenues", color="gender", title="gender distribution of detenues",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_detenues = px.bar(df2_detenues, x="year", y="detenues", color="caste", title="caste distribution of detenues",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
if tab=="tab-1":
return fig1_convicts, fig2_convicts
elif tab=="tab-3":
return fig1_detenues, fig2_detenues
elif tab=="tab-2":
return fig1_under_trial, fig2_under_trial
if __name__ == '__main__':
app.run_server(debug=True, port=5050)
How do I define the callback?
I could easily create gender and caste distribution of either convicts or under_trial or detenues. But, I am confused about how to show all 6 graphs under the 3 tabs based on the dropdown value.
I searched online and found that dcc.State might be useful in this case, but I couldn't understand it properly.

first thing is that you need to return all the output you define in the callback.
in your case you need 6 outputs to this callback
now as I see it you have 2 options:
generate all the graphs when you change the dropdown it will work with the current contact layout
only hold 1 tab contact and dynamically create only 2 graph when you switch tabs
option 1 code should look like this:
#app.callback([Output('graph1_convicts', 'figure'),
Output('graph2_convicts', 'figure'),
Output("graph1_under_trial", "figure"),
Output("graph2_under_trial", "figure"),
Output("graph1_detenues", "figure"),
Output("graph2_detenues", "figure")],
[Input('dropdown', 'value')])
def update_graph(dropdown):
# df1_convicts has only convicts column kept, rest all removed, and groupby is done on gender column
df1_convicts = df[df["state_name"]==dropdown]
df1_convicts = df1_convicts.drop(["is_state", "caste", "under_trial", "detenues", "others"], axis=1)
df1_convicts = df1_convicts.groupby(['state_name', "year", "gender"])['convicts'].sum().reset_index()
# df2_convicts has only convicts column kept, rest all removed, and groupby is done on caste column
df2_convicts = df[df["state_name"]==dropdown]
df2_convicts = df2_convicts.drop(["is_state", "gender", "under_trial", "detenues", "others"], axis=1)
df2_convicts = df2_convicts.groupby(["state_name", "year", "caste"])["convicts"].sum().reset_index()
# df1_under_trial has only under_trial column kept, rest all removed, and groupby is done on gender column
df1_under_trial = df[df["state_name"]==dropdown]
df1_under_trial = df1_under_trial.drop(["is_state", "caste", "convicts", "detenues", "others"], axis=1)
df1_under_trial = df1_under_trial.groupby(['state_name', "year", "gender"])['under_trial'].sum().reset_index()
# df2_under_trial has only under_trial column kept, rest all removed, and groupby is done on caste column
df2_under_trial = df[df["state_name"]==dropdown]
df2_under_trial = df2_under_trial.drop(["is_state", "gender", "convicts", "detenues", "others"], axis=1)
df2_under_trial = df2_under_trial.groupby(["state_name", "year", "caste"])["under_trial"].sum().reset_index()
# df1_detenues has only detenues column kept, rest all removed, and groupby is done on gender column
df1_detenues = df[df["state_name"]==dropdown]
df1_detenues = df1_detenues.drop(["is_state", "caste", "convicts", "under_trial", "others"], axis=1)
df1_detenues = df1_detenues.groupby(['state_name', "year", "gender"])['detenues'].sum().reset_index()
# df2_under_trial has only under_trial column kept, rest all removed, and groupby is done on caste column
df2_detenues = df[df["state_name"]==dropdown]
df2_detenues = df2_detenues.drop(["is_state", "gender", "convicts", "under_trial", "others"], axis=1)
df2_detenues = df2_detenues.groupby(["state_name", "year", "caste"])["detenues"].sum().reset_index()
fig1_convicts = px.bar(df1_convicts, x="year", y="convicts", color="gender", title="gender distribution of convicts",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_convicts = px.bar(df2_convicts, x="year", y="convicts", color="caste", title="caste distribution of convicts",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig1_under_trial = px.bar(df1_under_trial, x="year", y="under_trial", color="gender", title="gender distribution of under_trial",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_under_trial = px.bar(df2_under_trial, x="year", y="under_trial", color="caste", title="caste distribution of under_trial",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig1_detenues = px.bar(df1_detenues, x="year", y="detenues", color="gender", title="gender distribution of detenues",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
fig2_detenues = px.bar(df2_detenues, x="year", y="detenues", color="caste", title="caste distribution of detenues",
color_discrete_sequence=px.colors.qualitative.Set1, opacity=0.6)
return fig1_convicts, fig2_convicts,
fig1_under_trial, fig2_under_trial,
fig1_detenues, fig2_detenues
for option 2 you can see https://dash.plotly.com/dash-core-components/tabs it is the first option in the examples

Related

Dash dynamic live graph update of multiple traces

I am trying to build a Dash app that takes several different data sources as inputs and plots one graph for each data source. As the data is a continuous stream, I store only a small amount (the last 50 observations) of data inside a dcc.Store, one for each data source.
I therefore have one callback allowing me to update the data contained inside the different dcc.Store using pattern matching callback.
I would also like to have one pattern matching callback allowing me to update the dcc.Graph associated with each of the data source, using the data in the dcc.Store.
However, some of the datasources give me several time series that I would like to plot all on the same graph using one trace for each time series. My update_graphs function seems to be working fine to update the graph with only one trace but not when there are multiple traces. I think I am not returning the proper format to update multiple traces with extendData property of the graph.
I want my app to be dynamic so I'm trying to avoid having one call back for each data source
Here is a small example:
import dash
from dash.dependencies import Output, Input, State, MATCH, ALL
from dash import dcc, html
import plotly
import random
import plotly.graph_objs as go
import pandas as pd
# Initializing the data with the correct format
init_store = {}
n=3
init_df = pd.DataFrame({'a':pd.Series(dtype='int'), 'b':pd.Series(dtype='int'), 'c':pd.Series(dtype='int'), 'd':pd.Series(dtype='int')}, index=range(50))
init_df['a'] = init_df.index
init_store['0'] = init_df
for i in range(n):
init_df = pd.DataFrame({'a':pd.Series(dtype='int'), 'b':pd.Series(dtype='int')}, index=range(50))
init_df['a'] = init_df.index
init_store[f'{i+1}'] = init_df
# Function to update the dataframes with the new observations
def get_data(json_data):
df = pd.read_json(json_data)
compteur = df['a'][len(df['a'])-1]
if len(df.columns) > 2:
new_row = {'a':compteur + 1, 'b':random.randint(13,26), 'c':random.randint(13,26), 'd':random.randint(13,26)}
else:
new_row = {'a':compteur + 1, 'b':random.randint(13,26)}
df = df.shift(periods=-1)
df.iloc[len(df)-1] = new_row
return(df.to_json())
# Function to update the graphs based on the dataframes
def update_graphs(json_data):
df = pd.read_json(json_data)
nb_obs = df.shape[0]
x_new = df['a'][len(df)-1]
if len(df.loc[:, df.columns != 'a'].columns) > 1:
df = df.loc[:, df.columns != 'a']
return_lists = []
for i in range(len(df.columns)):
return_lists.append([dict(x=[[x_new]], y=[[df[df.columns[i]][nb_obs-1]]]), [i]])
return return_lists
else:
y_new = df['b'][nb_obs-1]
return dict(x=[[x_new]], y=[[y_new]]), [0]
def generate_graph_containers(index, json_data):
dataframe = pd.read_json(json_data)
X = dataframe['a']
Y = dataframe.loc[:, dataframe.columns != 'a']
graph_id = {'type': 'graph-', 'index': index}
return(
html.Div(
html.Div(
dcc.Graph(
id=graph_id,
style={"height": "8rem"},
config={
"staticPlot": False,
"editable": False,
"displayModeBar": False,
},
figure=go.Figure(
{
"data": [
{
"x": list(X),
"y": list(Y[variable]),
"mode": "lines",
"name": variable,
"line": {"color": "#f4d44d"},
}
for variable in Y.columns
],
"layout": {
"uirevision": True,
"margin": dict(l=0, r=0, t=4, b=4, pad=0),
"xaxis": dict(
showline=False,
showgrid=False,
zeroline=False,
showticklabels=False,
),
"yaxis": dict(
showline=False,
showgrid=False,
zeroline=False,
showticklabels=False,
),
"paper_bgcolor": "rgba(0,0,0,0)",
"plot_bgcolor": "rgba(0,0,0,0)",
}
}
)
)
)
)
)
app = dash.Dash(__name__)
store = [dcc.Store(id={'type':'store-', 'index':i}, data=init_store[str(i)].to_json()) for i in range(n)]
def make_layout():
return(
html.Div(
[
html.Div(
store
),
dcc.Interval(
id = 'interval',
interval = 1000,
n_intervals = 0
),
html.Div(
[
generate_graph_containers(str(i), store[i].data) for i in range(n)
]
)
]
)
)
app.layout = make_layout
#app.callback(
Output(component_id={'type':'store-', 'index':MATCH}, component_property='data'),
[
Input('interval', 'n_intervals'),
State(component_id={'type':'store-', 'index':MATCH}, component_property='data')
]
)
def update_data(time, data):
return(get_data(data))
#app.callback(
Output(component_id={'type':'graph-', 'index':MATCH}, component_property='extendData'),
Input(component_id={'type':'store-', 'index':MATCH}, component_property="data")
)
def update_graph(data):
print(pd.read_json(data))
return (
update_graphs(data)
)
if __name__ == '__main__':
app.run_server(debug=True)

plotly graph_objects (go) selecting two lines from two dropdowns menu to compare in the same figure

i am trying to compare two lines in the same fig in plotly go, the goal is to let the user to select two entities (i.e. countries) from two dropdown menu in the chart
this is the chart with all entities active
when i select different country in the two dropdown:
i.e. dropdown1 = Italy, dropdown2= France
the chart shows just one line (the last selected from the dropdown, so it update)but it doesn't plot the two lines in the same figure
Plot with just one line
this is the dataframe:
dataframe head
this is the code i am working on:
fig1 = go.Figure()
for column in df.columns.to_list():
fig1.add_trace(
go.Scatter(
x = df.index,
y = df[column],
name = column
)
)
button_all = dict(label = 'All',
method = 'restyle',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
button_none = dict(label = 'None',
method = 'update',
args = [{'visible': df.columns.isin(df.columns),
'title': 'All',
'showlegend':True}])
def create_layout_button(column):
return dict(label = column,
method = 'restyle',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
def create_layout_buttonE(column):
return dict(label = column,
method = 'update',
args = [{'visible': df.columns.isin([column]),
'title': column,
'showlegend': True}])
addAll = True
# Update plot sizing
fig1.update_layout(
width=700,
height=500,
autosize=False,
margin=dict(t=120, b=0, l=0, r=0),
)
# Add dropdowns
button_layer_1_height = 1.08
fig1.update_layout(
updatemenus=[
dict(
buttons=([button_all] * addAll) + list(df.columns.map(lambda column: create_layout_button(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.1,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
dict(
buttons=([button_none] * addAll) + list(df.columns.map(lambda column: create_layout_buttonE(column))),
direction="down",
pad={"r": 10, "t": 0},
showactive=True,
x=0.37,
xanchor="left",
y=button_layer_1_height,
yanchor="top"
),
]
)
fig1.show()
synthesized dataframe in same structure as you defined
taken a different approach. Rather than creating all traces and controlling visibility in updatemenus. Create two traces and control name and contents of y in updatemenus
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# get some countries
countries = (
pd.read_csv(
"https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/locations.csv"
)
.loc[lambda d: d["iso_code"].str.len() == 3, "location"]
.sample(20)
.sort_values()
.values
)
# build data frame of same struct
df = pd.DataFrame(
np.random.randint(200, 1500, [22, len(countries)]),
columns=countries,
index=range(2000, 2022),
)
# create a figure with two place holder traces
fig = go.Figure(
[
go.Scatter(x=df.index, y=np.full(len(df), np.nan), meta=i, name=i)
for i in range(2)
]
)
# but data for y and name in when country is selected
fig.update_layout(
updatemenus=[
{
"x": b / 3,
"y": 1.4,
"active": None,
"buttons": [
{
"label": c,
"method": "restyle",
"args": [{"y": [df[c]], "name": c}, [b]],
}
for c in df.columns
],
}
for b in range(2)
]
)

keyerror 'column name' updating plotly dash figure

Issue:
I'm trying to load a simple plotly scatter plot, where
x axis = keyword
y axis = date
color = locale
however, even plotly throws a key error every time I add the "color" variable. The error code I get states that I don't have a column by that name, but when I print the column names I see the column there.
Anyone faced a similar issue?
Data:
Code:
# Importing needed libraries
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_table
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import random
import datetime
import plotly.graph_objects as go
# Reading the dataframe
df = pd.read_csv('./beanstalk_app/ti_data_ti_dash_v1.csv')
categories = df['category']
locales = df['locale']
# clean dataframe
ti_data = df.copy()
ti_data = ti_data.sample(1000)
# Contents of the app
# 1.Controls
# 1.1 Dropdown list
locs = dcc.Dropdown(id='choose_loc',
options=[
{
'label': locale, 'value': locale
} for locale in locales.unique()
],
placeholder='Select Locale')
cats = dcc.Dropdown(id='choose_cat',
options=[
{
'label': c, 'value': c
} for c in categories.unique()
],
placeholder='Select Category')
# 1.2 DatePicker
datepick = dcc.DatePickerRange(
id='choose_date',
min_date_allowed=df.date.min(),
max_date_allowed=df.date.max(),
initial_visible_month=df.date.min(),
clearable=True,
start_date=df.date.min(),
end_date=df.date.max()
)
# 2.Interactive table
table = dash_table.DataTable(
id='datatable',
columns=[
{"name": i, "id": i, "selectable": True, "presentation": "markdown"}
if i == "google_query"
else {"name": i, "id": i, "selectable": True}
for i in df.columns
],
data=df.to_dict('records'),
editable=False,
filter_action="native",
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_selectable="multi",
row_deletable=True,
selected_columns=[],
selected_rows=[],
page_action="native",
page_current=0,
page_size=10,
style_cell={
'whiteSpace': 'normal',
'height': 'auto',
},
)
# Initialize the app
app = dash.Dash(__name__)
app.title = 'Dot Graph Demo - All Entities'
app.layout = html.Div([
# modifiable table
dbc.Container([
dbc.Row(
[
dbc.Col(locs, width=4),
dbc.Col(cats, width=4),
dbc.Col(datepick, width=4)
]
),
dbc.Row(
table
),
], style={'backgroundColor': '#faf9f9'}),
# graphs resulting from modified table
html.Div(dcc.Graph(id='key_graph_l'))
],
style={'backgroundColor': '#faf9f9'})
# add callback functions
#app.callback(
Output('datatable', 'data'),
[
Input('choose_cat', 'value'),
Input('choose_loc', 'value'),
Input('choose_date', 'start_date'),
Input('choose_date', 'end_date')
]
)
def update_table(choose_cat, choose_loc, start_date, end_date):
df = ti_data.copy()
df['date'] = pd.to_datetime(df['date'])
df['date'] = df['date'].dt.date
if choose_cat is not None:
df = df[df['category'] == choose_cat]
if choose_loc is not None:
df = df[df['locale'] == choose_loc]
if start_date is not None and end_date is not None:
start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d").date()
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date()
df = df.loc[(df.date >= start_date) & (df.date <= end_date)]
data = df.to_dict('records')
return data
#app.callback(
Output('datatable', 'style_data_conditional'),
Input('datatable', 'selected_columns')
)
def update_styles(selected_columns):
return [{
'if': {'column_id': column},
'background_color': '#D2F3FF'
} for column in selected_columns]
#app.callback(
Output('key_graph_l', 'figure'),
[
Input('choose_date', 'start_date'),
Input('choose_date', 'end_date'),
Input('choose_cat', 'value'),
Input('choose_loc', 'value')
]
)
def update_key_l(choose_loc, choose_cat, start_date, end_date):
df = ti_data.copy()
if choose_cat is not None:
df = df[df['category'] == choose_cat]
if choose_loc is not None:
df = df[df['locale'] == choose_loc]
if start_date is not None and end_date is not None:
df = df.loc[(df.date >= start_date) & (df.date <= end_date)]
df = df.sort_values('total_count', ascending=False)[:25]
figure = px.scatter(df, x='keyword', y='date', color='locale', size='total_count',labels={'keyword': 'Keyword', 'total_count': 'Total Count', 'locale': 'Locale', 'date': 'Date'},
title='Weighing top words accross locales')
return figure
# requirements for beanstalk (aws)
if __name__ == '__main__':
app.run_server(debug=True)
The reason for the error is that the order of the arguments in the callback function is different. The error occurs because the date format that is set when there is no calendar selection does not match the date format handled in Dash.
# Importing needed libraries
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_table
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import random
import datetime
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
# Reading the dataframe
df = pd.read_csv('./beanstalk_app/ti_data_ti_dash_v1.csv')
categories = df['category']
locales = df['locale']
# clean dataframe
ti_data = df.copy()
ti_data = ti_data.sample(4) # sample data
# Contents of the app
# 1.Controls
# 1.1 Dropdown list
locs = dcc.Dropdown(id='choose_loc',
options=[
{
'label': locale, 'value': locale
} for locale in locales.unique()
],
placeholder='Select Locale')
cats = dcc.Dropdown(id='choose_cat',
options=[
{
'label': c, 'value': c
} for c in categories.unique()
],
placeholder='Select Category')
# 1.2 DatePicker
datepick = dcc.DatePickerRange(
id='choose_date',
min_date_allowed=df.date.min(),
max_date_allowed=df.date.max(),
initial_visible_month=df.date.min(),
clearable=True,
start_date=df.date.min(),
end_date=df.date.max()
)
# 2.Interactive table
table = dash_table.DataTable(
id='datatable',
columns=[
{"name": i, "id": i, "selectable": True, "presentation": "markdown"}
if i == "google_query"
else {"name": i, "id": i, "selectable": True}
for i in df.columns
],
data=df.to_dict('records'),
editable=False,
filter_action="native",
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_selectable="multi",
row_deletable=True,
selected_columns=[],
selected_rows=[],
page_action="native",
page_current=0,
page_size=10,
style_cell={
'whiteSpace': 'normal',
'height': 'auto',
},
)
# Initialize the app
app = dash.Dash(__name__)
app.title = 'Dot Graph Demo - All Entities'
app.layout = html.Div([
# modifiable table
dbc.Container([
dbc.Row(
[
dbc.Col(locs, width=4),
dbc.Col(cats, width=4),
dbc.Col(datepick, width=4)
]
),
dbc.Row(
table
),
], style={'backgroundColor': '#faf9f9'}),
# graphs resulting from modified table
html.Div(dcc.Graph(id='key_graph_l'))
],
style={'backgroundColor': '#faf9f9'})
# add callback functions
#app.callback(
Output('datatable', 'data'),
[
Input('choose_cat', 'value'),
Input('choose_loc', 'value'),
Input('choose_date', 'start_date'),
Input('choose_date', 'end_date')
]
)
def update_table(choose_cat, choose_loc, start_date, end_date):
df = ti_data.copy()
# df['date'] = pd.to_datetime(df['date'])
# df['date'] = df['date'].dt.date
if choose_cat is not None:
df = df[df['category'] == choose_cat]
if choose_loc is not None:
df = df[df['locale'] == choose_loc]
if start_date is not None and end_date is not None:
# start_date = datetime.datetime.strptime(start_date, "%Y-%m-%dT00:00:00").date()
# end_date = datetime.datetime.strptime(end_date, "%Y-%m-%dT00:00:00").date()
df = df.loc[(df.date >= start_date) & (df.date <= end_date)]
data = df.to_dict('records')
return data
#app.callback(
Output('datatable', 'style_data_conditional'),
Input('datatable', 'selected_columns')
)
def update_styles(selected_columns):
return [{
'if': {'column_id': column},
'background_color': '#D2F3FF'
} for column in selected_columns]
#app.callback(
Output('key_graph_l', 'figure'),
[
Input('choose_cat', 'value'),
Input('choose_loc', 'value'),
Input('choose_date', 'start_date'),
Input('choose_date', 'end_date')
]
)
def update_key_l(choose_cat, choose_loc, start_date, end_date):
df = ti_data.copy()
if choose_cat is not None:
df = df[df['category'] == choose_cat]
if choose_loc is not None:
df = df[df['locale'] == choose_loc]
if start_date is not None and end_date is not None:
df = df.loc[(df.date >= start_date) & (df.date <= end_date)]
df = df.sort_values('total_count', ascending=False)[:25]
figure = px.scatter(df, x='keyword', y='date', color='locale', size='total_count',labels={'keyword': 'Keyword', 'total_count': 'Total Count', 'locale': 'Locale', 'date': 'Date'},
title='Weighing top words accross locales')
return figure
# requirements for beanstalk (aws)
if __name__ == '__main__':
app.run_server(debug=True)

In dash, how do I use a callback to update a graph when a radio button is selected?

I'm new to dash and I'm having problems finding examples on using data frames within a callback. I created a weekly radio button and a monthly radio button.
When the monthly radio button is selected I would like the graph to pull data from df_monthly where each bar would be a monthly sum of pay. When the weekly radio button is checked I would like to see the graph populate each bar on a weekly basis which would be each row in the data frame since I get paid once a week.
I'm not certain where I'm going wrong but I keep receiving an error stating TypeError: update_fig() takes 0 positional arguments but 1 was given
The graph populates without data like the picture below. Thanks for any help on this matter.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.plotly as py
import plotly.graph_objs as go
import sqlite3
import pandas as pd
from functools import reduce
import datetime
conn = sqlite3.connect('paychecks.db')
df_ct = pd.read_sql('SELECT * FROM CheckTotal',conn)
df_earn = pd.read_sql('SELECT * FROM Earnings', conn)
df_whold = pd.read_sql('SELECT * FROM Withholdings', conn)
data_frames = [df_ct, df_earn, df_whold]
df_paystub = reduce(lambda left,right: pd.merge(left,right,on=['Date'], how='outer'), data_frames)
def date_extraction(df):
df['Date'] = pd.to_datetime(df['Date'])
df['Year'] = df['Date'].dt.strftime('%Y')
df['Month'] = df['Date'].dt.strftime('%B')
df['Day'] = df['Date'].dt.strftime('%d')
return df
date_extraction(df_paystub)
df_monthly = df_paystub.groupby(['Month']).sum()
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({'external_url': 'https://codepen.io/amyoshino/pen/jzXypZ.css'})
app.layout = html.Div(children=[
html.Div([
html.Div([
dcc.RadioItems(
id='data-view',
options=[
{'label': 'Weekly', 'value': 'Weekly'},
{'label': 'Monthly', 'value': 'Monthly'},
],
value='',
labelStyle={'display': 'inline-block'}
),
], className = 'two columns'),
html.Div([
dcc.Dropdown(
id='year-dropdown',
options=[
{'label': i, 'value': i} for i in df_paystub['Year'].unique()
],
placeholder="Select a year",
),
], className='five columns'),
html.Div([
dcc.Dropdown(
id='month-dropdown',
options=[
{'label': i, 'value': i} for i in df_paystub['Month'].unique()
],
placeholder="Select a month(s)",
multi=True,
),
], className='five columns'),
], className = 'row'),
# HTML ROW CREATED IN DASH
html.Div([
# HTML COLUMN CREATED IN DASH
html.Div([
# PLOTLY BAR GRAPH
dcc.Graph(
id='pay',
)
], className = 'six columns'),
# HTML COLUMN CREATED IN DASH
html.Div([
# PLOTLY LINE GRAPH
dcc.Graph(
id='hours',
figure={
'data': [
go.Scatter(
x = df_earn['Date'],
y = df_earn['RegHours'],
mode = 'lines',
name = 'Regular Hours',
),
go.Scatter(
x = df_earn['Date'],
y = df_earn['OtHours'],
mode = 'lines',
name = 'Overtime Hours',
)
]
}
)
], className='six columns')
], className='row')
], className='ten columns offset-by-one')
#app.callback(dash.dependencies.Output('pay', 'figure'),
[dash.dependencies.Input('data-view', 'value')])
def update_fig():
figure={
'data': [
go.Bar(
x = df_monthly['Month'],
y = df_monthly['CheckTotal'],
name = 'Take Home Pay',
),
go.Bar(
x = df_monthly['Month'],
y = df_monthly['EarnTotal'],
name = 'Earnings',
)
],
'layout': go.Layout(
title = 'Take Home Pay vs. Earnings',
barmode = 'group',
yaxis = dict(title = 'Pay (U.S. Dollars)'),
xaxis = dict(title = 'Date Paid')
)
}
return figure
if __name__ == "__main__":
app.run_server(debug=True)
Hi #prime90 and welcome to Dash.
In glancing at your callback signature it looks like the update_fig() function needs to take the Input you've given it (using dash.dependencies.Input).
The callback is sending this Input what changes in your app you've specified. So it's sending along the value of #data-view you've given to your function update_fig(), which doesn't currently accept any variables, causing the error message.
Just update your function signature and add a couple of boolean variables to rid yourself of the error and get the potential functionality:
def update_fig(dataview_value):
# define your weekly OR monthly dataframe
# you'll need to supply df_weekly similarly to df_monthly
# though DO NOT modify these, see note below!
df = df_weekly if dataview == 'weekly' else df_monthly
dfkey = 'Week' if 'week' in df.columns else 'Month' # eh, worth a shot!
figure={
'data': [
go.Bar(
x = df[dfkey],
y = df['CheckTotal'],
name = 'Take Home Pay',
),
go.Bar(
x = df[dfkey],
y = df['EarnTotal'],
name = 'Earnings',
)
],
'layout': go.Layout(
title = 'Take Home Pay vs. Earnings',
barmode = 'group',
yaxis = dict(title = 'Pay (U.S. Dollars)'),
xaxis = dict(title = 'Date Paid')
)
}
return figure
As was written in the comments above, you'll need to do some type of prior manipulation to create a df_weekly, as you have with your current df_monthly.
In addition, the code snippet I wrote assumes the df column is named "Week" and "Month"--obviously update these as is necessary.
Data manipulation in Dash:
Ensure you read the data sharing docs, as they highlight how data should never be modified out of scope.
I hope this helps :-)

How to speed up Dash App with Pandas Groupby

My Dash App runs, however I have a dataframe with approximately 10k rows and to reload the plots and datatable that I use I need to rerun the groupby statement which takes very long to load. The user can select filters on the left which updates the dashboard. In the dashboard, I need the grouped number of customers per city. Since I have a dcc.Graphand a dash_table I basically refer to the same underlying dataframe twice and therefore update it twice, which I think is extremely inefficient.
Is there a way to have the dataframe only updated once and then send the result to the dcc.Graphand the dash_table at once? Also, are there other ways to speed up the app?
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
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],
'Gender':['m','f','m','f']})
df["CustomerCount"] = df.groupby(["zip_city"], as_index=False)["Customer"].transform("count")
gender_options = []
for gender in df['Gender'].unique():
gender_options.append({'label':str(gender),
'value':gender})
app = dash.Dash()
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})
app.layout = html.Div([html.H1('A Dashboard', style={'textAlign':'center'}),
html.Div(children=[
html.H1('Input', style={'textAlign':'center'}),
html.H6('Gender'),
html.P(
dcc.Checklist(id='gender-picker',
options=gender_options,
values=['m','f']
)
)
],
style = {'float':'left'},
className = "two columns"
),
html.Div([dcc.Tabs(children=[dcc.Tab(label='Map',
children=html.Div([
dcc.Graph(id='CustomerMap')
])
),
dcc.Tab(label='Data',
children=[html.Div([dash_table.DataTable(
id='table',
columns = [{"name": i, "id": i} for i in df.columns],
data = df.to_dict("rows")
)])
]
)
]
)
])
]
)
#app.callback(
dash.dependencies.Output('CustomerMap', 'figure'),
[dash.dependencies.Input('gender-picker', 'values')])
def update_figure(selected_gender):
filtered_df = df[df['Gender'].isin(selected_gender)]
filtered_df["CustomerCount"] = filtered_df.groupby(["zip_city"], as_index=False)["Customer"].transform("count")
customerCount = filtered_df['CustomerCount'].tolist()
zipcity = filtered_df['zip_city'].tolist()
hovertext = []
for i in range(len(customerCount)):
k = str(zipcity[i]) + ':' + str(customerCount[i])
hovertext.append(k)
return {'data':[dict(
type = 'scattergeo',
locationmode = 'USA-states',
lon = filtered_df['zip_longitude'],
lat = filtered_df['zip_latitude'],
text = hovertext,
hoverinfo = 'text',
marker = dict(
size = filtered_df['CustomerCount'],
line = dict(width=0.5, color='rgb(40,40,40)'),
sizemode = 'area'
),
transforms = [dict(
type = 'aggregate',
groups = filtered_df['zip_city'],
aggregations = [dict(target = filtered_df['Customer'], func = 'count', enabled = True)]
)
]
)
]
}
#app.callback(
dash.dependencies.Output('table', 'data'),
[dash.dependencies.Input('gender-picker', 'values')])
def update_table(selected_gender):
filtered_df = df[df['Gender'].isin(selected_gender)]
filtered_df["CustomerCount"] = filtered_df.groupby(["zip_city"], as_index=False)["Customer"].transform("count")
return filtered_df.to_dict("rows")
if __name__ == '__main__':
app.run_server()

Categories

Resources