Assign component properties from dict or other variable in Plotly Dash - python

In Plotly Dash, you construct a dashboard using HTML or HTML-like components. Now there are many more parameters that can be assigned to "Core" or HTML components, many of which may be commonly shared among components. What I'm trying to figure out is how to store common parameters in a data structure, and deploy that to a variety of components. This would either before cleaning up and streamlining verbose parameters into a short and tidy variable name, or tie parameters to one commonly set point for easier modification. An example:
You can change the persistence of data on refresh or browser close with persistence = True, persistence_type ='memory' in the component declaration. Expanding the snippet above:
from dash import Dash, dcc, html
app = Dash(__name__)
app.layout = html.Div([
dcc.Checklist(
['New York City', 'Montréal', 'San Francisco'],
['Montréal', 'San Francisco'],
inline=True,
persistence = True,
persistence_type = 'memory'
)
dcc.Checklist(
['Steakhouse', 'Seafood', 'Italian'],
['Steakhouse'],
inline=True,
persistence = True,
persistence_type = 'memory'
)
])
if __name__ == '__main__':
app.run_server(debug=True)
What I would like to do is store these parameters as a data structure and assign them to the component dynamically.
from dash import Dash, dcc, html
persist = {'persistence':True, 'persistenceType':'memory'}
app = Dash(__name__)
app.layout = html.Div([
dcc.Checklist(
['New York City', 'Montréal', 'San Francisco'],
['Montréal', 'San Francisco'],
inline=True,
persist
)
dcc.Checklist(
['Steakhouse', 'Seafood', 'Italian'],
['Steakhouse'],
inline=True,
persist
)
])
if __name__ == '__main__':
app.run_server(debug=True)
For this specific example, I know I can have the values True and memory set in a variable, and using that var in the parameter value, but this is just one example and I have some more verbose and challenging situations where I would like to have both the parameter and parameter value stored in a data structure. Is there any way I can achieve that?

You can use the **kwargs syntax to expand/unpack keyword arguments given a dictionary of parameters, for example :
persist = {'persistence': True, 'persistence_type': 'memory'}
app.layout = html.Div([
dcc.Checklist(
['New York City', 'Montréal', 'San Francisco'],
['Montréal', 'San Francisco'],
inline=True,
**persist
)
dcc.Checklist(
['Steakhouse', 'Seafood', 'Italian'],
['Steakhouse'],
inline=True,
**persist
)
])
#see Unpacking With the Asterisk Operators

Related

How To Make Dash DataTables Responsive [Height & Weight] dynamically to fit in any device?

I'm going through the Dash Tables design to create a webpage with data preview, But the problem I'm facing is it's not responsive to browser, Though Column wise it's responsive enough but the height it's not helping at all. I'm using the bellow python code for the same.
from dash import Dash, dash_table
import pandas as pd
from collections import OrderedDict
app = Dash(__name__)
df = pd.DataFrame(OrderedDict(
[
[
'Column {}'.format(i + 1), list(range(30))
] for i in range(15)
]
))
app.layout = dash_table.DataTable(
data=df.to_dict('records'),
columns=[{'id': c, 'name': c} for c in df.columns],
virtualization=True,
fixed_rows={'headers': True},
style_cell={'minWidth': 95, 'width': 95, 'maxWidth': 95},
style_table={"overflowY":"auto",'maxHeight':'75vh'} # default is 500
)
if __name__ == '__main__':
app.run_server(debug=True)
I'm not getting any proper config to make it responsive height wise. Few ref. doc's I've came across are as follows:
https://dash.plotly.com/datatable/height
https://dash.plotly.com/datatable/style
https://community.plotly.com/t/dash-data-table-not-displayed-on-full-page/68332/14
Adjusting the MaxHeight of a Table in Dash Tables Python
Can someone help here? How can I make this table fully responsive for any screen size ?

I am trying to create a dash app for a Restaurant directory the dropdown filter is work I know but map doesn't update

I have restaurant name, cusines, lat , long as columns. The map I am trying to show on the html page is not updating as per the filter , It shows all the restaurnats.
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import pandas as pd
import plotly.express as px
app = dash.Dash('app')
df = pd.read_excel('Resturants.XLSX')
app.layout = html.Div([
html.H1('Resturant map by cusine', style={'text-align': 'center'} ),
dcc.Dropdown(id='cusine_dd',
options=[{'label': i, 'value': i} for i in df.cusines.unique()],
value = 'Chinese',
style={'width':'40%'}),
html.Div(id= 'output', children =[] ),
dcc.Graph(id='mapbycusine', figure={})
])
#app.callback(
[Output(component_id='output', component_property='children'),
Output(component_id='mapbycusine', component_property='figure')],
[Input(component_id='cusine_dd', component_property='value')]
)
def update_figure(input_cusine):
#cusine_filter = 'All'
container = f"Currently showing {input_cusine}"
df_copy = df.copy(deep=True)
if input_cusine:
df_copy = df_copy[df_copy['cusines'] == input_cusine]
print(df_copy.head())
fig = px.scatter_mapbox(data_frame=df_copy, lat='Latitude', lon='Longitude', zoom=3, height=300, hover_data= ['cusines'])
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(height=1000, margin={"r":100,"t":100,"l":100,"b":300})
return container, fig
if __name__ == '__main__':
app.run_server(debug=True)
I am checking every step just to make sure if input and output is working I have used countainers updates on the html page and print the df_copy after filtering data as per the input cusine. The containers give the correct output and the df.copy.head() shows only filltered columns in the console but the map on html dash app is not updating.
I want the map to show the restaurants for just the selected cusine
I cannot reproduce your error, but perhaps some of the steps I took in debugging might prove helpful:
First, I took the first 100 rows of a restaurant dataset from kaggle, and then randomly assigned three different cuisine types to each unique restaurant name. Then when creating the fig object with px.scatter_mapbox, I passed the cuisine column to the color argument and a mapping between cuisine and color to the color_discrete_map argument so that the marker colors will be different when you select different cuisine types from the dropdown.
This is the result, with visible changes between the marker locations as well as the marker colors (depending on the dropdown selection). Also, as you mentioned, the print outs of df_copy from inside your update_figure function match the dropdown selections as expected.
As far as I can tell, the only thing fundamentally different between your dashboard and mine would be the data that was used. Maybe it is possible that you have a bunch of duplicate rows that span multiple cuisines (i.e. imagine I took a data set of only Mexican restaurants, then duplicated all of the rows but switched the cuisine to Italian – then if you select Mexican or Italian from the dropdown, the result would be the same because you're plotting all of the same latitudes and longitudes in each case so the figure wouldn't appear to change, and you might not catch this from examining the df_copy print outs).
Can you verify that your data set doesn't contain duplicate latitude and longitude points with different cuisines assigned to the same point?
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import numpy as np
import pandas as pd
import plotly.express as px
app = dash.Dash('app')
# df = pd.read_excel('Resturants.XLSX')
## create a small restaurant dataset similar to yours
## downloaded from: https://www.kaggle.com/datasets/khushishahh/fast-food-restaurants-across-us?resource=download
df = pd.read_csv('Fast_Food_Restaurants_US.csv', nrows=100, usecols=['latitude','longitude','name'])
df.rename(columns={'latitude':'Latitude', 'longitude':'Longitude'}, inplace=True)
## randomly assign some cuisine types to unique restaurant names
unique_names = df['name'].unique()
np.random.seed(42)
name_cuisine_map = {
name:cuisine for name,cuisine in
zip(unique_names, np.random.choice(['American','Mexican','Chinese'], size=len(unique_names)))
}
cuisine_color_map = {
'American':'blue',
'Mexican':'green',
'Chinese':'red'
}
df['cusines'] = df['name'].map(name_cuisine_map)
df['color'] = df['cusines'].map(cuisine_color_map)
app.layout = html.Div([
html.H1('Resturant map by cusine', style={'text-align': 'center'} ),
dcc.Dropdown(id='cusine_dd',
options=[{'label': i, 'value': i} for i in df.cusines.unique()],
value = 'Chinese',
style={'width':'40%'}),
html.Div(id= 'output', children =[] ),
dcc.Graph(id='mapbycusine', figure={})
])
#app.callback(
[Output(component_id='output', component_property='children'),
Output(component_id='mapbycusine', component_property='figure')],
[Input(component_id='cusine_dd', component_property='value')]
)
def update_figure(input_cusine):
#cusine_filter = 'All'
container = f"Currently showing {input_cusine}"
df_copy = df.copy(deep=True)
if input_cusine:
df_copy = df_copy[df_copy['cusines'] == input_cusine]
print("\n")
print(df_copy.head())
fig = px.scatter_mapbox(data_frame=df_copy, lat='Latitude', lon='Longitude', zoom=3, height=300, hover_data= ['cusines'], color='cusines', color_discrete_map=cuisine_color_map)
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(height=1000, margin={"r":100,"t":100,"l":100,"b":300})
return container, fig
if __name__ == '__main__':
app.run_server(debug=True)

How to get data about state which is currently hovered? Plotly Choropleth - USA map

I created an interactive map of individual US states. The map will contain information on electric vehicles in the US. Currently, it is colored depending on the range (average in kilometers) of a given vehicle.
Here is my code:
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('https://gist.githubusercontent.com/AlbertKozera/6396b4333d1a9222193e11401069ed9a/raw/ab8733a2135bcf61999bbcac4f92e0de5fd56794/Pojazdy%2520elektryczne%2520w%2520USA.csv')
for col in df.columns:
df[col] = df[col].astype(str)
df['range'] = pd.to_numeric(df['range'])
df_range = df.drop(columns = ['state', 'brand', 'model', 'year of production', 'type']).groupby('code', as_index=False)
df_range_mean = df_range.agg({'range':'mean'})
fig = go.Figure(data=go.Choropleth(
locations=df['code'].drop_duplicates(keep='first').reset_index(drop=True),
z = round(df_range_mean['range'], 2),
locationmode='USA-states',
colorscale='Reds',
autocolorscale=False,
marker_line_color='black',
))
fig.update_layout(
geo = dict(
scope='usa',
projection=go.layout.geo.Projection(type = 'albers usa'),
showlakes=True, # lakes
lakecolor='rgb(255, 255, 255)'),
)
fig.show()
It looks like this:
Here is my question:
I need to dynamically return information about the given state in which the mouse cursor is currently located. Unfortunately, I don't know how to do it and whether it is possible at all. I have to implement a method that will display a different image (chernoff face) depending on what state is currently highlighted by the user.
Can anyone tell me if there is any method that will return data about the currently highlighted state? Or maybe, unfortunately - I will have to write my own listener.
I was searching such a method in documentation but I couldn't find it.
The argument locations=df['state'] into the go.Choropleth function should return the abbreviation of the state you refer whenever you point the cursor.

Grid dashboard with Plotly dash

I'm trying to build a dashboard with Dash from Plotly composed of a series of tiles (Text) like in the picture below.
I'm trying to build a component to reuse it an build the layout below. Each box will contain a Title, a value and a description as shown below.
Is there a component available? Can someone help me with any basic idea/code ?
Thanks in advance!
I would recommend checking out Dash Bootstrap Components (dbc).
You can use dbc.Col (columns) components nested into dbc.Row (rows) components to produce your layout. You can check them out here.
Then for the actual 'cards' as I'll call them, you can use the dbc.Card component. Here's the link.
Here's some example code replicating your layout:
import dash_bootstrap_components as dbc
import dash_html_components as html
card = dbc.Card(
dbc.CardBody(
[
html.H4("Title", id="card-title"),
html.H2("100", id="card-value"),
html.P("Description", id="card-description")
]
)
)
layout = html.Div([
dbc.Row([
dbc.Col([card]), dbc.Col([card]), dbc.Col([card]), dbc.Col([card]), dbc.Col([card])
]),
dbc.Row([
dbc.Col([card]), dbc.Col([card]), dbc.Col([card]), dbc.Col([card])
]),
dbc.Row([
dbc.Col([card]), dbc.Col([card])
])
])
Best thing would probably be to have a function which creates those cards with parameters for ids, titles and descriptions to save the hassle of creating different cards:
def create_card(card_id, title, description):
return dbc.Card(
dbc.CardBody(
[
html.H4(title, id=f"{card_id}-title"),
html.H2("100", id=f"{card_id}-value"),
html.P(description, id=f"{card_id}-description")
]
)
)
You can then just replace each card with create_card('id', 'Title', 'Description') as you please.
Another quick tip is that the col component has a parameter, width. You can give each column in a row a different value to adjust the relative widths. You can read more about that in the docs I linked above.
Hope this helps,
Ollie

Plotly / Dash: Multiple filters

I would like to implement a few data filters to preselect the data by certain criteria. These filters should be diagrams itself, i.e. a pie chart (e.g. where one can select a continent) and a time line (e.g. where one can select a time-span). Most importantly, I need to apply multiple filters from mutliple diagrams without them resetting every time I filter by selecting another diagram.
However, I do not know how to implement this. I found something old using dash.dependencies.Events, but that is not supported anymore.
Whenever I filter by a criterion in diagram A and then want to filter by another criterion from diagram B, diagram A gets reset.
Since this is probably a situation encountered by many people, and since dash does not seem to support this natively, I wanted to ask whether anyone has a workaround on this?
//edit: Here is a simple example. I can filter by clicking on a datapoint on the bar graph above. But whenever I click on a point on the line graph below, it resets the settings from the bar graph. I want to keep both.
import datetime
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly
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.Graph(id='graph')
])
# Multiple components can update everytime interval gets fired.
#app.callback(Output('graph', 'figure'),
[Input('graph', 'selectedData')])
def update_graph_live(input):
print(input)
data = {
'x': [1,2,3,4,5],
'y': [1,2,3,4,5],
'a': [0,-1,-2],
'b': [100,101,102]
}
# Create the graph with subplots
fig = plotly.tools.make_subplots(rows=2, cols=1, vertical_spacing=0.2)
fig['layout']['margin'] = {
'l': 30, 'r': 10, 'b': 30, 't': 10
}
fig['layout']['legend'] = {'x': 0, 'y': 1, 'xanchor': 'left'}
fig['layout']['clickmode'] = 'event+select'
fig.append_trace({
'x': data['x'],
'y': data['y'],
'name': 'xy',
'type': 'bar',
}, 1, 1)
fig.append_trace({
'x': data['a'],
'y': data['b'],
'name': 'ab',
'mode': 'lines+markers',
'type': 'scatter'
}, 2, 1)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
Right now it seems your problem is that selecting data in any of the graphs in the figure of the graph component, i.e. the output of your function, triggers the Input(graph,'selectedData') to that same function!
So what you need to do is separate the graphs into separate dcc.Graph things and use dash.dependencies.State to listen to and maintain each graph's selectedData property.
Thank you for your answers. I managed to find a workaround.
First of all, as #russellr mentioned, dash.dependencies.State can be passed, but it will not trigger a callback. I would like callbacks to be triggered on multiple filters without them resetting each other.
Now, for the good people at Dash, disabling this reset would enable endless loops, so it there is a lot of sense in disabling it.
The way I did it is that I introduced Dropdown lists for filtering, and callbacks only go from the value of the Dropdown to the figure of the graph.
I select multiple conditions on the dropdowns, get the (non-interactive) visualisations from it. It might be not as pretty, but the app still got good useability feedback.

Categories