Dash datatable - bar plot when choosing/clicking one value of the table - python

I'm new using Dash and I wonder if it is possible to have a Dash table with 1 column of numeric values like this one:
Values
-------
1
2
3
4
And have the option to choose/click to one of the values and make a bar plot appear with the value clicked.
Hope you can help me. Thanks in advance.

You could use the active_cell property of dash_table.DataTable to get the clicked value in a callback. Then you can use this value to plot the graph:
import pandas as pd
from dash import Dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash_table import DataTable
import plotly.graph_objects as go
df = pd.DataFrame(
{"values": [1, 2, 3, 4], "labels": ["value 1", "value 2", "value 3", "value 4"]}
)
app = Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id="graph"),
DataTable(
id="table",
columns=[{"name": "values", "id": "values"}],
data=df.to_dict("records"),
),
]
)
#app.callback(
Output("graph", "figure"), Input("table", "active_cell"), prevent_initial_call=True
)
def update_output_div(active_cell):
selected_value = df.iloc[active_cell["row"], active_cell["column"]]
num_values = len(df["values"])
fig = go.Figure(go.Bar(x=[selected_value], y=[selected_value]))
fig.update_layout(yaxis_range=[0, num_values])
fig.update_layout(
yaxis=dict(
tickmode="array",
tickvals=df["values"],
ticktext=df["labels"],
),
)
fig.update_layout(
xaxis=dict(
tickmode="array",
tickvals=[selected_value],
ticktext=[selected_value],
)
)
return fig
if __name__ == "__main__":
app.run_server()

Related

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'].

plotly dash range slider with datetime and scatterplot interaction

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

Dash Python: Add date-picker and sidebar to dashboard

I have a sidebar and a date-picker in my dashboard built with Dash. However, I am unable to load the graph correctly based on the date range selected. The data source can be found in the link below, however, I changed the column "Year" values to dates in this format "YYYY-MM-DD".
Data Source
Help is much appreciated.
Output errors:
FileNotFoundError: [Errno 2] No such file or directory: 'iranian_students.csv'
ID not found in layout
Attempting to connect a callback Input item to component:
"date-range"
but no components with that id exist in the layout.
If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
`suppress_callback_exceptions=True`.
This ID was used in the callback(s) for Output(s):
students.figure
students.figure
ID not found in layout
Attempting to connect a callback Output item to component:
"students"
but no components with that id exist in the layout.
If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
`suppress_callback_exceptions=True`.
This ID was used in the callback(s) for Output(s):
students.figure
My code:
# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from flask import Flask
import pandas as pd
from pandas import DataFrame
from pandas import json_normalize
import pymongo
from pymongo import MongoClient
import plotly.graph_objs as go
import plotly.offline as pyo
import plotly.express as px
import numpy as np
import pytz, time
from datetime import datetime, tzinfo, timezone, timedelta, date
import dash_bootstrap_components as dbc
import random
from sqlalchemy import create_engine
from plotly.subplots import make_subplots
# %%
# constants
df = pd.read_csv('Bootstrap\Side-Bar\iranian_students.csv')
localTimezone = pytz.timezone('Africa/Cairo')
datetimeNow = datetime.now(localTimezone)
# %%
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.FLATLY],
meta_tags=[{'name': 'viewport',
'content': 'width=device-width, initial-scale=1.0'}]
)
# %%
def serve_layoutOnReload():
# styling the sidebar
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
sidebar = html.Div(
[
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"Number of students per education level", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Page 1", href="/page-1", active="exact"),
dbc.NavLink("Page 2", href="/page-2", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
content = html.Div(id="page-content", children=[], style=CONTENT_STYLE)
web_layout = html.Div([
dcc.Location(id="url"),
sidebar,
content
])
return web_layout
app.layout = serve_layoutOnReload
# app.layout = html.Div([
# dcc.Location(id="url"),
# sidebar,
# content
# ])
# %%
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/":
return [
html.H1('Kindergarten',
style={'textAlign':'center'}),
dbc.Container([
dbc.Row([
dbc.Col([
html.P("Date picker:", className='text-right font-weight-bold mb-4'),
],xs=12, sm=12, md=12, lg=5, xl=5),
dbc.Col([
dcc.DatePickerRange(id='date-range',
min_date_allowed=date(2020, 6, 1),
max_date_allowed=datetimeNow.date(),
start_date=datetimeNow.date() - timedelta(days=7),
end_date=datetimeNow.date(), ),
],xs=12, sm=12, md=12, lg=5, xl=5),], no_gutters=True, justify='center'),
dbc.Row(
dbc.Col(html.H2(" ", className='text-center text-primary mb-4'), width=12)
),
dbc.Row([
dbc.Col([
dcc.Graph(id="students", figure={}),
], xs=12, sm=12, md=12, lg=5, xl=5),], justify='center')
], fluid=True)
]
elif pathname == "/page-1":
return [
html.H1('Grad School',
style={'textAlign':'center'}),
dcc.Graph(id='bargraph',
figure=px.bar(df, barmode='group', x='Date',
y=['Girls Grade School', 'Boys Grade School']))
]
elif pathname == "/page-2":
return [
html.H1('High School',
style={'textAlign':'center'}),
dcc.Graph(id='bargraph',
figure=px.bar(df, barmode='group', x='Date',
y=['Girls High School', 'Boys High School']))
]
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
# %%
#app.callback([Output("students", "figure")],
[Input("date-range", "start_date"),
Input("date-range", "end_date")])
def update_charts(start_date, end_date):
# read data upon refresh browser
df = pd.read_csv('iranian_students.csv')
# masks
mask1 = (
(df.Date >= pd.to_datetime(start_date))
& (df.Date <= pd.to_datetime(end_date))
)# daily sgym signups
# filtered dataframes
filtered_data1 = df.loc[mask1, :].reset_index(drop=True)
# plots
# daily sgym signups
trace = []
trace.append(go.Bar(
x = filtered_data1['Date'],
y = filtered_data1['Girls Kindergarten'],
name = 'Count of students'
))
# add moving average
filtered_data1['Moving Avg'] = filtered_data1['Girls Kindergarten'].rolling(window=7, min_periods=1).mean()
trace.append(go.Scatter(x=filtered_data1['Date'], y=filtered_data1['Moving Avg'], name = 'Rolling Mean=7'))
students_fig = {'data': trace,
'layout': go.Layout(
{"title": {"text": "Student Count\n"
'('+str(start_date)+' to '+str(end_date)+')',
"x": 0.05, "xanchor": "left"},
"xaxis": {"fixedrange": False, 'title':'Date',
'tickmode':'linear', 'automargin':True},
"yaxis": {"fixedrange": False, 'title':'Count of Signups'},
'xaxis_tickformat':'%d %b',
'title_font_size': 14,
'hovermode':'closest',
'legend_title_text':'Gym',
'hovermode':'closest',
},) }
return students_fig
# %%
if __name__=='__main__':
app.run_server(debug=True, port=3000)
The first error is telling you exactly where the problems start. The problem is in the file path that you define when updating the graph.
instead of this:
def update_charts(start_date, end_date):
# read data upon refresh browser
df = pd.read_csv('iranian_students.csv')
You need to do this:
def update_charts(start_date, end_date):
# read data upon refresh browser
df = pd.read_csv('Bootstrap\Side-Bar\iranian_students.csv')
the second problem is that you
call function serve_layoutOnReload without the brackets?
app.layout = serve_layoutOnReload
instead of
app.layout = serve_layoutOnReload()
The third problem is in how you define your layout. See this discussion for reference.This discussion from January is using the same code/database that you have and they are facing the same issues.
I would try defining the layout directly like this:
app.layout = html.Div([])

Connecting graphs with dropdown (Plotly Dash)

I am building the app in which I want to plot two separate graphs from two data frames. I would like to use the dropdown to display only one graph per page based on each data frame (df,df1).
I followed the Plotly reference ​and couldn't reproduce it on my example.
This is my code so far that works (it displays both graphs on the same page, without dropdown):
import pandas as pd
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
fig = px.bar(df, x=df['Color'], y=df['Number'])
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph',
figure=fig
),
html.Div(children='Letter', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph1',
figure=fig1
)
])
if __name__ == '__main__':
app.run_server(debug=True)
This is what I would like to get:
What would be the best approach to do this?
Thanks in advance.
I don't have much experience with Dash, but here's a great answer and your code throughout. The point is to introduce a dropdown so that the initial value is displayed as fig. Depending on the return value of the callback function, I use the if function to switch the graph.
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
html.Div([
html.Label(['Choose a graph:'],style={'font-weight': 'bold'}),
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'graph1', 'value': 'graph1'},
{'label': 'graph2', 'value': 'graph2'},
],
value='graph1',
style={"width": "60%"}),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output('graph', 'figure'),
[Input(component_id='dropdown', component_property='value')]
)
def select_graph(value):
if value == 'graph1':
fig = px.bar(df, x=df['Color'], y=df['Number'])
return fig
else:
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
return fig1
if __name__ == '__main__':
app.run_server(debug=True)

In Dash DataTable, how to edit a datatable from the same callback function where data and chart was rendered?

Wondering, i have an app where i load the chart and the table from a callback, now, once that happends i need to edit the table and get the chart changed, i was trying to use: derived_virtual_row_ids, but when i add this in the input and run the server i cannot edit and the server goes crazy like updating every 3 miliseconds,
from dash.dependencies import Input, Output
import dash_table as dt
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import pymysql as psql
from django_plotly_dash import DjangoDash
import plotly.graph_objects as go
app2.layout = html.Div([
dcc.Graph(id='table-editing-simple-output'),
dcc.Interval(
id='interval_',
interval=2000 * 1000,
n_intervals=0
),
dt.DataTable(id="table",
columns= [
{"name": x, "id": x} for x in params
],
data=[],
editable=True,
sort_action="native",
sort_mode='multi',
row_selectable='multi',
row_deletable=False,
selected_rows=[],
page_action='native',
page_current= 0,
page_size= 10,
style_cell={
'whiteSpace': 'normal',
'height': 'auto',
},
),
html.Div(id='datatable-interactivity-container')
])
#app2.callback(
[Output('table-editing-simple-output', 'figure'), Output('table', 'data')],
[Input("interval_", "n_intervals"), Input("table", "derived_virtual_row_ids"]
)
def display_output(rows):
print(rows)
dff = pd.DataFrame(rows)
#df = pd.DataFrame(rows, columns=[c['name'] for c in columns])
qry = """SELECT * FROM new_schema_excel.xto_dvd_larger_file WHERE id < 25"""
cnx = psql.connect(db='', user='', passwd='', port=3306)
df = pd.read_sql_query(qry, cnx)
x=df['id']
y=df['End Depth']
y2 = df['Start Depth']
chart = go.Scatter(
x=x,
y=y
)
chart2 = go.Scatter(
x=x,
y=y2
)
data = [chart, chart2]
layout = go.Layout(
paper_bgcolor="white",
plot_bgcolor='white',
yaxis=dict(autorange='reversed'),
hovermode='closest',
xaxis=dict(showspikes=True),
showlegend=True
)
return {
'data': data, "layout": layout}, df.to_dict('records')
how can i accomplish to pass the data from datatable to the chart? do i need another callback?
Yes. The flow would have to be callback_1 updates the table, and callback_2 uses the table to update the chart.
Right now your table is using one of its own props as an input to update itself, and I'm actually amazed that Dash allowed. This is essentially a cyclical callback, which Dash usually won't allow because it would mean the callback calls itself in an infinite loop.
You also have two inputs to your existing callback, but only one arg in the function signature (rows). You should have a separate arg for each input (ex. (n_intervals, rows)).

Categories