How to update marker positions in a scatter mapbox? - python

I'm trying to display live location data on a mapbox scatter plot. In order to mimic new data received from the server the callback moves all points every 3 seconds:
import plotly.express as px
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
px.set_mapbox_access_token(open(".mapbox_token").read())
df = px.data.carshare()
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='map', animate=True),
dcc.Interval(
id='interval-component',
interval=3000,
)
])
#app.callback(Output('map', 'figure'), [Input('interval-component', 'n_intervals')])
def update_map(n):
df['centroid_lon'] += 0.01
fig = px.scatter_mapbox(df, lat="centroid_lat", lon="centroid_lon")
return fig
if __name__ == '__main__':
app.run_server(debug=True)
While the labels are correctly changing their location, the markers are stuck at their original positions.
result

I found a work around by having two callbacks.
my html looks like this dbc.Col > dcc.Graph(figure = fig)
#app.callback(
Output('graph-id','figure'),
Input('control-id', 'n_clicks')
)
def update_func(scatter_map_fig):
return go.Figure
The second callback returns a new graph component with the updated information of the figure
#app.callback(
Output('col-id','children'),
Input('graph-id', 'figure'),
State('date_range','start_date'),
State('date_range','end_date'),
)
def update_func_2(scatter_map_fig):
scatter_fig = go.Figure()
scatter_fig.update_layout(...)
... (put your figure logic here)
return dcc.Graph(figure=fig)
It is a bit janky but it works until there's a better solution. Hope it helps.

Related

Switching plots with radio button screws up the format

I am trying to do something relatively simple. I want to create a radio button that switches between two plotting formats. The radio button switches between two options. Let us call them 'a' and 'b.'
When 'a' is selected, I want to plot two figures side by side using bootstrap row/col. When option 'b' is selected, I want to use the entire column for a single figure.
I believe I coded that correctly below, but when I switch from 'a' to 'b' and back to 'a', the plot screws up the plot and the two figures show up vertically.
import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
import plotly.graph_objs as go
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div(
children=html.Div(children=[
dcc.RadioItems(id='radio', options=('a', 'b'), value='a', inline=True),
dbc.Row(id='row')
])
)
#app.callback(
Output("row", "children"),
[Input("radio", "value")],
)
def fill_row(radio):
f1 = go.Figure()
f2 = go.Figure()
f3 = go.Figure()
if radio == 'a':
return [
dbc.Col(dcc.Graph(figure=f1)),
dbc.Col(dcc.Graph(figure=f2)),
]
else:
return [
dbc.Col(dcc.Graph(figure=f3)),
]
if __name__ == "__main__":
app.run_server(debug=True, port=8051)
Here are the screenshots:
The default behavior for dbc.Col components filling up a dbc.Row component is a horizontal alignment, so I can see the confusion here. However, the default size of column components is 12. And if you're trying to put two components of that size next to eachother will cause an overflow, and the columns will be stacked on top og eachother. Therefore, a possible solution is to set width = 6 for the columns in your first return statement. Or other numbers that do not sum to more than 12:
Solution
#app.callback(
Output("row", "children"),
[Input("radio", "value")],
)
def fill_row(radio):
f1 = go.Figure()
f2 = go.Figure()
f3 = go.Figure()
if radio == "a":
return [
dbc.Col([dcc.Graph(figure=f1)], width=6),
dbc.Col([dcc.Graph(figure=f2)], width=6),
]
else:
return [
dbc.Col(dcc.Graph(figure=f3), width=12),
]
Below are a few images from a test run. You'll find a complete code snippet at the end of the answer
Output 1: radio = a
Output 2: radio = b
Output 1: radio = a again
Complete code:
import dash
from dash import dcc, html, Input, Output
import dash_bootstrap_components as dbc
import plotly.graph_objs as go
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div(
children=html.Div(
children=[
dcc.RadioItems(id="radio", options=("a", "b"), value="a", inline=True),
dbc.Row(id="row"),
]
)
)
#app.callback(
Output("row", "children"),
[Input("radio", "value")],
)
def fill_row(radio):
f1 = go.Figure()
f2 = go.Figure()
f3 = go.Figure()
if radio == "a":
return [
dbc.Col([dcc.Graph(figure=f1)], width=),
dbc.Col([dcc.Graph(figure=f2)], width=6),
]
else:
return [
dbc.Col(dcc.Graph(figure=f3), width=12),
]
# if __name__ == "__main__":
app.run_server(debug=True, port=8052)

Produce multiple plots using dash callbacks

I'm not clear how I could dynamically create multiple charts at once - or if that is not possible then how I could loop through a list of values using a single callback.
For example in the code below list of continents is a a list of filter options. Is it possible to basically make it so when this page loads, I see 5 charts automatically?
Currently, what I'm doing is I have to type 5 #app.callback...make_scatter_plot(option=_dropdown_value) which ends up with a million lines of code in my file and makes it hard to read even though everything is doing the same thing.
What am I missing? Thanks
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import numpy as np
app = Dash(__name__)
df = px.data.gapminder()
list_of_continents = ["Asia", "Africa", "Europe", 'Oceania', 'Americas']
app.layout = html.Div([
html.H4('Restaurant tips by day of week'),
dcc.Dropdown(
id="dropdown",
options=list_of_continents,
multi=False
),
dcc.Graph(id="graph"),
#dcc.Graph(id ='graph2') ##????
])
#app.callback(
Output("graph", "figure"),
Input("dropdown", "value")
)
def make_scatter_plot( value =[i for i in list_of_continents], df = df):
"""
"""
data = df[df['continent'].isin([value])]
fig = px.scatter(data, x="lifeExp", y="gdpPercap",
size="pop")
return fig
if __name__ == '__main__':
app.run_server(debug=True)
although plotly express can help you set up a graph with just one line of code it’s not very handy when it comes to customizing each trace. So, for that, you’ve to switch to graphs_objects.
In the following lines of code, the callback generates a Graph component for each trace and appends each graph component to a Div component. Hence you get multiple graphs using a single callback.
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
app = Dash(__name__)
df = px.data.gapminder()
app.layout = html.Div([
html.H4('Restaurant tips by day of week'),
html.Div(id='graphs',children=[])
])
#app.callback(
Output("graphs", "children"),
Input("graphs", "children")
)
def make_scatter_plot(child):
"""
"""
for continent in df['continent'].unique():
df_filtered = df[df['continent'] == continent]
fig = go.Figure()
fig.add_trace(
go.Scatter(x = df_filtered['lifeExp'],
y = df_filtered['gdpPercap'],
mode = 'markers',
marker = dict(size = 10 + (df_filtered['pop'] - df_filtered['pop'].min()) * 20
/ (df_filtered['pop'].max() - df_filtered['pop'].min())) # This is just to scale the marker size value between 10 and 20.
)
)
fig.update_layout(
title_text = continent
)
child.append(dcc.Graph(figure=fig))
return child
if __name__ == '__main__':
app.run_server(debug=True)
The output of the Code:
Click here

Windrose (Barpolar) in Dash is not updating

I want to implement a windrose (with Dash) for displaying the direction of wind data. I am using a callback function and the idea is that the data is displayed for different times - for this I am using a slider, where the time can be selected. The problem here is that after selecting the time, I have to double click on the windrose so that it is updated and displays the data.
I used the same code with a normal line plot, and there it worked fine (meaning that it updated right away without double clicking).
Thanks in advance!
# visit http://127.0.0.1:8050/ in your web browser.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
from dash.dependencies import Input, Output
import logging
# Load data
df = pd.read_csv('..\\..\\data\\raw\\fake_wind.csv', #
index_col=0,
parse_dates=True) # finds dates "automatically"
df.index = pd.to_datetime(df['Date']) # convert argument to datetime
# Initialize the app
app = dash.Dash(__name__)
# app.config.suppress_callback_exceptions = True # ??
app.layout = html.Div(
children=[
html.H1("Wind Direction"),
dcc.Slider(id='windrose',
min=1,#min(df.index),
max=5,#max(df.index),
value=5,
marks={
0: '0 °F',
3: '3 °F',
5: '5 °F',
7.65: '7.65 °F',
10: '10 °F'
}
),
html.Div(id='output_container', children=[]),
#html.Br(),
#dcc.Graph(id='sun_map', figure={})
dcc.Graph(id='wind_map2', config={'displayModeBar': False}, animate=True)
])
import numpy as np
#app.callback(
dash.dependencies.Output(component_id='wind_map2', component_property='figure'),
[dash.dependencies.Input('windrose', 'value')])
def update_output(value):
#fig=make_subplots(rows=2, cols=2, specs=[[{'type': 'polar'}]*2]*2)
row = df.iloc[value,:]
barplot = go.Barpolar(
r = [np.random.randint(1,10)],
width=[10],
theta = [np.random.randint(1,360)],
marker_color=["#E4FF87", '#709BFF', '#709BFF', '#FFAA70', '#FFAA70', '#FFDF70', '#B6FFB4'],
marker_line_color="black",
marker_line_width=2,
opacity=0.8
)
fig = go.Figure(data=barplot)
return fig
#return 'You have selected "{}"'.format(value)
fig.update_layout(
xaxis=dict(title="time"),
template='plotly_dark',
paper_bgcolor='rgba(0, 0, 0, 0)',
plot_bgcolor='rgba(0, 0, 0, 0)',
#yaxis="W/s^2",
#yaxis2=dict(title="Celsius",
#overlaying='y',
#side='right'),
font=dict(
family="Courier New, monospace",
size=18,
color="RebeccaPurple"
)
)
return fig
# Run the app
if __name__ == '__main__':
app.run_server(debug=True) # "hot-reloading" (Dash automatically refreshes browser when changes)
might be a bit late to answer. However I think you should change the "updatemode " in your slider from 'mouseup' to drag. As per the docs here https://dash.plotly.com/dash-core-components/slider
updatemode (a value equal to: 'mouseup', 'drag'; default 'mouseup'):
Determines when the component should update its value property. If
mouseup (the default) then the slider will only trigger its value when
the user has finished dragging the slider. If drag, then the slider
will update its value continuously as it is being dragged. If you want
different actions during and after drag, leave updatemode as mouseup
and use drag_value for the continuously updating value.

Plotly Dash dropdown menu python

I would like to add a dropdown menu to show only one figure. I mean, if I select fig the dash must show me only the fig and if I select fig2 the dash must show me the fig 2. Is it possible?
My code is an example, I have more than 500 figs.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go # or plotly.express as px
fig = go.Figure()
fig2 = go.Figure()
fig.add_trace(go.Scatter(y=[4, 2, 1], mode="lines"))
fig2.add_trace(go.Bar(y=[2, 1, 3]))
figs = [fig, fig2]
div = []
for item in figs:
div.append(dcc.Graph(figure=item))
app = dash.Dash()
app.layout = html.Div(div)
"""
add a dropdown to show only one fig
"""
app.run_server(debug=True, use_reloader=False)
Yes, it is possible.
First you need to create the dropdown containing the figure-names / filenames or the identifier you wish, just keep the {'label': x, 'value': x} structure for the option parameter. label is what you will see in the dropdown, and value will be passed to the callback (s. below).
fig_names = ['fig1', 'fig2']
fig_dropdown = html.Div([
dcc.Dropdown(
id='fig_dropdown',
options=[{'label': x, 'value': x} for x in fig_names],
value=None
)])
Next you need a blank div (with an id) where the plot will appear:
fig_plot = html.Div(id='fig_plot')
Now create a callback. When an input with the id='fig_dropdown' is changed, the value parameter will be passed to the update_output function. The output of this function will be passed to passed to the children parameter of the id='fig_plot' div.
#app.callback(
dash.dependencies.Output('fig_plot', 'children'),
[dash.dependencies.Input('fig_dropdown', 'value')])
def update_output(fig_name):
return name_to_figure(fig_name)
The name_to_figure(fig_name) function returns a dcc.Graph() objects, containing your figure, depending on the fig_name value of the dropdown.
Full example:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go # or plotly.express as px
app = dash.Dash()
fig_names = ['fig1', 'fig2']
fig_dropdown = html.Div([
dcc.Dropdown(
id='fig_dropdown',
options=[{'label': x, 'value': x} for x in fig_names],
value=None
)])
fig_plot = html.Div(id='fig_plot')
app.layout = html.Div([fig_dropdown, fig_plot])
#app.callback(
dash.dependencies.Output('fig_plot', 'children'),
[dash.dependencies.Input('fig_dropdown', 'value')])
def update_output(fig_name):
return name_to_figure(fig_name)
def name_to_figure(fig_name):
figure = go.Figure()
if fig_name == 'fig1':
figure.add_trace(go.Scatter(y=[4, 2, 1]))
elif fig_name == 'fig2':
figure.add_trace(go.Bar(y=[2, 1, 3]))
return dcc.Graph(figure=figure)
app.run_server(debug=True, use_reloader=False)
Incase you have so many fig to choose from in your Drop Down box, the following changes to the code may be necessary to implement:
#app.callback(Output('fig_plot', 'figure'), [Input('fig_dropdown', 'value')])
def cb(plot_type):
plot_type = plot_type if plot_type else 'fig1'
df_year = head_db.copy()
if plot_type:
return px.bar(df_year, x='Week #', y=str(plot_type), color='Name')
#Libraries/Imports
from dash import Dash, html, dcc, Input, Output
import plotly.graph_objects as go
fig = go.Figure()
fig2 = go.Figure()
fig.add_trace(go.Scatter(y=[4, 2, 1], mode="lines"))
fig2.add_trace(go.Bar(y=2, 1, 3]))
figs = ['fig', 'fig2']
#Your HTML to display the graph
#Disables the multiple dropdown values attributes
app.layout = html.Div([
html.Div(children=[
html.label('Dropdown'),
dcc.Dropdown(id='dropdown', options=(figs), multi=False
html.div(id='show-my-graph')
])
])
#Your callback; is used to display the graph when the dropdown values are selected or updated
#app.callback(
Output(component_id='show-my-graph'), component_property='children'),
Input(component_id='dropdown', component_property='value')
)
#Defines the Function used to display the graph when an option is selected or updated
def update_graph(dropdown_value):
"Returns the appropriate graph component to display when a dropdown is selected or updated"
if(dropdown_value == 'fig'):
ret_val = dcc.Graph(id='scatter-plot-graph', figure=fig)
return ret_val
if (dropdown_value == 'fig2'):
ret_val = dcc.Graph(id='bar-graph', figure=fig2)
return ret_val
app.run_server(debug=True, use_reloader=False)

How to update a plotly graph dash with different dropdowns

I am trying to update a plotly graph dash with two different dropdowns as inputs.
This is my sample dataframe:
import pandas as pd
df1 = {'category' : ['A','A','A','B','B','B'],'subcategory' : ['x', 'y', 'z', 'x1','y1','z1'],
'x_coord' : [1, 2,3,2,2,2],'y_coord' : [1,3,2,1,3,2]}
df_test = pd.DataFrame(df1)
df_test
And what I pretend to do is if I select category A, that plots in a scatter all the correspondent points to the category, but If Also I select a subcategory that modifies the graph plotting only the correspondent category-subcategory point of the dataframe.
The code is below, and it works if I only add the callback of the first dropdown, but when I add the second callback to the subcategory it doesn't work.
I am following the suggestions in the dash plotly tutorial where it says:
A word of caution: it's not always a good idea to combine Outputs, even if you can:
If the Outputs depend on some but not all of the same Inputs, keeping them separate can avoid unnecessary updates.
If they have the same Inputs but do independent computations with these inputs, keeping the callbacks separate can allow them to run in parallel.
Dash documentation callbacks
But anyway if I put the output in separate callbacks or in the same I cannot make it work, here is the code that I am trying (using jupyter notebook):
import dash
import plotly as py
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from jupyter_plotly_dash import JupyterDash
py.offline.init_notebook_mode(connected = True)
app = JupyterDash('Test')
app.layout = html.Div([
dcc.Dropdown(id='dropdown1',
options=[{'label':i, 'value':i} for i in df_test['category'].unique()]),
dcc.Dropdown(id='dropdown2',
options=[{'label':i, 'value':i} for i in df_test['subcategory'].unique()]),
dcc.Graph(id='graphic')
])
#app.callback(
Output('dropdown2', 'options'),
[Input('dropdown1', 'value')])
def update_drop2(selected_drop):
filtered_df = df_test[(df_test.category == selected_drop)]
return [{'label':i, 'value':i} for i in filtered_df['subcategory'].unique()]
#app.callback(
Output('graphic', 'figure'),
[Input('dropdown1', 'value')])
def update_figure(selected_drop):
filtered_df = df_test[(df_test.category == selected_drop)]
fig = go.Figure()
fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord, marker = dict(size=15, color='green'), mode='markers'))
return fig
#app.callback(
Output('graphic', 'figure'),
[Input('dropdown2', 'value')])
def update_figure(selected_drop):
filtered_df = df_test[(df_test.subcategory == selected_drop)]
fig = go.Figure()
fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord, marker = dict(size=15, color='green'), mode='markers'))
return fig
app
If I use multiple inputs on the callback like this:
#app.callback(
Output('graphic', 'figure'),
[Input('dropdown1', 'value'), Input('dropdown2', 'value')])
def update_figure(selected_drop1, selected_drop2):
if not selected_drop2:
filtered_df = df_test[(df_test.category == selected_drop1)]
else:
filtered_df = df_test[(df_test.category == selected_drop1) &
(df_test.subcategory == selected_drop2)]
fig = go.Figure()
fig.add_trace(go.Scatter(x=filtered_df.x_coord,y=filtered_df.y_coord,
marker = dict(size=15, color='green'), mode='markers'))
return fig
It works better (or more near what I pretend), but however when I switch between categories I see no data.
Thanks in advance for your help and reccomendations.
I had a similar problems the trick is to add to the second dropdown an option all. Then I wanted on the second dropdown to show only the subcategories in the given category. So I actually use 2 callbacks for dropdowns and 1 callback for the plot.
app.py
import pandas as pd
import os
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' : ['x', '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=True, port=8051)

Categories

Resources