I'm new to Dash and Python. I have an app with a dropdown and a search input however I cannot get the callback to get both inputs to work. Currently either only the dropdown will work or just the input. I would like to first select the dropdown and then be able to search for text within the Datatable.
Below is my code.
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import pathlib
from dash.dependencies import Input, Output
df = pd.read_csv('data.csv',encoding='cp1252')
env_list = df["Environment"].unique()
PAGE_SIZE = 20
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]
)
def description_card():
"""
:return: A Div containing logo.
"""
return html.Div(
id="description-card",
children=[
html.Img(id="logo", src=app.get_asset_url("logo.png"), height=80)
],style={'textAlign': 'center'}
)
def generate_control_card():
"""
:return: Descriptions
"""
return html.Div(
id="env-card",
children=[
html.H3("Welcome to the Package Catalog"),
]
)
app.layout = html.Div(
id="app-container",
children=[
# Banner
html.Div(
id="banner",
className="banner",
children=[description_card(),generate_control_card()
],
),
html.Div([
html.P([
html.Label("Select Environment", style={"background": "#F5F5F5"}),
dcc.Dropdown(
id='env-select',
options=[{'label': i, 'value': i} for i in env_list],
value=env_list[0],
)]),
html.Div([
html.P([
html.Label("Search for a package and description in the search box.", style={'display':'inline-block', 'background': '#F5F5F5'}),
dcc.Input(value='', id='filter-input', placeholder='Filter', debounce=True)
]),
dash_table.DataTable(css=[{'selector': '.row', 'rule': 'margin: 0'}],
id='datatable-paging',
columns=[
{"name": i, "id": i} for i in df.columns # sorted(df.columns)
],
style_header={
'backgroundColor': 'F5F5F5',
'fontWeight': 'bold'
},
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
sort_action='custom',
sort_mode='single',
sort_by=[]
)
]),
]),
])
#app.callback(
Output('datatable-paging', 'data'),
[
Input('datatable-paging', 'page_current'),
Input('datatable-paging', 'page_size'),
Input('datatable-paging', 'sort_by'),
Input('env-select', 'value'),
Input('filter-input', 'value')
])
def update_table(page_current, page_size, sort_by, environment_input, filter_string,
):
# Filter
dff = df[df.Environment==environment_input]
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
# Sort
if len(sort_by):
dff = dff.sort_values(
sort_by[0]['column_id'],
ascending=sort_by[0]['direction'] == 'asc',
inplace=False
)
return dff.iloc[
page_current * page_size:(page_current + 1) * page_size
].to_dict('records')
Change this line
dff = df[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
to this
dff = dff[df.apply(lambda row: row.str.contains(filter_string, regex=False).any(), axis=1)]
In the line before the one shown above you apply your dropdown filter and store the filtered result as dff. So by using df instead of dff you're essentially discarding the dropdown filter result.
Related
I am working on a selection menu where the user can select the number of experiments they want to compare/see. Depending on this number I would like to adapt the number of columns that appear, being one for each experiment. In the image below an example of the menu with one experiment can be seen. However, I would like these dropdowns to duplicate if the user selects two experiments having them in side-to-side columns.
Here is an example of one column and one experiment:
The code I have so far is the following:
# Load Data
df = px.data.tips()
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
# Build App
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
# first row - selecting the number of experiments
html.Div([
html.I("Select the number of experiments you want to compare (min - 2 and max - 10):"),
html.Br(),
# input box for the number of experiments
dcc.Input(id="num_exp" , type='number', min = 2, max = 10, placeholder="No of experiments"),
html.Div(id="output")
], style = {'width': '100%'}
),
# second row - where the experiments are organized in columns
html.Div([
html.Label([
html.I("X axis "),
dcc.Dropdown(
id = 'dd_axis',
clearable = False,
placeholder = 'Select property x-axis',
options=[
{'label': i, 'value': i}
for i in properties
],
style={
'width': '100%'
})
]),
html.Label([
html.I("Y Axis "),
dcc.Dropdown(
id = 'dd_yaxis',
clearable = False,
placeholder = 'Select property y-axis',
options=[ ],
disabled = False)
])
], style = {'display': 'inline-block', 'vertical-align': 'top', 'margin-left': '3vw', 'margin-top': '3vw', 'width': '50%'})
])
#app.callback(
Output("output", "children"),
# number of experiments input
Input("num_exp", "value"),
)
def update_output(test_type):
in_testtype = test_type
return u'{} experiments'.format(test_type)
def cb_render(*vals):
return " | ".join((str(val) for val in vals if val))
Can you help me adding more dropdown menus to the side and dividing the second row in columns based in the user selection?
Thank you!
You can do this by using dbc.Col and dbc.Row.
In your layout, have a Div and within it an empty dbc.Row that will be used to populate its children attribute with columns.
Then, in the callback that is triggered by the input box value, have a loop that returns the number of columns you want, with the relevant code for the dbc.Col and what you'd like to display in it.
Here's a worked-up example of your original code:
import dash
from dash import dcc, html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
properties = ["a", "b", "c"]
app.layout = html.Div([
# first row - selecting the number of experiments
html.Div([
html.I(
"Select the number of experiments you want to compare (min - 2 and max - 10):"),
html.Br(),
# input box for the number of experiments
dcc.Input(id="num_exp", value=2, type='number', min=2,
max=10, placeholder="No of experiments"),
html.Div([
dbc.Row([], id="output")
])
], style={'width': '100%'}
),
])
#app.callback(
Output("output", "children"),
# number of experiments input
Input("num_exp", "value"),
)
def update_output(test_type):
columns_list = []
letters = "xyzabcdefg"
for i in range(test_type):
columns_list.append(
dbc.Col([
html.Label([
html.I(f"{letters[i].upper()} axis "),
dcc.Dropdown(
id=f'{letters[i]}_axis',
clearable=False,
placeholder=f'Select property {letters[i]}-axis',
options=[
{'label': i, 'value': i}
for i in properties
],
style={
'width': '100%'
})
])
])
)
return columns_list
if __name__ == "__main__":
app.run_server(debug=True)
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'].
I would like to add a range slider along with my dropdown, and make the range slider the 'Wallclock' datetime along with an interaction that allows the range slider to chose the datetime for that capsules based on the dropdown value. I managed to find several ways that other people have done this but none seems to work for my situation especially the callback and the update of the graph. Thank you!
Data looks like this.
Dash looks like this.
Code looks like this.
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np
import openpyxl
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_excel("tcd vs rh 2.xlsx")
print(df)
capsuleID = df['Capsule_ID'].unique()
print(capsuleID)
capsuleID_names = sorted(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("Relative Humidity vs TCD", 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=['All'],
style={'width': "100%"}
),
dcc.RangeSlider(id='slider',
min=df['Wallclock'].min(),
max=df['Wallclock'].max(),
value=[df.iloc[-101]['Wallclock'].timestamp(), df.iloc[-1]['Wallclock'].timestamp()]
),
html.Div([
dcc.Graph(id="the_graph"),
]),
])
# -----------------------------------------------------------
#app.callback(
Output('the_graph', 'figure'),
Output('capsule_select', 'value'),
Input('capsule_select', 'value'),
Input('slider', 'value'),
)
def update_graph(capsule_chosen):
lBound = pd.to_datetime(value[0], unit='s')
uBound = pd.to_datetime(value[1], unit='s')
filteredData = df.loc[(df['date'] >= lBound) & (df['date'] <= uBound)]
dropdown_values = capsule_chosen
if "All" in capsule_chosen:
dropdown_values = capsuleID_names
dff = df
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",
hover_name="Wallclock",
)
scatterplot.update_traces(textposition='top center')
return scatterplot, dropdown_values
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
obviously I don't have access to your Excel spreadsheet so generated a data frame with same shape
taken approach of using a second figure with a rangeslider for slider capability
updated callback to use this figure as input for date range
used jupyter dash inline, this can be changed back to your setup (commented lines)
generate some sample data
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
"Wallclock": pd.date_range(
"22-dec-2020 00:01:36", freq="5min", periods=2000
),
"tcd": np.linspace(3434, 3505, 2000) *np.random.uniform(.9,1.1, 2000),
"humidity": np.linspace(63, 96, 2000),
}
).pipe(lambda d: d.assign(Capsule_ID=(d.index // (len(d)//16))+2100015))
slider is a figure with a rangeslider
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np
import openpyxl
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
from jupyter_dash import JupyterDash
# app = dash.Dash(__name__)
# server = app.server
app = JupyterDash(__name__)
# df = pd.read_excel("tcd vs rh 2.xlsx")
# print(df)
capsuleID = df["Capsule_ID"].unique()
# print(capsuleID)
capsuleID_names = sorted(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
def slider_fig(df):
return px.scatter(
df.groupby("Wallclock", as_index=False).size(), x="Wallclock", y="size"
).update_layout(
xaxis={"rangeslider": {"visible": True}, "title":None},
height=125,
yaxis={"tickmode": "array", "tickvals": [], "title": None},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
)
app.layout = html.Div(
[
html.H1("Relative Humidity vs TCD", 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=["All"],
style={"width": "100%"},
),
dcc.Graph(
id="slider",
figure=slider_fig(df),
),
html.Div(
[
dcc.Graph(id="the_graph"),
]
),
]
)
# -----------------------------------------------------------
#app.callback(
Output("the_graph", "figure"),
Output("capsule_select", "value"),
Output("slider", "figure"),
Input("capsule_select", "value"),
Input('slider', 'relayoutData'),
State("slider", "figure")
)
def update_graph(capsule_chosen, slider, sfig):
dropdown_values = capsule_chosen
if "All" in capsule_chosen:
dropdown_values = capsuleID_names
dff = df
else:
dff = df[
df["Capsule_ID"].isin(capsule_chosen)
] # filter all rows where capsule ID is the capsule ID selected
if slider and "xaxis.range" in slider.keys():
dff = dff.loc[dff["Wallclock"].between(*slider["xaxis.range"])]
else:
# update slider based on selected capsules
sfig = slider_fig(dff)
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
hover_name="Wallclock",
)
scatterplot.update_traces(textposition="top center")
return scatterplot, dropdown_values, sfig
# ------------------------------------------------------------------------------
if __name__ == "__main__":
# app.run_server(debug=True)
app.run_server(mode="inline")
I need a logic which selects all checkboxes when the option All is checked in the checklist And the graph would change accordingly.
Or if we can disable the other checkboxes(x,y,x and x1, x2,x3) if the option All is Checked.
The code which I have used is as follows
import pandas as pd
import plotly.graph_objs as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
df = pd.DataFrame({'category' : ['A','A','A','B','B','B'],
'subcategory' : ['l', 'y', 'z', 'x1','y1','z1'],
'x_coord' : [1, 2,3,2,2,2],
'y_coord' : [1,3,2,1,3,2]})
# lists of categories
options1 = sorted(df["category"].unique().tolist())
# dictionary of category - subcategories
all_options = df.groupby("category")["subcategory"].unique()\
.apply(list).to_dict()
# we add as first subcategory for each category `all`
for k, v in all_options.items():
all_options[k].insert(0, 'all')
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='first-dropdown',
options=[{'label': k, 'value': k} for k in all_options.keys()],
value=options1[0]
),
html.Hr(),
dcc.Dropdown(id='second-dropdown'),
html.Hr(),
dcc.Graph(id='display-selected-values')
])
# the following two callbacks generate a dynamic 2 option
#app.callback(
dash.dependencies.Output('second-dropdown', 'options'),
[dash.dependencies.Input('first-dropdown', 'value')])
def set_2_options(first_option):
return [{'label': i, 'value': i} for i in all_options[first_option]]
#app.callback(
dash.dependencies.Output('second-dropdown', 'value'),
[dash.dependencies.Input('second-dropdown', 'options')])
def set_2_value(available_options):
return available_options[0]['value']
#app.callback(
dash.dependencies.Output('display-selected-values', 'figure'),
[dash.dependencies.Input('first-dropdown', 'value'),
dash.dependencies.Input('second-dropdown', 'value')])
def update_graph(selected_first, selected_second):
if selected_second == 'all':
ddf = df[df["category"]==selected_first]
else:
ddf = df[(df["category"]==selected_first) &
(df["subcategory"]==selected_second)]
fig = go.Figure()
fig.add_trace(
go.Scatter(x=ddf["x_coord"],
y=ddf["y_coord"],
marker = dict(size=15, color='green'),
mode='markers'))
return fig
if __name__ == '__main__':
app.run_server(debug=False)
If the All option is checked from the checkbox for category A then the subcategories can either be all selected(i.e checked) or the can be disbled and the graph must be shown for all the subcategories.
What calls and function is necessary to achieve these.
Thanks in Advance!!!
I am using dash core components to create a dash app that includes tabs that return tables based on callbacks. I'm trying to see if it's possible to change the label on the tab itself based on the callback, though it seems like the label will only accept a string, and not a callback with and id and children.
Right now the label of the tab simply says 'Car Type' (this is just a snippet of the code):
dbc.Row([
dbc.Col(dcc.Dropdown(id='car-types', multi=False,,
options=[{'label':x, 'value':x}
for x in sorted(car_list)]),
#width={'size': 5, "offset": 1, 'order': 1}
),
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type', children=
dbc.Col([
html.Div(id="table1"
)]
)
)
#app.callback(
[Output('table1', 'children'),
Output('car_label', 'children')],
[Input('car-types', 'value')],
[State('car_manuf', 'value')],
def update_table1(a,b):
code for table,
a = code for car_label string
return html.Div([dt.DataTable(),
), a
But what if I wanted it to say "Car Type SUV" or "Car Type Sedan" based on what the output 'car_label' says, how can I change the label of the tab to say that?
I tried something like:
html.Div([
dcc.Tabs([
dcc.Tab(label='Car Type ' + (children=[],id='car_label'), children=
dbc.Col([
html.Div(id="table1"
)]
)
But obviously that won't work. Any suggestions?
Maybe something like this with a dropdown and string formatting.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Tabs(id='tabs-example', value='tab-1', children=[
dcc.Tab(label='', id='first-tab', value='tab-1'),
dcc.Tab(label='', id='second-tab', value='tab-2'),
]),
dcc.Dropdown(id='car-types', multi=False, value='honda',
options=[{'label':'honda', 'value':'honda'},
{'label':'toyota', 'value':'toyota'}]
),
dcc.Dropdown(id='car-types2', multi=False, value='tesla',
options=[{'label': 'jeep', 'value': 'jeep'},
{'label': 'tesla', 'value': 'tesla'}]
),
html.Div(id='tabs-example-content')
])
#app.callback(Output('tabs-example-content', 'children'),
Input('tabs-example', 'value'))
def render_content(tab):
if tab == 'tab-1':
return html.Div([
html.H3('Tab content 1...')
])
elif tab == 'tab-2':
return html.Div([
html.H3('Tab content 2...')
])
#app.callback(
Output('first-tab', 'label'),
Input('car-types', 'value')
)
def update_label(name):
return f"Car Type: {name}"
#app.callback(
Output('second-tab', 'label'),
Input('car-types2', 'value')
)
def update_label(name2):
return f"Car Type: {name2}"
if __name__ == '__main__':
app.run_server(debug=True)