Update dashtable columns name dynamically - python

I'm trying to make a dashtable and its columns will be change dynamically based on dropdowns value.
Below is my sample code:
import pandas as pd
import numpy as np
import plotly.express as px
import dash
from dash import html
from dash import dcc
from dash.dependencies import Input, Output,State
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
from dash import dash_table
df1 = pd.DataFrame({
'Contract No': ['VN122001','VN122002','VN122003','VN122004','VN122005'],
'Amount': [22071,20775,20841,21891,22395]})
df1 = df1.set_index('Contract No')
df2 = pd.DataFrame({
'Contract No': ['VN122001','VN122002','VN122003','VN122004','VN122005'],
'Cusname': ['A','B','C','D','E'],
'Branch': ['HN','HCM','HP','BN','DN']})
df2 = df2.set_index('Contract No')
app = dash.Dash(__name__,external_stylesheets=[dbc.themes.LUX])
app.layout = html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(id='columns_name',placeholder="Columns name", # Dropdown for heatmap color
options=list_column,
value='Cusname',
multi=False,
disabled=False,
clearable=True,
searchable=True)
],width={'size':4,'offset':0,'order':1},style={'padding-top' : 15}),
]),
dbc.Row([
html.Div(
id = 'tableDiv',
className = 'tableDiv'
)
])
])
#app.callback(Output('tableDiv', 'children'),
[Input('columns_name', 'value')])
def update_columns_name(columns):
col_name = df2[[columns]]
df3 = pd.merge(df1, col_name, left_index=True, right_index=True)
df3 = df3.reset_index()
mycolumns = [{'name': i, 'id': i} for i in df3.columns]
return html.Div([
dash_table.DataTable(
id='table',
columns=mycolumns,
data=df3.to_dict("rows")
)
])
if __name__ == "__main__":
app.run_server(debug=False,port=1222)
Actually it is working well but when I try to create Tabs and then return it to Tab, nothing show. Below is my code to create Tab.
app.layout = html.Div([dbc.Tabs(
[dbc.Tab(label="Dashboard", tab_id="dashboard"),
dbc.Tab(label="Table", tab_id="table"),
],
id="tabs",
active_tab="dashboard"),
html.Div(id="tab-content", className="p-4"),
])
#app.callback(
Output("tab-content", "children"),
[Input("tabs", "active_tab")])
def render_tab_content(active_tab):
if active_tab == "dashboard":
return html.Div([dbc.Row([html.H5('Graphs')])])
elif active_tab == "table":
return html.Div([
dbc.Row([
html.Div(id = 'tableDiv',
className = 'tableDiv')
])
])
#app.callback(Output('tableDiv', 'children'),
[Input('columns_name', 'value')])
def update_columns_name(columns):
col_name = df2[[columns]]
df3 = pd.merge(df1, col_name, left_index=True, right_index=True)
df3 = df3.reset_index()
mycolumns = [{'name': i, 'id': i} for i in df3.columns]
return html.Div([
dash_table.DataTable(
id='table',
columns=mycolumns,
data=df3.to_dict("rows"))
])
if __name__ == "__main__":
app.run_server(debug=False,port=1222)
What should I do to fix this issue. Thank you.

This is the error javascript throws back in the browser console:
Object { message: "ID not found in layout", html: "Attempting to connect a callback Output item to component:\n \"tableDiv\"\nbut no components with that id exist in the layout.\n\nIf you are assigning callbacks to components that are\ngenerated by other callbacks (and therefore not in the\ninitial layout), you can suppress this exception by setting\n```suppress_callback_exceptions=True```.\nThis ID was used in the callback(s) for Output(s):\n tableDiv.children" } dash_renderer.v2_1_0m1644023699.min.js:2:84904
Object { message: "ID not found in layout", html: "Attempting to connect a callback Input item to component:\n \"columns_name\"\nbut no components with that id exist in the layout.\n\nIf you are assigning callbacks to components that are\ngenerated by other callbacks (and therefore not in the\ninitial layout), you can suppress this exception by setting\n```suppress_callback_exceptions=True```.\nThis ID was used in the callback(s) for Output(s):\n tableDiv.children" }
As it suggests, you are referring to id components that are generated by another callback (namely, callback render_tab_content generates the tableDiv div and is being referred to by update_column_name). This is a problem, as your tableDiv won't be present when you select the dashboard table but the callback may still be invoked (at least initially which is why you are having an issue to begin with).
It's best to perhaps have the contents of update_column_name combined with render_table_content and generate the column stuff you need through the one callback. You don't really gain anything from splitting into 2 callbacks in this instance, and if you really want, you can abstract the contents of the other callback into a normal function and just call it in render_tab_content. Here is a suggested adaptation:
def update_columns_name(columns):
col_name = df2[[columns]]
df3 = pd.merge(df1, col_name, left_index=True, right_index=True)
df3 = df3.reset_index()
mycolumns = [{'name': i, 'id': i} for i in df3.columns]
return (
html.Div([
dbc.Row([
html.Div([
html.Div([
dash_table.DataTable(
id='table',
columns=mycolumns,
data=df3.to_dict("rows")
)
])
],
id='tableDiv',
className='tableDiv')
])
])
)
#app.callback(
Output("tab-content", "children"),
[Input("tabs", "active_tab")],
[State('columns_name', 'value')]
)
def render_tab_content(active_tab, columns):
print(active_tab, columns)
if active_tab == "dashboard":
return html.Div([dbc.Row([html.H5('Graphs')])])
elif active_tab == "table":
return update_columns_name(columns)

Related

using hoverData in plotly dash to create hover go.Table

I am trying to use the hoverData of a plot with many traces to display a side table of values related to each trace. The main code runs as follows. (note this is not the full code, but i included the relevant info)
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_average', 'p_repitition_average', 'p_median','p_median_all', 'p_median_average','p_range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output, State
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
dcc.Graph(id="plot", style={'width':'75%', 'float': 'left','height': '70vh','display':'inline-block'}),
html.Div([dcc.Graph(id='hover-data', style ={'float':'right'})], style={'width':'20%', 'paddingTop':35}),
])
#app.callback(
Output("plot", "figure"),
[Input("plot-picker", "value"), Input("test-picker", "value")])
def update_bar_chart(plot_picker, picker_test):
i=0
if plot_picker == 'weakness':
data = []
for test in picker_test:
df = df_lists[test]
p_value = [x for x in df.columns if x.startswith('pva')]
n_rounds = len(p_value)
trace = go.Bar(x=df.test_name, y = df.weak_rate, name = '{}, #rounds: {}'.format(test,n_rounds))
data.append(trace)
layout = go.Layout(title = 'Fraction of weak and failed results per each Dieharder test')
fig = go.Figure(data, layout)
fig.update_yaxes(title_text='Failed/weak fractions')
fig.update_layout(legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01))
return fig
The hover data includes the number of the trace not its name, which i need to specify the df source of the data. I am using the following code to get the hover data to generate the table:
#app.callback(Output('hover-data', 'graph'),
[Input('plot', 'hoverData')] )
def hover_data(hoverData):
Die_test = hoverData['points'][0]['x']
curve_number = hoverData['points'][0]['curveNumber']
trace_name = app.layout['plot'].figure['data'][curve_number]['name']
df = df_lists[trace_name]
df = df[df['test_name'] == Die_test]
data = [go.Table(header=dict(values=['p_mean', 'p_median', 'range', 'std'], fill_color='paleturquoise', align='left'), cells=dict(values=[df['p_mean'], df['p_median'], df['range'], df['std']] ))]
fig = go.Figure(data,)
return fig
The problem it is not working. I am not seeing anything when i hover over the data. I am not sure where the problem is coming, but most probably from the trace_name variable as i am getting the error:
Callback error updating hover-data.graph
AttributeError: 'Graph' object has no attribute 'figure'.
I tried to include a [State('plot', 'figure')] in the input of the callback. and then use the .figure['data'][curve_number]['name'] directly (instead of using app.layout['plot'] first), but it also didn't work.
Any help is appreciated.
Thanks
I don't have your dataframe so I think you can refer my code to revise yours:
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
app.layout = html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Fertility rate, total (births per woman)',
id='crossfilter-xaxis-column',
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-xaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Life expectancy at birth, total (years)',
id='crossfilter-yaxis-column'
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-yaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1})
], style={'padding': '10px 5px'}, className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Graph(
id='crossfilter-indicator-scatter',
hoverData={'points': [{'customdata': 'Japan'}]}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dash_table.DataTable(id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=[],
style_table={'height': 550},
style_header={'backgroundColor': 'orange', 'padding': '10px', 'color': '#000000'},
style_cell={'textAlign': 'center', 'font_size': '12px',
'whiteSpace': 'normal', 'height': 'auto'},
editable=True, # allow editing of data inside all cells
filter_action="native", # allow filtering of data by user ('native') or not ('none)
sort_action="native", # enables data to be sorted per-column by user or not ('none')
sort_mode="single", # sort across 'multi' or 'single' columns
column_selectable="multi", # allow users to select 'multi' or 'single' columns
row_selectable="multi", # allow users to select 'multi' or 'single' rows
row_deletable=True, # choose if user can delete a row (True) or not (False)
selected_columns=[], # ids of columns that user selects
selected_rows=[], # indices of rows that user selects
page_action="native",
export_headers='display')
], width={'size': 6, "offset": 0, 'order': 1}),
], className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Slider(
df['Year'].min(),
df['Year'].max(),
step=None,
id='crossfilter-year--slider',
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()}
)
], width={'size': 6, "offset": 0, 'order': 1})
], className='p-2 align-items-center')
])
#app.callback(
Output('crossfilter-indicator-scatter', 'figure'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-yaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'),
Input('crossfilter-yaxis-type', 'value'),
Input('crossfilter-year--slider', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_traces(customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_xaxes(title=xaxis_column_name, type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name, type='linear' if yaxis_type == 'Linear' else 'log')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
#app.callback(
Output('table', 'data'),
Input('crossfilter-indicator-scatter', 'hoverData'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'))
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
country_name = hoverData['points'][0]['customdata']
dff = df[df['Country Name'] == country_name]
dff = dff[dff['Indicator Name'] == xaxis_column_name]
return dff.to_dict(orient='records')
if __name__ == '__main__':
app.run_server(debug=False, port=1414)
I think instead of return go.Table, you can use dash_table in your Div and then return filtered data frame. Hope this help.

Dash Filtering For Numeric Columns

I would like to use the code below in order to allow user to filter columns. The problem i sthat I cannot filter the columns with numerical values. How can I solve this issue? I was thinking to find column type, but it was not in the code
from dash import Dash, dcc, html, Input, Output, dash_table
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')
df['id'] = df['country']
df.set_index('id', inplace=True, drop=False)
app = Dash(__name__)
app.layout = html.Div([
dcc.RadioItems(
[{'label': 'Read filter_query', 'value': 'read'}, {'label': 'Write to filter_query', 'value': 'write'}],
'read',
id='filter-query-read-write',
),
html.Br(),
dcc.Input(id='filter-query-input', placeholder='Enter filter query'),
html.Div(id='filter-query-output'),
html.Hr(),
dash_table.DataTable(
id='datatable-advanced-filtering',
columns=[
{'name': i, 'id': i, 'deletable': True} for i in df.columns
# omit the id column
if i != 'id'
],
data=df.to_dict('records'),
editable=True,
page_action='native',
page_size=10,
filter_action="native"
),
html.Hr(),
html.Div(id='datatable-query-structure', style={'whitespace': 'pre'})
])
#app.callback(
Output('filter-query-input', 'style'),
Output('filter-query-output', 'style'),
Input('filter-query-read-write', 'value')
)
def query_input_output(val):
input_style = {'width': '100%'}
output_style = {}
if val == 'read':
input_style.update(display='none')
output_style.update(display='inline-block')
else:
input_style.update(display='inline-block')
output_style.update(display='none')
return input_style, output_style
#app.callback(
Output('datatable-advanced-filtering', 'filter_query'),
Input('filter-query-input', 'value')
)
def write_query(query):
if query is None:
return ''
return query
#app.callback(
Output('filter-query-output', 'children'),
Input('datatable-advanced-filtering', 'filter_query')
)
def read_query(query):
if query is None:
return "No filter query"
return dcc.Markdown('`filter_query = "{}"`'.format(query))
#app.callback(
Output('datatable-query-structure', 'children'),
Input('datatable-advanced-filtering', 'derived_filter_query_structure')
)
def display_query(query):
if query is None:
return ''
return html.Details([
html.Summary('Derived filter query structure'),
html.Div(dcc.Markdown('''```json
{}
```'''.format(json.dumps(query, indent=4))))
])
if __name__ == '__main__':
app.run_server(debug=True)
Is there any way to handle this issue? Alternatively, where is the column type included in this script? I took it from Dash website.
If you want to write your own filter, you should enclose the column name with {}. For example, in order to get the value 708573 from pop column, you should write it as:
{pop} = 708573
But if you write your filter under a specific column, in this case, you only need to write = 708573
Please look in the documentation to familiarize yourself more to the filtering syntax.

Create dcc.Dropdown values Dynamically with Python Function

I am creating a search application with Dash by Plotly. I have a main search function for that creates a dataframe for the whole application that is defined as:
def search(term):
with index.searcher() as searcher:
parser = QueryParser("content", index.schema)
myquery = parser.parse(term)
results = searcher.search(myquery, limit=None)
print("Documents Containing ", term, ": ", len(results), "\n")
df = pd.DataFrame([i['date'], i['site'], i['system'], i['ticket'], i.score, i['level'], i['first'], i['last'], i['department'], \
i['detect'], i['code'],i['content'], i['description'], i['owner'], i['ownerGroup'], i['docId']] for i in results)
df.columns=['Reported Date', 'Site', 'System','Ticket ID', 'Score', 'Level', 'First', 'Last', 'Department', \
'Detection', 'Code', 'Content', 'Description', 'Owner', 'Owner Group', 'Document ID']
return df
I want users to be able to filter down search results with filters built into dcc.Dropdown. A couple of them can be hard-coded like so:
dcc.Dropdown(
id='siteFilter',
options=[
{'label': 'ABC', 'value': 'ABC'},
{'label': 'DEF', 'value': 'DEF'},
{'label': 'HIJ', 'value': 'HIJ'},
{'label': 'LMO', 'value': 'LMO'}
],
value=['ABC', 'DEF', 'HIJ', 'LMO'],
multi=True
However, some of the fields I want to filter on contain many options for a search and cannot be hard-coded. I can get the options to work. However, my application will not apply the filter changing when a user changes values. So, I have in my app.layout:
html.Div(dbc.Row([dbc.Col([
html.Label(["System Filter",
dcc.Dropdown(
id='systemFilter',
options='options',
value='All',
multi=True,
)
])
], width = 5)
]))
In my callback, I have tried several different options/combos of Outputs and States to achieve this with no luck. Callback:
#app.callback(
[Output(component_id='outTable', component_property='data'),
Output(component_id='outTable', component_property='columns'),
Output(component_id='commonWords', component_property='children'),
Output(component_id='systemFilter', component_property='options'),
#Output(component_id='systemFilter', component_property='value')
],
[Input(component_id='button', component_property='n_clicks')],
[State('searchId', 'value'),
State('siteFilter', 'value'),
State('detectFilter', 'value'),
State('levelFilter', 'value'),
State('codeFilter', 'value'),
#State(component_id='systemFilter', component_property='value')
])
def tableCreate(n_clicks, searchId, siteFilter, detectFilter, levelFilter, codeFilter):
if n_clicks > 0:
searchFrame = search(searchId)
###################### System Filter #######################################
#print('HERE DLKFSJSLDFKJSLDKJFJLKFDSJLDSKF')
#print(searchFrame['System'].unique())
#global optionsArray
optionsArray = searchFrame['System'].unique()
optionsArray = optionsArray.tolist()
print('Test')
print(optionsArray)
print('Test')
system_filter_options = [{'label': i, 'value': i} for i in optionsArray]
systemFilter = optionsArray
searchFrame = searchFrame[searchFrame['System'].isin(systemFilter)]
# Bunch of other code
searchFrame = searchFrame.drop(columns=['ContentNoStop'])
columns = [{'name': col, 'id': col} for col in searchFrame.columns]
data = searchFrame.to_dict(orient='records')
return data, columns, wordComponent, system_filter_options #, systemFilter
else:
return dash.no_update, dash.no_update, dash.no_update, dash.no_update
Essentially, one of 2 things happens. 1) The values return blank because they are not defined or 2) the options continually override the updated value parameter which I know is caused by the line systemFilter = optionsArray but I cannot seem to figure out a work around for this.
Here is an example image. I would like the results to populate all, but when a user filters by system and only selects a few, all my graphs and tables should update. But everytime, it does not seem to catch that. What am I missing here? How can the dcc.Dropdown selections be applied as a filter dynamically? I cannot and do not want to hard-code all the options
Easier reproduceable example:
I would like the dropdowns options to filter the table. If dog is selected, the table only shows dog.
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_table
import pandas as pd
from dash.dependencies import Input, Output, State
external_stylesheets = [dbc.themes.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
def createTable():
df = pd.DataFrame({'num_legs': [2, 4, 8, 0],
'num_wings': [2, 0, 0, 0],
'system': ['falcon', 'dog', 'spider', 'fish']},
index=[1, 2, 3, 4])
return df
app.layout = html.Div([
html.Button('Submit and Refresh', id='button', n_clicks=0, className='btn btn-primary'),
dbc.Row([dbc.Col([
html.Label(["System Filter",
dcc.Dropdown(
id='systemFilter',
options='options',
value='All',
multi=True,
)
])
], width = 5)
]),
html.Div([dash_table.DataTable(style_cell={
'whiteSpace': 'normal',
'height': 'auto',
'textAlign': 'left'
},
fill_width=False,
id='outTable',
sort_action="native",
sort_mode="multi",
column_selectable="single",
row_deletable=True,
)
],
) ])
#app.callback(
[Output(component_id='outTable', component_property='data'),
Output(component_id='outTable', component_property='columns'),
Output(component_id='systemFilter', component_property='options'),
#Output(component_id='systemFilter', component_property='value')
],
[Input(component_id='button', component_property='n_clicks')],
[
State(component_id='systemFilter', component_property='value')
])
def tableCreate(n_clicks, systemFilter):
if n_clicks > 0:
searchFrame = createTable()
optionsArray = searchFrame['system'].unique()
optionsArray = optionsArray.tolist()
print('Test')
print(optionsArray)
print('Test')
system_filter_options = [{'label': i, 'value': i} for i in optionsArray]
systemFilter = optionsArray
searchFrame = searchFrame[searchFrame['system'].isin(systemFilter)]
columns = [{'name': col, 'id': col} for col in searchFrame.columns]
data = searchFrame.to_dict(orient='records')
return data, columns, system_filter_options#, systemFilter
if __name__ == '__main__':
app.run_server(debug=False)

Manually update Dash table from user Input

I am trying to build a dashboard with Dash. One of the feature of this dashboard is that after dynamically updating dropdowns a user will be able to select multiple options and group them together.
I am able to group options together but when I go to group other options it rewrites my previous changes.
Such as when I group t1 and t2 in Group1 it is done but when I remove these selection from dropdown and select t3 and t4 in Group2 it rewrites my previous group.
Following is my code:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_table
import pandas as pd
data = [['tom', 'nick','t1',''], ['tom', 'nick','t2',''], ['tom', 'john','t3',''], ['tom','john','t4','']]
df = pd.DataFrame(data, columns = ['Captain', 'VCaptain', 'Teams','Groups'])
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server
colors = {
"graphBackground": "#F5F5F5",
"background": "#ffffff",
"text": "#000000"
}
app.layout = dbc.Container(
[
dbc.Row(
[
dbc.Col(dbc.Card([
dbc.FormGroup([
dbc.Label(html.B("Select Captains from list below:")),
dcc.Dropdown(id="captains",
options=[{'label': i, 'value': i} for i in df['Captain'].unique()],
value=[i for i in df['Captain'].unique()],)
]),
dbc.FormGroup([
dbc.Label(html.B("Select Vice Captains from below:")),
dcc.Dropdown(id="vcaptains")
]
),
dbc.FormGroup([
dbc.Label(html.B("Select Teams which needs to be grouped together:")),
dcc.Dropdown(id="teams",multi = True),
]
),
dbc.FormGroup([
dbc.Label(html.B("Input new group name:")),
dbc.Input(id="group", placeholder="Type group name...", type="text"),
]
),
html.I("Write a Name for the Group of the selected teams.",
style={"color":"#737373", "font-size":"0.8rem"}),
html.Br(),
dbc.Button("Add Group", id="assign", block=False,
outline=True, color="primary", #className = "mr-2",
style = {"margin-right": "10.5rem"}),
html.Br(),
], body = True,
style={"top": "20px",
"left":10,
"height":"900px",
"width": "20rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",}
), width=2),
dbc.Col(width = 1),
dbc.Col(id='table',width=6, style={"top":-120})
],
align="center",
),
],fluid=True)
# # Dropdown to filter Captains
# #app.callback(
# Output('captains', 'options'),
# [Input('captains', 'value')])
# def captain_options(value):
# opts=[]
# if value is not None:
# opts = df['Captain'].unique()
# return [{'label': i, 'value': i} for i in opts]
# Dropdown to filter Vice Captains
#app.callback(
Output('vcaptains', 'options'),
[Input('captains', 'value')])
def vice_captain_options(value):
opts=[]
if value is not None:
opts = df[df['Captain'].isin([value])]['VCaptain'].unique()
return [{'label': i, 'value': i} for i in opts]
# Dropdown to filter Vice Captains
#app.callback(
Output('teams','options'),
[Input('captains', 'value'),
Input('vcaptains', 'value')])
def teams_options(captains, vcaptains):
opts=[]
if captains is not None:
opts = df[df['Captain'].isin([captains])]['Teams'].unique()
if vcaptains is not None:
opts = df[df['Captain'].isin([captains]) &
df['VCaptain'].isin([vcaptains])]['Teams'].unique()
return [{'label': i, 'value': i} for i in opts]
#app.callback(
Output('table', 'data'),
[Input('captains', 'value'),
Input('vcaptains', 'value')])
def table(captains, vcaptains):
df1 = df
if captains is not None:
df1 = df[(df['Captain'].isin([captains]))]
if vcaptains is not None:
df1 = df[(df['VCaptain'].isin([vcaptains]))]
if df1 is not None:
return df1.to_dict('records')
#app.callback(
Output('table', 'children'),
[Input('table', 'data'),
Input('assign', 'n_clicks')],
[State('teams', 'value'),
State('group', 'value')])
def table(data,n,teams,group):
df1 = pd.DataFrame.from_dict(data)
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'assign' in changed_id:
if teams is not None:
if type(teams)==str:
df1.loc[df1['Teams'].isin([teams]), 'Groups'] = group
else:
df1.loc[df1['Teams'].isin(teams), 'Groups'] = group
if df is not None:
return dash_table.DataTable(data = df1.to_dict('records'),
columns=[{'id': c, 'name': c} for c in df1.columns])
if __name__ == '__main__':
app.run_server(debug=False)
If I've understood your problem correctly, then I think what you're missing is an intermediate step to hold data you've already selected. You could use the store component to do that. The flow would work like this:
Add selections to group 1
Group 1 selections update the Store
Add selections to group 2
Read in the existing Store value, and combine it with group 2 selections, then update the Store with the new combined values
Repeat until all desired selections are made
Something (perhaps a button) triggers the table to update using the data in the Store

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 :-)

Categories

Resources