How to display image on point hover of 3D plot in python? - python

I'd like for an image to display at the point I'm hovering over in the 3D plot (shown below) or next it. sHow can I do this? Currently, I'm using plotly to create 3D plots like the one below. It has the hover feature but only for text. Is there some other python program that allows for images to be displayed instead of text?
Thanks.
The following code is in response to Derek's comment
Traceback (most recent call last):
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel_launcher.py", line 16, in <module>
app.launch_new_instance()
File "C:\Users\User\anaconda3\lib\site-packages\traitlets\config\application.py", line 663, in launch_instance
app.initialize(argv)
File "<decorator-gen-125>", line 2, in initialize
File "C:\Users\User\anaconda3\lib\site-packages\traitlets\config\application.py", line 87, in catch_config_error
return method(app, *args, **kwargs)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 567, in initialize
self.init_sockets()
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 271, in init_sockets
self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 218, in _bind_socket
return self._try_bind_socket(s, port)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 194, in _try_bind_socket
s.bind("tcp://%s:%i" % (self.ip, port))
File "zmq/backend/cython/socket.pyx", line 550, in zmq.backend.cython.socket.Socket.bind
File "zmq/backend/cython/checkrc.pxd", line 26, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Address in use

According to this thread, displaying images on point hover isn't possible in plotly-python but might be built out in the future.
However, you can accomplish this in plotly-dash because this library allows you to modify the html using callbacks.
Following the example here, I created some sample data and a go.Scatter3d figure that matches your use case a bit more:
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
if __name__ == "__main__":
app.run_server(debug=True)
EDIT: I am also including an answer that allows you to run a dash app from a jupyter notebook based on this sample notebook:
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = JupyterDash(__name__)
server = app.server
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
app.run_server(mode="inline")

Related

Plotly go: how to add an image to the hover feature?

The first segment of code below (code # 1) generates a graph for which 1) when you hover over each point, the data associated with each point is displayed and 2) when you click on each point, the data associated with each point is saved to a list. For this code, I would also like to display the image associated with each point. Assume the dataframe df has a column 'image' which contains the image pixel/array data of each point. I found code online (code #2) that implements this image hover feature but without the click feature. I'm having a hard time combining the image hover feature with the click feature. So, basically, I'm trying to combine the click feature (click on point, it's data is saved to a list) of code # 2 into code # 1.
CODE # 1 (with click feature):
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
from dash import dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# app info
app = JupyterDash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
# data
df = px.data.gapminder().query("continent=='Oceania'")
# plotly figure
fig = px.line(df, x="year", y="lifeExp", color="country", title="No label selected")
fig.update_traces(mode="markers+lines")
app.layout = html.Div([
dcc.Graph(
id='figure1',
figure=fig,
),
html.Div(className
='row', children=[
html.Div([
dcc.Markdown(d("""Hoverdata using figure references""")),
html.Pre(id='hoverdata2', style=styles['pre']),
], className='three columns'),
html.Div([
dcc.Markdown(d("""
Full hoverdata
""")),
html.Pre(id='hoverdata1', style=styles['pre']),
], className='three columns')
]),
])
# container for clicked points in callbacks
store = []
#app.callback(
Output('figure1', 'figure'),
Output('hoverdata1', 'children'),
Output('hoverdata2', 'children'),
[Input('figure1', 'clickData')])
def display_hover_data(hoverData):
if hoverData is not None:
traceref = hoverData['points'][0]['curveNumber']
pointref = hoverData['points'][0]['pointNumber']
store.append([fig.data[traceref]['name'],
fig.data[traceref]['x'][pointref],
fig.data[traceref]['y'][pointref]])
fig.update_layout(title = 'Last label was ' + fig.data[traceref]['name'])
return fig, json.dumps(hoverData, indent=2), str(store)
else:
return fig, 'None selected', 'None selected'
app.run_server(mode='external', port = 7077, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
CODE # 2 (includes image hover feature):
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = JupyterDash(__name__)
server = app.server
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
app.run_server(mode="inline")
you want a callback that does hover and click
on hover display image associated with point and full hover info
on click update list of clicked points and figure title
Assume the dataframe df has a column 'image' have created one that is a b64 encoded image
have inserted this into the figure by using customdata (hover_data parameter in px)
have added an additional div image
have changed callback to behave as it did before and also contents on new div. This uses b64 encoded image, extending with necessary "data:image/png;base64,"
need to take note of this https://dash.plotly.com/vtk/click-hover and https://dash.plotly.com/advanced-callbacks
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings
import base64, io, requests
from PIL import Image
from pathlib import Path
warnings.simplefilter(action="ignore", category=FutureWarning)
# app info
app = JupyterDash(__name__)
styles = {"pre": {"border": "thin lightgrey solid", "overflowX": "scroll"}}
# data for whare images can be found
df_flag = pd.read_csv(
io.StringIO(
"""country,Alpha-2 code,Alpha-3 code,URL
Australia,AU,AUS,https://www.worldometers.info//img/flags/small/tn_as-flag.gif
New Zealand,NZ,NZL,https://www.worldometers.info//img/flags/small/tn_nz-flag.gif"""
)
)
# ensure that images exist on your file system...
f = Path.cwd().joinpath("flags")
if not f.exists():
f.mkdir()
# download some images and use easy to use filenames...
for r in df_flag.iterrows():
flag_file = f.joinpath(f'{r[1]["Alpha-3 code"]}.gif')
if not flag_file.exists():
r = requests.get(r[1]["URL"], stream=True, headers={"User-Agent": "XY"})
with open(flag_file, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
# encode
def b64image(country):
b = io.BytesIO()
im = Image.open(Path.cwd().joinpath("flags").joinpath(f"{country}.gif"))
im.save(b, format="PNG")
b64 = base64.b64encode(b.getvalue())
return b64.decode("utf-8")
df_flag["image"] = df_flag["Alpha-3 code"].apply(b64image)
# data
df = px.data.gapminder().query("continent=='Oceania'")
df = df.merge(df_flag, on="country") # include URL and b64 encoded image
# plotly figure. Include URL and image columns in customdata by using hover_data
fig = px.line(
df,
x="year",
y="lifeExp",
color="country",
title="No label selected",
hover_data={"URL": True, "image": False},
)
fig.update_traces(mode="markers+lines")
app.layout = dash.html.Div(
[
dash.dcc.Graph(
id="figure1",
figure=fig,
),
dash.html.Div(
className="row",
children=[
dash.html.Div(id="image"),
dash.html.Div(
[
dash.dcc.Markdown(d("""Hoverdata using figure references""")),
dash.html.Pre(id="hoverdata2", style=styles["pre"]),
],
className="three columns",
),
dash.html.Div(
[
dash.dcc.Markdown(
d(
"""
Full hoverdata
"""
)
),
dash.html.Pre(id="hoverdata1", style=styles["pre"]),
],
className="three columns",
),
],
),
]
)
# container for clicked points in callbacks
store = []
#app.callback(
Output("figure1", "figure"),
Output("hoverdata1", "children"),
Output("hoverdata2", "children"),
Output("image", "children"),
[Input("figure1", "clickData"), Input("figure1", "hoverData")],
)
def display_hover_data(clickData, hoverData):
# is it a click or hover event?
ctx = dash.callback_context
if ctx.triggered[0]["prop_id"] == "figure1.clickData":
traceref = clickData["points"][0]["curveNumber"]
pointref = clickData["points"][0]["pointNumber"]
store.append(
[
fig.data[traceref]["name"],
fig.data[traceref]["x"][pointref],
fig.data[traceref]["y"][pointref],
]
)
fig.update_layout(title="Last label was " + fig.data[traceref]["name"])
return fig, dash.no_update, str(store), dash.no_update
elif ctx.triggered[0]["prop_id"] == "figure1.hoverData":
# simpler case of just use a URL...
# dimg = dash.html.Img(src=hoverData["points"][0]["customdata"][0], style={"width": "30%"})
# question wanted image encoded in dataframe....
dimg = dash.html.Img(
src="data:image/png;base64," + hoverData["points"][0]["customdata"][1],
style={"width": "30%"},
)
return fig, json.dumps(hoverData, indent=2), dash.no_update, dimg
else:
return fig, "None selected", "None selected", "no image"
# app.run_server(mode='external', port = 7077, dev_tools_ui=True,
# dev_tools_hot_reload =True, threaded=True)
app.run_server(mode="inline")

How to add values from selectedData input to a list?

I build a Plotly Dash web app to display sensor data. I want to have a map where I can select the stations and therefore I can see the time series chart.
This is my callback right now:
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'selectedData'))
def display_selected_data(selectedData):
if selectedData is None: # Plot whole Dataframe if nothing is selected.
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
return fig
else:
selectedData['points'][0]['customdata'][0] # This line shows me the name of the location and I want to add this to a list
return
I can show the location in the selected data. Now my question is, how can I add this to a list?
My goal ist to filter the dataframe like this dff2 = df[df.location.isin(selected_locations)] so that I only plot the selected locations.
My full app right now:
from jupyter_dash import JupyterDash
import plotly.graph_objs as go
from dash import Dash, dcc, html, Input, Output, State
import pandas as pd
import plotly.express as px
import json
loc = pd.read_csv('location_sensors.csv')
df = pd.read_csv('measurement.csv')
style = {'width': '50%', 'height': '500px', 'float': 'left'}
# Build small example app.
app = dash.Dash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
fig_map = px.scatter_mapbox(loc, lat="lat", lon="lon", hover_name="location",
hover_data={'location':True, 'lat':False, 'lon':False}, zoom=3, height=600,
color='location', mapbox_style="open-street-map")
fig_map.update_layout(clickmode='event+select')
app.layout = html.Div([
dcc.Graph(id='map_sensors', figure=fig_map , className='six columns'),
html.Div([dcc.Graph(
id='time_series1',
style={'height': 400}
),
])
])
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'selectedData'))
def display_selected_data(selectedData):
if selectedData is None:
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
return fig
else:
# Here I want to filter the dataframe to the selected locations.
return
if __name__ == '__main__':
app.run_server()
Locations csv data:
lat,lon,location
51.20966,4.43182,BETR801
48.83722,2.3939,FR04014
51.49467,-0.13193,London Westminster
Time series data:
https://github.com/pandas-dev/pandas/blob/master/doc/data/air_quality_long.csv
For your #app.callback decorator, I think you want your input to be clickData instead of selectionData. If you look at the first example in the documentation here, once you click a location on the map and it is greyed out, when you click it again at a later time, clickData will input a dictionary with marker information, while selectionData will input null (this means that dash will have trouble knowing when you click on a point again after it's been greyed out if you use selectionData instead of clickData)
You can then have a dynamic list that changes depending on locations the user selects and deselects. Also a very minor point, but I changed your DataFrame variable name from loc to locs since .loc is a pandas DataFrame method.
from jupyter_dash import JupyterDash
import plotly.graph_objs as go
import dash
from dash import Dash, dcc, html, Input, Output, State
import pandas as pd
import plotly.express as px
import json
locs = pd.read_csv('location_sensors.csv')
df = pd.read_csv('https://raw.githubusercontent.com/pandas-dev/pandas/master/doc/data/air_quality_long.csv')
style = {'width': '50%', 'height': '500px', 'float': 'left'}
# Build small example app.
app = dash.Dash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
fig_map = px.scatter_mapbox(locs, lat="lat", lon="lon", hover_name="location",
hover_data={'location':True, 'lat':False, 'lon':False}, zoom=3, height=600,
color='location', mapbox_style="open-street-map")
fig_map.update_layout(clickmode='event+select')
app.layout = html.Div([
dcc.Graph(id='map_sensors', figure=fig_map , className='six columns'),
html.Div([dcc.Graph(
id='time_series1',
style={'height': 400}
),
])
])
## define a list that will hold the columns of the dataframe
## this will be used to modify the px.line chart
selected_locations = list(locs['location'])
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'clickData'))
def display_selected_data(clickData):
## when the app initializes
if clickData is None:
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
## when the user clicks on one of the loc points
else:
selection = clickData['points'][0]['customdata'][0]
if selection in selected_locations:
selected_locations.remove(selection)
else:
selected_locations.append(selection)
fig = px.line(data_frame=df[df.location.isin(selected_locations)], x='date.utc', y='value', color='location')
return fig
if __name__ == '__main__':
app.run_server(debug=True)

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([])

Is there a way to force the initial load of a figure?

Using Python/Plotly, I'm trying to create a figure that has a map and a slider to choose the year. Depeding on the year that is selected in the slider, the map should render different results.
Evertyhing is working fine with exception for one thing - I cannot set the initial load state of the map to be the same as the initial step as defined in
sliders = [dict(active=0, pad={"t": 1}, steps=steps)]
When the first render is done, and the slider has not yet been used, the data displayed seems to be accordingly to the last state of the data_slider list, ie. it shows the last values that were loaded in the list (I'm not totally sure about this conclusion).
Is there a way to set the first data display accordingly to the step that is pre-defined? Or in alternative, a way to force the first load to be something like data_slider[0].
Below is the code I'm using.
import pandas as pd
import os
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import numpy as np
import random
ds = 'https://archive.org/download/globalterrorismdb_0718dist/globalterrorismdb_0718dist.csv'
# ds = os.getcwd() + '\globalterrorismdb_0718dist.csv'
fields = ['eventid', 'iyear', 'country', 'country_txt', 'region_txt', 'city', 'latitude', 'longitude', 'nkill']
df = pd.read_csv(ds, encoding='ISO-8859-1', usecols=fields)
df = df.loc[df['iyear'] >= 2000]
df['nkill'].fillna(0, inplace=True)
df = df.groupby(['country_txt', 'iyear'])['nkill'].sum().reset_index()
df = df.loc[df['nkill'] > 0]
data_slider = []
for year in df.iyear.unique():
df_year = df[(df['iyear'] == year)]
data_year = dict(
type='choropleth',
name='',
colorscale='amp',
locations=df_year['country_txt'],
z=df_year['nkill'],
zmax=15000,
zmin=0,
locationmode='country names',
colorbar=dict(
len=0.5,
thickness=10,
title=dict(
text='Number of fatalities',
font=dict(
family='Arial',
size=14,
),
side='right'
),
tickfont=dict(
family='Arial',
size=12),
)
)
data_slider.append(data_year)
steps = []
for i in range(len(data_slider)):
step = dict(
method='restyle',
args=['visible', [False] * len(data_slider)],
label=(format(i + 2000))
)
step['args'][1][i] = True
steps.append(step)
sliders = [dict(active=0, pad={"t": 1}, steps=steps)]
layout = dict(
geo=dict(
scope='world',
showcountries=True,
projection=dict(
type='equirectangular'
),
showland=True,
landcolor='rgb(255, 255, 255)',
showlakes=False,
showrivers=False,
showocean=True,
oceancolor='white',
),
sliders=sliders
)
fig = go.Figure(data=data_slider, layout=layout)
fig.update_layout(
font=dict(family='Arial', size=12),
autosize=False,
width=1250,
height=750,
margin=dict(
l=25,
r=0,
b=100,
t=50,
pad=0,
)
)
app = dash.Dash(__name__)
server = app.server
app.layout = html.Div(children=[
# html.H1(children='Test2'),
# html.Div(children='''
# Example of html Container
# '''),
dcc.Graph(
id='fig',
figure=fig
)
])
if __name__ == '__main__':
app.run_server(debug=True)
The problem here is not on sliders but on the traces you are defining in data_slider. You need to play with the visible parameter in order to have the first plot properly rendered. The following trick should work.
data_slider = []
for i, year in enumerate(df.iyear.unique()):
df_year = df[(df['iyear'] == year)]
data_year = dict(
type='choropleth',
name='',
colorscale='amp',
locations=df_year['country_txt'],
z=df_year['nkill'],
zmax=15000,
zmin=0,
locationmode='country names',
visible= True if i==0 else False,
colorbar=dict(
len=0.5,
thickness=10,
title=dict(
text='Number of fatalities',
font=dict(
family='Arial',
size=14,
),
side='right'
),
tickfont=dict(
family='Arial',
size=12),
)
)
data_slider.append(data_year)
Extra
Given that you are generating all the plots outside the app you could actually avoid to use dash.

Dynamically adding traces to graph with dropdown input

I'm currently working on my first web-application. I recently started following tutorials on python and on some modules. Most of the questions I have I'm able to figure by looking them up, exceptfor this one. I can't figured it out.
Goal is as follows:
- I'm trying to show a graph with a line-chart which dynamically updates by the user input. The user can choose multiple items from a multiple value dropdown.
If I add multiple 'go.Scatters' I can add multiple lines to the graph, except then the trace in the graph is static. So, I tried to write a for loop in go.Scatter that adds more go.Scatters for each item in data_name. Each time I try I got syntax erros. I tried to add values to the data list in dcc.Graph. Each time i failed.
The questions I've are the following:
- Where can i add the for loop to add traces to the data list?
- How should the for loop be structed?
I'm just a beginner so every advice is welcome :-)
Thanks in advance.
If i'm not clear enough on the question, please let me know.
My code is the following:
import dash
import dash_core_components as dcc
import dash_html_components as html
from pandas_datareader.data import DataReader
import time
from collections import deque
import plotly.graph_objs as go
import random
app = dash.Dash('vehicle-data')
max_length = 50
#times = deque(maxlen=max_length)
times = [1,2,3,4,5,6,7,8,9,10,11,12]
oil_temps = [11,12,13,14,15,16,17,18,19]
intake_temps = [11,12,13,14,15,16,17,18,19]
coolant_temps = [11,12,13,14,15,16,17,18,19]
#rpms = deque(maxlen=max_length)
#speeds = deque(maxlen=max_length)
#throttle_pos = deque(maxlen=max_length)
data_dict = {"NH Utrecht":oil_temps,
"NH Amsterdam": intake_temps,
"NH Schiller": coolant_temps
#"NH Sparrenhorst":rpms,
#"Amsterdam":speeds,
#"Overig":throttle_pos
}
app.layout = html.Div([
html.Div([
html.H2('Hotel',
style={'float': 'left',
}),
]),
dcc.Dropdown(id='vehicle-data-name',
options=[{'label': s, 'value': s}
for s in data_dict.keys()],
value=['NH Utrecht'],
multi=True
),
html.Div(children=html.Div(id='graphs'), className='row'),
dcc.Interval(
id='graph-update',
interval=100),
], className="container",style={'width':'98%','margin-left':10,'margin-right':10,'max-width':50000})
#app.callback(
dash.dependencies.Output('graphs','children'),
[dash.dependencies.Input('vehicle-data-name', 'value')],
events=[dash.dependencies.Event('graph-update', 'interval')]
)
def update_graph(data_names):
graphs = []
#update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)
if len(data_names)>2:
class_choice = 'col s12 m6 l4'
elif len(data_names) == 2:
class_choice = 'col s12 m6 l6'
else:
class_choice = 'col s12'
html.Div(children=
graphs.append(dcc.Graph(
figure=go.Figure(
data = [
go.Scatter(
x=times,
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156],
name='Hotel Okura',
marker=go.Marker(
color='rgb(55, 83, 109)'
),
type='scatter',
connectgaps=True
),
],
layout=go.Layout(
title='Hotel comparison data',
showlegend=True,
legend=go.Legend(
x=1.0,
y=1.0
),
margin=go.Margin(l=40, r=0, t=40, b=30)
),
),
style={'height': 300},
id='my-graph'
)
))
return graphs
external_css = ["https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css"]
for css in external_css:
app.css.append_css({"external_url": css})
external_js = ['https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js']
for js in external_css:
app.scripts.append_script({'external_url': js})
if __name__ == '__main__':
app.run_server(debug=True)
The for loop I used is the following (which obviously doesn't work because i try to add a graph in stead of a data time. But data.append doesn't work):
for data_name in data_names:
graph.append(go.Scatter(
x=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
name='Voorbeeld',
marker=go.Marker(
color='rgb(255, 153, 0)'
),
type='scatter'
))

Categories