Construct and pass dataframes between callbacks after clicking a button in Dash - python

I am trying to add an item after a button is clicked. After clicking several buttons (A, B, C), the dataframe should have those information passed after the selected buttons are clicked. I want the passed dataframe to include df = pd.DataFrame( combination of A, B, C). However, at this stage, when I press the button, and write the corresponding information into a dataframe, the callback deletes all the information, and keeps only the last pressed button. I bypassed this limitation by writing corresponding information into csv but I wonder if there is a more elegant approach.
#app.callback(Output('intermediate_value', 'children'),
[Input('like_val_0', 'n_clicks_timestamp'),
Input('like_val_1','n_clicks_timestamp'),
Input('like_val_2', 'n_clicks_timestamp')
])
def construct_preferences(like_val_0, like_val_1, like_val_2):
listedTimestamps = [like_val_0, like_val_1, like_val_2]
listedTimestamps = [0 if v is None else v for v in listedTimestamps]
sortedTimestamps = sorted(listedTimestamps)
if like_val_0 == sortedTimestamps[-1]:
pickedButton = "like_val_0"
print (pickedButton)
sample_that_matches_address_0 = data[data['addresses'].str.contains(item_in_df[0].partition('.jpg')[0])]
sample_that_matches_address_0.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_1 == sortedTimestamps[-1]:
pickedButton = "like_val_1"
print (pickedButton)
sample_that_matches_address_1 = data[data['addresses'].str.contains(item_in_df[1].partition('.jpg')[0])]
sample_that_matches_address_1.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_2 == sortedTimestamps[-1]:
pickedButton = "like_val_2"
print (pickedButton)
sample_that_matches_address_2 = data[data['addresses'].str.contains(item_in_df[2].partition('.jpg')[0])]
sample_that_matches_address_2.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
As you can see I write that item to csv and I wonder if there is any way to prevent callback from updating until I press another button; in my case predict.
#app.callback(Output('container', 'children'),
[Input('intermediate_value', 'children'),
Input('predict_val', 'n_clicks')])
def update_output_images (intermediate_value, n_clicks):
if (n_clicks!=0):
preferences = pd.read_csv('prefs.csv')
preferences.drop_duplicates(keep='first', inplace=True)
EDIT:
As the comment suggests, I update the example with a reproducible one
import json
import os
import pandas as pd
import random
import glob
from os import listdir
import flask
import pickle
import base64
from PIL import Image as PImage
import urllib.request
import numpy as np
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server
data = pd.DataFrame(np.random.randn(100, 4), columns=list('ABCD'))
preferences = pd.DataFrame(columns=list(data))
row_1 = html.Div([(dbc.Row([
dbc.Col([
html.P('Random_val_0 is {}'.format(data['A'].loc[0])),
dbc.Button('Like This One', id='like_val_0', style={'vertical-align': "bottom"}, n_clicks=0),
]),
dbc.Col([
html.P('Random_val_1 is {}'.format(data['A'].loc[1])),
dbc.Button('Like This One', id='like_val_1', style={'vertical-align': "bottom"}, n_clicks=0),
]),
dbc.Col([
html.P('Random_val_2 is {}'.format(data['A'].loc[2])),
dbc.Button('Like This One', id='like_val_2', style={'vertical-align': "bottom"}, n_clicks=0),
])
]))])
predict = html.Div([
html.Button ('Predict', id='predict_val', n_clicks=0),
html.Div(id='images_new')
])
container_out = html.Div([html.Div(id='container')])
placeholder = html.Div([html.Div(id='intermediate_value', style={'display': 'none'})])
app.layout = dbc.Container(children=[row_1, html.Br(), html.Br(), placeholder, html.Br(), container_out, html.Br(), predict])
#app.callback(Output('intermediate_value', 'children'),
[Input('like_val_0', 'n_clicks_timestamp'),
Input('like_val_1','n_clicks_timestamp'),
Input('like_val_2', 'n_clicks_timestamp')
])
def construct_preferences(like_val_0,like_val_1, like_val_2):
listedTimestamps = [like_val_0, like_val_1, like_val_2]
listedTimestamps = [0 if v is None else v for v in listedTimestamps]
sortedTimestamps = sorted(listedTimestamps)
if like_val_0 == sortedTimestamps[-1]:
pickedButton = "like_val_0"
print (pickedButton)
sample_output_0 = data.loc[data['A'] == data['A'].loc[0]]
return sample_output_0.to_json()
#sample_that_matches_address_0.to_csv('prefs.csv', columns=list(data), mode='a', header=False)
if like_val_1 == sortedTimestamps[-1]:
pickedButton = "like_val_1"
print (pickedButton)
sample_output_1 = data.loc[data['A'] == data['A'].loc[1]]
return sample_output_1.to_json()
if like_val_2 == sortedTimestamps[-1]:
pickedButton = "like_val_2"
print (pickedButton)
sample_output_2 = data.loc[data['A'] == data['A'].loc[2]]
return sample_output_2.to_json()
#app.callback(Output('container', 'children'),
[Input('intermediate_value', 'children'),
Input('predict_val', 'n_clicks')])
def update_output_images (intermediate_value, n_clicks):
if (n_clicks!=0):
preferences = pd.read_json(intermediate_value)
print (preferences.shape)
if __name__ == '__main__':
app.run_server(host='0.0.0.0', debug=True)

Related

simple dash app with table with histogram that updates with selected cell

I'm trying to create a dash app that updates a histogram depending on what cell is selected in the 'group' column.
I can get the table to display but having trouble with the histogram.
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.express as px
import numpy as np
from dash import Dash, dash_table
import json
app = dash.Dash(__name__)
## create data
df_rand = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df_rand = pd.melt(df_rand, value_vars=list('ABCD'))
df_rand_summary = df_rand.groupby('variable').describe()
df_rand_summary = df_rand_summary.droplevel(level=0, axis=1)
df_rand_summary.insert(0, 'group', df_rand_summary.index)
app.layout = html.Div(children=[
## add table
dash_table.DataTable(
data=df_rand_summary.to_dict('records'),
columns=[{'id': c, 'name': c, } for c in df_rand_summary]
),
# include histogram
html.Div([
dcc.Graph(
id='hist'
)
])
])
#app.callback(
Output('hist', 'figure'),
Input('table', 'active_cell'),
State('table', 'data'))
def update_hist(active_cell, df_rand):
# subset histogram with selected cell
# from 'group' column
cell = json.dumps(active_cell, indent=2)
row = active_cell['row']
col = active_cell['column_id']
value = df_rand[row][col]
fig = px.histogram(df_rand[df_rand['variable'] == value], x='value')
return fig
if __name__ == '__main__':
app.run_server(debug=True)
When you use df_rand as a parameter to update_hist, you overwrite the df_rand defined outside the update_hist. To solve this problem, define new parameter, instead as follows:
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.express as px
import numpy as np
from dash import Dash, dash_table
import json
app = dash.Dash(__name__)
## create data
df_rand = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df_rand = pd.melt(df_rand, value_vars=list('ABCD'))
df_rand_summary = df_rand.groupby('variable').describe()
df_rand_summary = df_rand_summary.droplevel(level=0, axis=1)
df_rand_summary.insert(0, 'group', df_rand_summary.index)
app.layout = html.Div(children=[
## add table
dash_table.DataTable(id="table",
data=df_rand_summary.to_dict('records'),
columns=[{'id': c, 'name': c, } for c in df_rand_summary]
),
# include histogram
html.Div([
dcc.Graph(
id='hist'
)
])
])
#app.callback(
Output('hist', 'figure'),
Input('table', 'active_cell'),
State('table', 'data'),
prevent_initial_call=True)
def update_hist(active_cell, data_dict): #<------ here we define data_dict instead of df_rand
row = active_cell['row']
col = active_cell['column_id']
value = data_dict[row][col]
fig = px.histogram(df_rand.query("variable == #value"), x='value')
return fig
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)
Output:

How to control dash print output

I've made a dash and it showed as I want, my code as below:
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import dash_bootstrap_components as dbc
df3 = pd.DataFrame({'Branch': ['Hanoi','Hanoi','Hanoi','Hochiminh','Hochiminh','Hochiminh','Danang','Danang','Danang'],
'Name': ['Sales','Card','Loan','Sales','Card','Loan','Sales','Card','Loan'],
'KPI': [1000,1200,8000,1000,1200,8000,1000,1200,8000],
'Complete' : [982,1015,7105,780,1120,7600,815,1150,6800]})
df3['Percent'] = ((df3['Complete']/df3['KPI'])*100).round(decimals=2)
app = dash.Dash(__name__,external_stylesheets=[dbc.themes.LUX])
app.layout = html.Div([dbc.Row(dbc.Col(html.H2('KPI Subplots',className='text-center text primary, mb-3'))),
dbc.Row([
dbc.Col([html.H5('Sales KPI',className='text-center'),
dcc.Graph(id='credit_fluctuation',figure={},style={'height':150}),
],width={'size':1,"offset":0,'order':1},),
dbc.Col([html.H5('Card KPI',className='text-center'),
dcc.Graph(id='credit_fluctuation_2',figure={},style={'height':150}),
],width={'size':1,"offset":0,'order':1}),
dbc.Col([html.H5('Loan KPI',className='text-center'),
dcc.Graph(id='credit_fluctuation_3',figure={},style={'height':150}),
],width={'size':1,"offset":0,'order':1}),
dbc.Col([html.H5('Drop Down',className='text-center'),
dcc.Dropdown(id='br_cd_2',placeholder="Please select branch code",
options=[{'label':x,'value':x} for x in df3.sort_values('Branch')['Branch'].unique()],
value='Select',
multi=False,
disabled=False,
clearable=True,
searchable=True),
],width={'size':2,"offset":0,'order':1})
]),
])
#app.callback(
Output(component_id='credit_fluctuation',component_property='figure'),
[Input(component_id='br_cd_2',component_property='value')])
def build_graph(branch_cd):
global dff
if not branch_cd or 'Select' in branch_cd:
dff = df3[(df3['Name']=="Sales")]
dff = pd.pivot_table(dff,('KPI','Complete'),index=['Name',],aggfunc=np.sum).reset_index()
dff['Percent'] = ((dff['Complete']/dff['KPI'])*100).round(decimals=2)
val=dff.iloc[0,3]
else:
dff = df3[(df3['Branch']== branch_cd)]#.isin(branch_cd)
dff = dff[(dff['Name']=='Sales')]
val=dff.iloc[0,4]
fig=go.Figure(data=[go.Pie(labels=['',''],
values=[val,100-val],
hole=0.85,
textinfo='none',
marker_colors=['rgb(113,209,145)','rgb(240,240,240)'],
)],
layout=go.Layout(annotations=[{'text':str(val)+"%",'x':0.5,'y':0.5,'font_size':20,'showarrow':False}],
showlegend=False))
fig.update_layout(margin=dict(l=5,r=5,t=5,b=5))
return fig
#app.callback(
Output(component_id='credit_fluctuation_2',component_property='figure'),
[Input(component_id='br_cd_2',component_property='value')])
def build_graph(branch_cd_2):
global dff2
if not branch_cd_2 or 'Select' in branch_cd_2:
dff2 = df3[(df3['Name']=="Card")]
dff2 = pd.pivot_table(dff2,('KPI','Complete'),index=['Name',],aggfunc=np.sum).reset_index()
dff2['Percent'] = ((dff2['Complete']/dff2['KPI'])*100).round(decimals=2)
val2=dff2.iloc[0,3]
else:
dff2 = df3[(df3['Branch']== branch_cd_2)]#.isin(branch_cd)
dff2 = dff2[(dff2['Name']=='Card')]
val2=dff2.iloc[0,4]
fig_2=go.Figure(data=[go.Pie(labels=['',''],
values=[val2,100-val2],
hole=0.85,
textinfo='none',
marker_colors=['rgb(113,209,145)','rgb(240,240,240)'],
)],
layout=go.Layout(annotations=[{'text':str(val2)+"%",'x':0.5,'y':0.5,'font_size':20,'showarrow':False}],
showlegend=False))
fig_2.update_layout(margin=dict(l=5,r=5,t=5,b=5))
return fig_2
#app.callback(
Output(component_id='credit_fluctuation_3',component_property='figure'),
[Input(component_id='br_cd_2',component_property='value')])
def build_graph(branch_cd_3):
global dff3
if not branch_cd_3 or 'Select' in branch_cd_3:
dff3 = df3[(df3['Name']=="Loan")]
dff3 = pd.pivot_table(dff3,('KPI','Complete'),index=['Name',],aggfunc=np.sum).reset_index()
dff3['Percent'] = ((dff3['Complete']/dff3['KPI'])*100).round(decimals=2)
val3=dff3.iloc[0,3]
else:
dff3 = df3[(df3['Branch']== branch_cd_3)]#.isin(branch_cd)
dff3 = dff3[(dff3['Name']=='Loan')]
val3=dff3.iloc[0,4]
fig_3=go.Figure(data=[go.Pie(labels=['',''],
values=[val3,100-val3],
hole=0.85,
textinfo='none',
marker_colors=['rgb(113,209,145)','rgb(240,240,240)'],
)],
layout=go.Layout(annotations=[{'text':str(val3)+"%",'x':0.5,'y':0.5,'font_size':20,'showarrow':False}],
showlegend=False))
fig_3.update_layout(margin=dict(l=5,r=5,t=5,b=5))
return fig_3
if __name__ == "__main__":
app.run_server(debug=False,port=8056)
This is the result:
But when I try to print it out, it was overlapped as attached picture.
So I would like to ask that is there any way to print dash same with this appearance. I tried to export to pdf but it was overlapped too. Please give me some suggestion.
Thank you.

Python Plotly Dash dropdown Adding a "select all" for scatterplot

I would like to add a select all for my dropdown, and make it the default when the app opens up, with the ability to then one by one remove capsule and also to unselect the select all button and select a group of capsule. I managed to find several ways that other people have use the select all option but none seems to work for my situation. Thank you!
Data looks this this.
Dash look like this.
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import dash # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
server = app.server
df = pd.read_csv("tcd vs rh.csv")
print(df)
capsuleID = df['Capsule_ID'].unique()
print(capsuleID)
capsuleID_names = list(capsuleID)
print(capsuleID_names)
capsuleID_names_1 = [{'label': k, 'value': k } for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2
app.layout = html.Div([
html.H1("Web Application Dashboards with Dash", style={'text-align': 'center'}),
dcc.Dropdown(id="capsule_select",
options=capsuleID_names_all,
optionHeight=25,
multi=True,
searchable=True,
placeholder='Please select...',
clearable=True,
value=[''],
style={'width': "40%"}
),
html.Div([
dcc.Graph(id="the_graph")
]),
])
# -----------------------------------------------------------
#app.callback(
Output('the_graph', 'figure'),
[Input('capsule_select', 'value')]
)
def update_graph(capsule_chosen):
if capsule_chosen == 'all_values':
dff = df['Capsule_ID']
else:
dff = df[df['Capsule_ID'].isin(capsule_chosen)] # filter all rows where capsule ID is the capsule ID selected
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition='top center')
return scatterplot
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
If by default you want to select everything from df['Capsule_ID'] you can simply pass it to the value of your dropdown.
Then you can change your callback to something like this for the 'select all' functionality:
#app.callback(
Output("the_graph", "figure"),
Output("capsule_select", "value"),
Input("capsule_select", "value"),
)
def update_graph(capsules_chosen):
dropdown_values = capsules_chosen
if "All" in capsules_chosen:
dropdown_values = df["Capsule_ID"]
dff = df
else:
dff = df[df["Capsule_ID"].isin(capsules_chosen)]
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition="top center")
return scatterplot, dropdown_values
In addition to your check not working with the default dropdown value, you were doing this:
dff = df['Capsule_ID']
which means you were setting dff to a single column. This is not what want since 'tcd' and 'humidity' columns don't exist on df['Capsule_ID'].

Assigning functions to drop drown elements in python dash

I have created a dashboard to display data in CSV's. However i am not sure how to assign separate functions to relative drop down options. Below is the code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from dash.dependencies import Input, Output
import plotly.figure_factory as ff
import plotly.graph_objs as go
df1 = pd.read_csv('ReturnsData - returns_summary.csv.csv')
df2 = pd.read_csv('ReturnsData - NonReturn_guideid_counts.csv.csv')
df3 = pd.read_csv('ReturnsData - AllReturns.csv.csv')
df4 = pd.read_csv('ReturnsData - OrderCreationdetails.csv.csv')
df5 = pd.read_csv('ReturnsData - Return_guideid_counts.csv.csv')
def generate_table(dataframe,max_rows=1000000):
return html.Table(
# Header
[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
[html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))]
)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Dropdown(
id='my-dropdown',
options=[
{'label' : 'Returns Summary' , 'value' : 'df1'},
{'label' : 'NonReturn guideid counts' , 'value' : 'df2'}
],
# value='df1'
# multi = True
),
html.Div(id='output-container')
])
#app.callback(
dash.dependencies.Output('output-container', 'children'),
[dash.dependencies.Input('my-dropdown', 'value')])
def update_output(value):
return generate_table(df1)
if __name__ == '__main__':
app.run_server(debug=True)
Here out of the two options i can only trigger the function to display df1. How do i trigger df2 when second drop down is selected?
You have hard-coded df1 in your generate_table method. You need to make this method's parameter dynamic, depending on what value is passed through the update_output method of the callback (this is what the user chooses from the dropdown). Following the example from the official Dash-Plotly docks you need to modify your code this way:
#app.callback(
dash.dependencies.Output('output-container', 'children'),
[dash.dependencies.Input('my-dropdown', 'value')])
def update_output(value):
return generate_table(value)
If you need to use different functions for df1 and df2 you could add this logic:
def update_output(value):
if value == df1:
return generate_table(value)
elif value == df2:
return other_generate_table_function(value)

Dynamic list of python dash core components

I am using python dash and want to create a list of menus/forms that can be extended and reduced dynamically by clicking buttons to do so. Adding new forms/menus should add another identical form to the page (a list of forms/menus).
The following code allows the addition/removal of additional divs enclosing multiple dash core components, however, whenever I choose an option in one of the dropdowns or enter anything into one of the input fields, what I've chosen or entered disappears again.
import dash
import dash_core_components as dcc
import dash_html_components as html
step = html.Div(
children=[
"Menu:",
dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']]),
dcc.Input(placeholder="Enter a value ...", type='text', value='')
])
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
div_list = [step]
app.layout = html.Div(
children=[
html.H1(children='Hello Dash'),
html.Div(children=div_list, id='step_list'),
html.Button('Add Step', id='add_step_button', n_clicks_timestamp='0'),
html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp='0')])
#app.callback(
dash.dependencies.Output('step_list', 'children'),
[dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
[dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
add_ts = int(add_ts)
remove_ts = int(remove_ts)
if add_ts > 0 and add_ts > remove_ts:
div_list += [step]
if len(div_list) > 1 and remove_ts > add_ts:
div_list = div_list[:-1]
return div_list
if __name__ == '__main__':
app.run_server(debug=True)
Can anybody explain to me what I’m doing wrong?
Thanks a lot!
Dash components need to have ids specified in order to save values after callbacks.
This example generating step as a function using random ids fixes the problem:
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
def step():
return html.Div(
children=[
"Menu:",
dcc.Dropdown(options=[{'label': v, 'value': v} for v in ['option1', 'option2', 'option3']],id=str(np.random.randn())),
dcc.Input(placeholder="Enter a value ...",id=str(np.random.randn()))
])
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
children=[
html.H1(children='Hello Dash'),
html.Div(children=[step()], id='step_list'),
html.Button('Add Step', id='add_step_button', n_clicks_timestamp=0),
html.Button('Remove Step', id='remove_step_button', n_clicks_timestamp=0)])
#app.callback(
dash.dependencies.Output('step_list', 'children'),
[dash.dependencies.Input('add_step_button', 'n_clicks_timestamp'),
dash.dependencies.Input('remove_step_button', 'n_clicks_timestamp')],
[dash.dependencies.State('step_list', 'children')])
def add_step(add_ts, remove_ts, div_list):
add_ts = int(add_ts)
remove_ts = int(remove_ts)
if add_ts > 0 and add_ts > remove_ts:
div_list += [step()]
if len(div_list) > 1 and remove_ts > add_ts:
div_list = div_list[:-1]
return div_list
if __name__ == '__main__':
app.run_server(debug=True)

Categories