create dynamic columns with dash plotly based on user selection - python

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.

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

Update dashtable columns name dynamically

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)

Dynamically update the title in Dash Plotly

I have created this very simplified example in which I would like to dynamically update the title of a chart as a dropdown label changes.
Here's the code:
data = {'Stock': ['F', 'NFLX', 'AMZN'], 'Number': [10, 15, 7]}
df = pd.DataFrame(data, columns=['Stock', 'Number'])
stock_options = df['Stock']
app = dash.Dash(__name__)
app.layout = html.Div(children=[
html.H1(children='Example Bar Chart'),
html.Div([
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in stock_options],
value='F',
),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output(component_id='graph', component_property='figure'),
Input(component_id='dropdown', component_property='value')
)
def update_graph(stock):
msk = df.Stock.isin([stock])
figure = px.bar(df[msk], x='Stock', y='Number', title=f"{stock} open price")
return figure
if __name__ == '__main__':
app.run_server(debug=True)
So, while keeping the same label in dropdown instead of 'F open price' in the title I would like to get 'Facebook open price'... and so on.
I've tried to solve this with map but I couldn't get it working. Can someone point me in the direction on how to resolve this?
Thanks in advance.
As I wrote in my comment, not sure how you want to store the mapping between symbol name and the displayed title in the graph. Here is one solution that works using a dictionary stock_name_mapping that contains the symbols als keys and the displayed names as values:
import pandas as pd
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
data = {'Stock': ['F', 'NFLX', 'AMZN'], 'Number': [10, 15, 7]}
df = pd.DataFrame(data, columns=['Stock', 'Number'])
stock_options = df['Stock']
stock_name_mapping = {'F': 'Facebook',
'NFLX': 'Netflix',
'AMZN': 'Amazon'}
app = dash.Dash(__name__)
app.layout = html.Div(children=[
html.H1(children='Example Bar Chart'),
html.Div([
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in stock_options],
value='F',
),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output(component_id='graph', component_property='figure'),
Input(component_id='dropdown', component_property='value')
)
def update_graph(stock):
msk = df.Stock.isin([stock])
stock_name = stock_name_mapping[stock]
figure = px.bar(df[msk], x='Stock', y='Number', title=f"{stock_name} open price")
return figure
if __name__ == '__main__':
app.run_server(debug=True)

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

Graph_Update with two dropdown with multiple selection using excel data and dash python

I am trying to update a graph in dash using an excel with data. I have 2 drop down and an excel document with different sheets for each drop down. I couldn't manage yet to select a value from drop down and to charge that data into the table (I have also a table before the graph) and plot values into the graph.
import dash
import dash_auth
import dash_core_components as dcc
import dash_html_components as html
import plotly
import dash_daq as daq
import os
import random
import pandas as pd
import plotly.graph_objs as go
from collections import deque
import psycopg2
from dash.dependencies import Output, Input
DB_NAME = "LicentaTest"
DB_USER = "postgres"
DB_PASS = "admin"
DB_HOST = "localhost"
DB_PORT = "5433"
VALID_USERNAME_PASSWORD_PAIRS = [
['admin1', 'admin']
]
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(_name_, external_stylesheets=external_stylesheets)
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)
df = pd.read_excel('UploadData.xlsx', sheet_name='Total')
def generate_table(dataframe, max_rows=13):
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([
html.H1('Electrica SDEE Transilvania SUD SA'),
html.H4('Select the branch:'),
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in ['ALBA', 'BRASOV',
'COVASNA', 'HARGHITA', 'MURES', 'SIBIU', 'TOTAL']],
value='',
placeholder = 'Select branch'
#multi = True
),
html.H4('Select the year:'),
dcc.Dropdown(
id='dropdown1',
options=[{'label': i, 'value': i} for i in ['2012', '2013',
'2014', '2015', '2016', '2017', '2018', 'ALL']],
value='',
placeholder = 'Select year'
),
html.Br(),
html.Button('OK', id='submit-form'),
html.Br(),
html.Div(children=[
html.H4(children='Own Technological Consumption'),
generate_table(df),
]),
html.Br(),
html.Br(),
html.Br(),
dcc.Graph(id='graph')],
className='container')
#app.callback(
dash.dependencies.Output('graph', 'figure'),
[dash.dependencies.Input('dropdown', 'value')])
def update_graph1(dropdown_value):
df = pd.read_excel('UploadData.xlsx', sheet_name='Total')
X1 = df.Date.values
Y1 = df.CPT.values
data = plotly.graph_objs.Scatter(
x=X1,
y=Y1,
name='Graph',
mode='lines+markers'
)
return {
'data': [data], 'layout' : go.Layout(xaxis=dict(range[min(X1),max(X1)]),
yaxis=dict(range=[min(Y1),max(Y1)]),
)
}
if _name_ == '_main_':
app.run_server(debug=True)
I expect, when select the value Alba from drop down and year 2015 to show me on table all the values related to this and to plot the CPT function of data.

Categories

Resources