Plotly Dash Display Dynamic Number of Plots Based on SQL Query - python

I am trying to get the app to display multiple plots in grid format whenever a user changes the date input. This is what I have thus far. Although, when I run the app right now it only displays one, not ALL, of the graphs. I am not sure how to code this so that the number of graphs displayed is dynamic and that they take up just enough space on the screen of whoever is running the app.
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import pyodbc
from datetime import datetime as dt
app = dash.Dash()
app.layout = html.Div(children=[
html.Div(children='''
Enter Date:
'''),
dcc.DatePickerSingle(id='date_picker_single', date=dt(2017, 12, 18)),
html.Div(id='output-graphs'),
])
def connect_sql_server(driver, server, db):
conn_sql_server = pyodbc.connect(
r'DRIVER={' + driver + '};'
r'SERVER=' + server + ';'
r'DATABASE=' + db + ';'
r'Trusted_Connection=yes;',
autocommit=True
)
return conn_sql_server
#app.callback(
Output(component_id='output-graphs', component_property='children'),
[Input(component_id='date_picker_single', component_property='date')]
)
def update_graphs(input_date):
sql_conn = connect_sql_server('ODBC Driver 13 for SQL Server', 'Server', 'Database')
cursor = sql_conn.cursor()
cursor.execute(
"""
exec QCMonotonicityGraphs ?
""", [input_date])
rows = cursor.fetchall()
sql_data = []
for row in rows:
sql_data.append(list(row))
labels = ['strike', 'last', 'call_put', 'title']
df = pd.DataFrame.from_records(sql_data, columns=labels)
unique_titles = df.title.unique()
graphs = []
for unique_title in unique_titles:
title = unique_title
call_strike = []
call_last = []
for row in rows:
if row[2] == 'C' and row[3] == unique_title:
call_strike.append(row[0])
call_last.append(row[1])
put_strike = []
put_last = []
for row in rows:
if row[2] == 'P' and row[3] == unique_title:
put_strike.append(row[0])
put_last.append(row[1])
graphs.append(dcc.Graph(
id='example-graph',
figure={
'data': [
{
'x': call_strike,
'y': call_last,
'mode': 'lines+markers',
'name': 'call'
},
{
'x': put_strike,
'y': put_last,
'mode': 'lines+markers',
'name': 'put'
}
],
'layout': {
'title': title
}
}
))
return graphs
if __name__ == '__main__':
app.run_server(debug=True)
Example

Related

How to set validation in dash?

The following code has first two columns with getpass.getuser() values and rest columns are normal with editable cells
import pandas as pd
import dash
from dash import dash_table, html
from dash.dependencies import Input, Output, State
import getpass
df = pd.read_csv('df.csv')
current_user = getpass.getuser()
df = df[(df.iloc[:, 0] == current_user) | (df.iloc[:, 1] == current_user)]
app = dash.Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(
id='datatable',
columns=[
{'name': df.columns[0], 'id': df.columns[0], 'editable': False},
{'name': df.columns[1], 'id': df.columns[1], 'editable': False},
*[{'name': i, 'id': i, 'editable': True} for i in df.columns[2:]]
],
data=df.to_dict('records'),
editable=True
),
html.Button('Save Changes', id='save-button'),
html.Div(id='output')
])
#app.callback(Output('datatable', 'data'),
Output('output', 'children'),
Input('save-button', 'n_clicks'),
State('datatable', 'data'))
def save_changes(n_clicks, data):
if n_clicks:
df_new = pd.DataFrame.from_dict(data)
df_new = df_new[(df_new.iloc[:, 0] == current_user) | (df_new.iloc[:, 1] == current_user)]
df_new.to_csv('df.csv', index=False)
df_check = pd.read_csv('df.csv')
if df_check.equals(df_new):
return df_new.to_dict('records'), 'Changes saved!'
else:
raise Exception('Error saving changes.')
return data, ''
if __name__ == '__main__':
app.run_server(debug=True)
how to put validation in such a way that -
if the current user has getpass.getuser() values in first column, they can only edit and save data table values in df.csv only if they put "Sz", "Tx", "Ux" as values
if the current user has getpass.getuser() values in second column, they can only edit and save data table values in df.csv only if they put "Qr", "Fd", "Wv" as values

create dynamic columns with dash plotly based on user selection

My idea is that my dash_table.Datatable insert new columns based on what the user select in my dropdown.
I've experimented different approachs to receive back a dataframe, dict or list with my actual columns and be able to update my table but either of these worked. Below a reproduceble example:
import pandas as pd
import dash_mantine_components as dmc
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash import html
from dash import Dash, Input, Output, dash_table, State
from dash.exceptions import PreventUpdate
import plotly.express as px
import json
from dash_bootstrap_templates import load_figure_template
import datetime
import base64
dff = {'Nome':['Random1','Random2','Random3','Random4'],'tipo':['Random11','Random22','Random33','Random44'],'a': ['1','2','3','4',], 'b':['b1','b2','b3','b4'], 'c':['c1','c2','c3','c4'], 'd':['d1','d2','d3','d3'],'Indicator Name':['a','b','c','d']}
dff = pd.DataFrame(dff)
db_filtered = pd.DataFrame(dff)
app = Dash(__name__, external_stylesheets= [dbc.themes.DARKLY, dbc_css])
#---------------------------------------------------------------------------------------------------------------------------------------#
# 1. FUNCTIONS CONSTRUCTION
# 1.1 - table_creation
#---------------------------------------------------------------------------------------------------------------------------------------#
#------------------------------------------------------- 1.1 ---------------------------------------------------------------------------#
def table_creation(db_filtered):
db_as_list = db_filtered.columns.to_list()
#print(db_as_list)
dash_table.DataTable(
data = [{
}],
columns = [{"name": i,
"id": i
} for i in db_as_list], #<------------ setting columns of datatable
editable=False, #<------------ setting preferences if is or not editable
filter_action="native", #<------------ Filters in columns
sort_action="native", #<------------
sort_mode="multi", #<------------
column_selectable="single", #<------------ Selectable only one column
row_selectable=False, #<------------ dont let select just a row
selected_columns=[], #<------------ no column is selected previously
selected_rows=[], #<------------ no row is selected previously
page_action="native",
page_current= 0, #<------------ First page will be show
page_size= 25) #<------------ n of funds to be showed
#---------------------------------------------------------------------------------------------------------------------------------------#
#---------------------------------------------------------------------------------------------------------------------------------------#
app.layout = html.Div([
#------------------------------------------------------- ---------------------------------------------------------------------------#
dbc.Card(
dbc.CardBody([
# ------------------------------------------------------------------------------------------------------------------#
dbc.Row([
dbc.Col([
html.Div([
dbc.Button("b1", color="warning", id='todos_button', n_clicks_timestamp='0'),
dbc.Button("b2", color="light",id='multimercado_button', n_clicks_timestamp='0' ),
dcc.Dropdown(dff['Indicator Name'].unique(), id = 'dropdown'),
])
]),
]),
#---------------------------------------------------------------------------------------------------------------------------------#
dbc.Row([
dbc.Col([
html.Div([
dash_table.DataTable(table_creation(db_filtered), id = 'tabela-dash',#calling the function with dff, but only will be displayed if the user use the button
)], className="m-4 dbc")
])
])
])
)
])
#---------------------------------------------------------------------------------------------------------------------------------------#
#---------------------------------------------------------------------------------------------------------------------------------------#
#-------------------------------------------------------- 3.1 --------------------------------------------------------------------------#
#app.callback(Output('tabela-dash', 'data'),
Input('multimercado_button','n_clicks_timestamp'),
Input('todos_button','n_clicks_timestamp')
)
def display(multimercado_button, todos_button ):
if int(multimercado_button) > int(todos_button): #Multimercado button
value = 'Macro Alta Vol'
db_filtered = pd.DataFrame(dff[dff['tipo'] == value])
db_filtered = db_filtered.loc[:,['Nome','tipo']]
print(db_filtered)
db_filtered = db_filtered.to_dict('Records')
return db_filtered #<------------ Extracting data as dict, easy way to do
elif int(todos_button) > int(multimercado_button):
db_filtered = pd.DataFrame(dff)
db_filtered = db_filtered.loc[:,['Nome','tipo']]
print(db_filtered)
db_filtered = db_filtered.to_dict('Records')
return db_filtered#<------------ Extracting data as dict, easy way to do
else:
db_filtered = pd.DataFrame(dff)
db_filtered = db_filtered.loc[:,['Nome','tipo']]
db_filtered = db_filtered.to_dict('Records')
return db_filtered #<------------ Extracting data as dict, easy way to do
#-------------------------------------------------------- 3.2 --------------------------------------------------------------------------#
#app.callback(
Output('tabela-dash', 'columns'),
[Input('dropdown', 'value')],
[State('tabela-dash', 'columns')]
)
def update_table(value, columns):
if value is None:
print('test 1: Value is None')
else:
print('test 1: Value is not None')
raise PreventUpdate
if columns is None:
print('test 2: Columns is None')
else:
print('test 2: Columns in not None')
app.run_server(debug=True, use_reloader=False)
I dont know why, but this code should work like this.
import dash
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from dash_table import DataTable, FormatTemplate
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(
id='dropdown',
options=[{
'label': 'label: ' + id,
'value': id
} for id in ['b', 'c', 'd', 'e', 'f']]
),
DataTable(
id='table',
columns=[{
'name': x,
'id': x,
'selectable': True
} for x in ['a']],
column_selectable="single",
data=[{
'a': 'a' + str(x),
'b': 'b' + str(x),
'c': 'c' + str(x),
'd': 'd' + str(x),
'e': 'e' + str(x),
'f': 'f' + str(x)
} for x in range(0,100)]
)
])
#app.callback(
Output('table', 'columns'),
[Input('dropdown', 'value')],
[State('table', 'columns')]
)
def update_columns(value, columns):
if value is None or columns is None:
raise PreventUpdate
inColumns = any(c.get('id') == value for c in columns)
if inColumns == True:
raise PreventUpdate
columns.append({
'label': 'label: ' + value,
'id': value
})
return columns
if __name__ == '__main__':
app.run_server(debug=False)
i can imagine that my problem is that the function dont bring back the columns.

Dynamic update Dash Datatable from df

I'm creating an auto-update Dash datatable. The data is obtained from a SQL Server Query, passed to a Dataframe and displayed in the datatable:
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
import numpy as np
import pyodbc
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.MINTY, 'https://use.fontawesome.com/releases/v5.9.0/css/all.css'])
sql_query = ('''SELECT * FROM HOSVITAL.dbo.Censo_Diario
ORDER BY Dias_Estancia DESC''')
server = '#srv'
database = '#db'
username = '#usr'
password = '#pwd'
conn = pyodbc.connect('DRIVER={SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
df = pd.read_sql(sql_query,conn)
df = df.apply(lambda x: x.str.strip()
if x.dtype == "object" else x) # Trim whitespaces
After that, i'm defining a function for connect to my DB and another function for get and clean the data and return it as a dict:
def connectSQLServer(conn):
connSQLServer = conn
return connSQLServer
def getData():
df2 = pd.DataFrame()
for idx in range(10):
data = pd.read_sql(sql_query,conn)
df2 = df.apply(lambda x: x.str.strip()
if x.dtype == "object" else x)
df2 = df2.append(data, ignore_index=True)
df2 = df2.drop_duplicates().sort_index()
return df2.to_dict('records')
I´m giving custom name to the df columns:
tblcols=[{'name': 'Pabellon', 'id': 'Pabellon'},
{'name': 'Cama', 'id': 'Cama'},
{'name': 'Cedula', 'id': 'Cedula'},
{'name': 'Tipo', 'id': 'Tipo'},
{'name': 'EPS', 'id': 'EPS'},
{'name': 'Nivel', 'id': 'Nivel'},
{'name': 'Ingreso', 'id': 'Ingreso'},
{'name': 'Nombre', 'id': 'Nombre'},
{'name': 'Edad', 'id': 'Edad'},
{'name': 'Cod', 'id': 'Cod'},
{'name': 'Diagnostico', 'id': 'Diagnostico'},
{'name': 'Fecha Ingreso', 'id': 'Fecha_Ingreso'},
{'name': 'Dias Estancia', 'id': 'Dias_Estancia'}
]
Create my layout adding a Dcc.interval for update the data every 5 seconds:
app.layout = html.Div([
html.H4('CN Dashboard'),
dcc.Interval('graph-update', interval = 5000, n_intervals = 0),
dbc.Container([
dash_table.DataTable(
sort_action='native',
id = 'table',
css=[{
'selector': '.dash-cell div.dash-cell-value',
'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
}],
data = getData(),
columns=tblcols,]
)
Finally, I create this callback for update the table:
#app.callback(
dash.dependencies.Output('table','data'),
[dash.dependencies.Input('graph-update', 'n_intervals')])
def updateTable(n):
return getData()
if __name__ == '__main__':
app.run_server(debug=False, host='0.0.0.0', port = 8070)
app.title = 'Tablero CN' # appears in browser title bar
So, this App is displaying the data and it´s updating as expected, but the problem is that all rows are being duplicated in the Output.
Any suggestions?
Thanks in advance.
Error(Invalid prop for this component) - basically it is stating that dash_bootstrap_components.Table does not support data argument where you are trying to push update using callback. dash table has this feature. seems you imported it in code but didn't use. As far as I know it can't be solved with dbc. you can use dash_table if you really want this callback.
Query Re-usability - if both queries are same why not define query as variable in top as you did with conn and pass it as argument.
example:
query = '''select * from table'''

Dash Plotly error : Stuck on Loading... page after server run

Could you please help me with this error.
The page stuck with loading.... message but never loads.
There's no error on server run.
This is my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objects as go
import numpy as np
df1 = pd.read_csv('lines.csv', encoding='latin-1', low_memory=False)
df1.rename(columns={"Order Type": "OrderType"}, inplace=True)
df1.loc[(df1.Processed != '?'), 'OrderTypeProcessed'] = df1['Qty']
df1.loc[(df1.Processed == '?'), 'OrderTypeProcessed'] = 0
df2 = df1.groupby('OrderType').sum()
df3 = df1.groupby(' Packout station Number').sum()
df4 = df1.groupby('Packout station Operator').sum()
df5 = df1.groupby('Product Category').sum()
df1.rename(columns={df1.columns[12]:'Received Time'}, inplace=True)
df6 = df1.groupby('Received Time').sum()
df7 = df1.groupby('Cut Off Time').sum()
df2.reset_index(inplace=True)
df3.reset_index(inplace=True)
df4.reset_index(inplace=True)
df5.reset_index(inplace=True)
df6.reset_index(inplace=True)
df7.reset_index(inplace=True)
df5['Product Category'] = df5['Product Category'].str.upper()
app = dash.Dash()
app.layout = html.Div([
html.H1(children = "Dashboard Para Gestão De Produção - Caio",
style = {'textAlign' : 'center',}),
html.Div(children = "_______________________________",
style = {'textAlign' : 'center',}),
dcc.Graph(
id = 'lines-chart',
figure = {
'data' : [
{'x': df2['OrderType'], 'y': df2['Qty'], 'type': 'bar', 'name': 'Dropado'},
{'x': df2['OrderType'], 'y': df2['OrderTypeProcessed'], 'type': 'bar', 'name': 'Realizado'}
],
'layout' : {
'title': 'Grafico'
}
}
)
])
if __name__ == '__main__':
app.run_server(port =4050)
Could you please take a look, I tried call assets in app, however it also didn't work.
Here is the page after server run.
Here is the image that not load in question.

How to speed up Dash App with Pandas Groupby

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

Categories

Resources