Working from slightly modified code found here shown below
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.express as px
data_x = [1,2,3]
data_y = [1,2,1]
fig = px.line(x=data_x, y=data_y)
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
marker=dict(size=30)
)
app = JupyterDash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(
id="graph-3",
figure=fig,
clear_on_unhover=True),
dcc.Tooltip(
id="graph-tooltip-3",
background_color="lightgrey",
border_color="blue"),
],
)
#app.callback(
Output("graph-tooltip-3", "show"),
Output("graph-tooltip-3", "bbox"),
Output("graph-tooltip-3", "children"),
Input("graph-3", "hoverData"),
)
def update_tooltip_content(hoverData):
if hoverData is None:
return no_update
pt = hoverData["points"][0]
bbox = pt["bbox"]
children = [
html.P(f"x: {pt['x']}, y: {pt['y']}")
]
return True, bbox, children
app.run_server(mode='inline')
I am trying to position a tool tip with code at an x, y coord found in fig.data, rather than through pointing on the graph.
fig.data
(Scatter({
'hoverinfo': 'none',
'legendgroup': '',
'line': {'color': '#636efa', 'dash': 'solid'},
'marker': {'size': 30, 'symbol': 'circle'},
'mode': 'lines',
'name': '',
'orientation': 'v',
'showlegend': False,
'x': array([1, 2, 3], dtype=int64),
'xaxis': 'x',
'y': array([1, 2, 1], dtype=int64),
'yaxis': 'y'
}),)
I could achieve the desired result with Plotly.Fx.hover as described here, but I would prefer to not use a client side callback and to avoid the undocumented Plotly.Fx.hover function which I read was going to be deprecated.
Ultimately, I want to link a graph with a dash table using callbacks. When I click on a table cell, I want to see the same tooltip as when I hover on the graph. When I hover on the graph, I want to see a row of the table highlighted.
I have also asked this question on the plotly community forum here.
Related
I'm building a dashboard (code below) and my goal is to show several key facts about the data on a country-level basis. For example, current population vs. average population of all countries, life expectation vs. average life expectation of all countries etc.
I don't want to display the graph when no country (i.e. no dropdown option) is selected. This should also be standard when first launching the dashboard. However, I receive an error message when either clearing the dropdown or setting the value to '' in the layout area.
Does anybody know any solution to this problem?
import pandas as pd
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import plotly.express as px
import plotly.graph_objects as go
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')
fig = go.Figure()
# create the Dash app
app = dash.Dash()
# set up app layout
app.layout = html.Div(children=[
html.H1(children='Demographic Statistics'),
dcc.Dropdown(id='country-dropdown',
options=[{'label': x, 'value': x}
for x in df.country.unique()],
value='Argentina',
multi=False, clearable=True),
dcc.Graph(id='indicators', figure=fig)
])
# set up the callback function
#app.callback(
Output('indicators', 'figure'),
[Input('country-dropdown', 'value')])
def display_demographic_statistics(selected_country):
filtered_country = df[df.country == selected_country]
pop_confirmed = df.loc[df['country'] == selected_country, 'pop'].iloc[0]
lifexp = df.loc[df['country'] == selected_country, 'lifeExp'].iloc[0]
average_confirmed = df["pop"].mean()
average_lifexp = df["lifeExp"].mean()
return {
'data': [go.Indicator(
mode='number+delta',
value=pop_confirmed,
delta={'reference': average_confirmed,
'position': 'right',
'valueformat': ',g',
'relative': False,
'font': {'size': 15}},
number={'valueformat': ',',
'font': {'size': 20},
},
domain={'y': [0, 1], 'x': [0, 1]})],
'layout': go.Layout(
title={'text': 'Demgraphic Statistics'},
grid = {'rows': 2, 'columns': 2, 'pattern': "independent"},
template = {'data' : {'indicator': [{
'mode' : "number+delta+gauge",
'delta' : {'reference': 90}}]}}
),
}
# Run local server
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)
You could make the component with id='indicators' an html.Div and use the callback to update its children property as follows:
if the user selects a country from the dropdown, then the callback returns
a dcc.Graph with the indicators for the selected country,
if the user clears the dropdown, then the callback returns None (i.e. nothing).
Note also that the value of the dropdown when no selection has been made is None, not ''.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import plotly.graph_objects as go
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')
app = dash.Dash()
app.layout = html.Div(children=[
html.H1(children='Demographic Statistics'),
dcc.Dropdown(
id='country-dropdown',
options=[{'label': x, 'value': x} for x in df.country.unique()],
value=None,
multi=False,
clearable=True
),
html.Div(id='indicators')
])
#app.callback(
Output('indicators', 'children'),
[Input('country-dropdown', 'value')])
def display_demographic_statistics(selected_country):
if selected_country is not None:
pop_confirmed = df.loc[df['country'] == selected_country, 'pop'].iloc[0]
average_confirmed = df['pop'].mean()
return dcc.Graph(
figure=go.Figure(
data=go.Indicator(
mode='number+delta',
value=pop_confirmed,
delta={
'reference': average_confirmed,
'position': 'right',
'valueformat': ',g',
'relative': False,
'font': {'size': 15}
},
number={
'valueformat': ',',
'font': {'size': 20},
},
domain={
'y': [0, 1],
'x': [0, 1]
}
),
layout=go.Layout(
title={'text': 'Demgraphic Statistics'},
grid={'rows': 2, 'columns': 2, 'pattern': 'independent'},
template={'data': {'indicator': [{'mode': 'number+delta+gauge', 'delta': {'reference': 90}}]}}
)
)
)
else:
return None
if __name__ == '__main__':
app.run_server(host='127.0.0.1', debug=True)
Following code creates two subplots and updating it each second in browser window.
I can zoom it and hide some lines in plot, but each second all data updates and set zoom and all lines visibility to default
How I can keep settings for zoom and selected lines while updating?
import datetime
import random
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from plotly.subplots import make_subplots
# https://dash.plotly.com/live-updates
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
html.Div([
html.H4('Two random plots'),
dcc.Graph(id='live-update-graph'),
dcc.Interval(
id='interval-component',
interval=1 * 1000, # in milliseconds
n_intervals=0
)
])
)
DATA = {
'time': [],
'val0': [],
'val1_1': [],
'val1_2': []
}
def update_data():
DATA['val0'].append(random.randint(0, 50))
DATA['val1_1'].append(random.randint(0, 50))
DATA['val1_2'].append(random.randint(0, 50))
DATA['time'].append(datetime.datetime.now())
#app.callback(Output('live-update-graph', 'figure'),
Input('interval-component', 'n_intervals'))
def update_graph_live(n):
update_data()
# Create the graph with subplots
fig = 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.append_trace({
'x': DATA['time'],
'y': DATA['val0'],
'name': 'val 0',
'mode': 'lines+markers',
'type': 'scatter'
}, 1, 1)
fig.append_trace({
'x': DATA['time'],
'y': DATA['val1_1'],
'text': DATA['time'],
'name': 'val 1.1',
'mode': 'lines+markers',
'type': 'scatter'
}, 2, 1)
fig.append_trace({
'x': DATA['time'],
'y': DATA['val1_2'],
'text': DATA['time'],
'name': 'val 1.2',
'mode': 'lines+markers',
'type': 'scatter'
}, 2, 1)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
Since November 2018, there is a solution as posted on the Plotly Community Forum. The layout property of the figure property of dcc.Graph has a property uirevision, for which the post says:
uirevision is where the magic happens. This key is tracked internally by dcc.Graph, when it changes from one update to the next, it resets all of the user-driven interactions (like zooming, panning, clicking on legend items). If it remains the same, then that user-driven UI state doesn’t change.
So if you never want to reset the zoom settings, no matter how much the underlying data changes, just set uirevision to a constant, like so:
fig['layout']['uirevision'] = 'some-constant'
before returning the figure in your update function.
And there is more:
There are also cases where you want more control. Say the x and y data for a plot can be changed separately. Perhaps x is always time, but y can change between price and volume. You might want to preserve x zoom while resetting y zoom. There are lots of uirevision attributes, that normally all inherit from layout.uirevision but you can set them separately if you want. In this case set a constant xaxis.uirevision = 'time' but let yaxis.revision change between 'price' and 'volume'. Be sure to still set layout.uirevision to preserve other items like trace visibility and modebar buttons!
I want to use Python plotly to mark a vertical line that can be dragged around on a time series data graph.
Share my image below.
I remember browsing the plotly or dash web pages that previously described the features of this image, but I couldn't find it when I searched again.
If my mistake is, please let me know how to realize this function.
One approach is to use a shapes object in a dcc.Graph. You have to configure the graph to editable to be able to move the shape. You can then use the relayoutData property of the dcc.Graph as input in the callback function in order to get the position of the shape on the graph. This is explained in the link below. I don't think there is a way to restrict the movement of the shape, unfortunately. So in your case, there is no way to restrict the vertical line to stay vertical. A user would be able to alter it's angle, for example.
https://community.plotly.com/t/moving-the-location-of-a-graph-point-interactively/7161/2
I've also included some starter code as an example of a movable vertical line on a dash plot.
import json
from textwrap import dedent as d
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/dZVMbK.css'})
styles = {'pre': {'border': 'thin lightgrey solid', 'overflowX': 'scroll'}}
app.layout = html.Div(className='row', children=[
dcc.Graph(
id='basic-interactions',
className='six columns',
figure={
'data': [{
'x': [1, 2, 3, 4],
'y': [4, 1, 3, 5],
'text': ['a', 'b', 'c', 'd'],
'customdata': ['c.a', 'c.b', 'c.c', 'c.d'],
'name': 'Trace 1',
'mode': 'markers',
'marker': {
'size': 12
}
}, {
'x': [1, 2, 3, 4],
'y': [9, 4, 1, 4],
'text': ['w', 'x', 'y', 'z'],
'customdata': ['c.w', 'c.x', 'c.y', 'c.z'],
'name': 'Trace 2',
'mode': 'markers',
'marker': {
'size': 12
}
}],
'layout': {
'shapes': [{
'type': 'line',
'x0': 0.5,
'x1': 0.5,
'xref': 'paper',
'y0': 0,
'y1': 9,
'yref': 'y',
'line': {
'width': 4,
'color': 'rgb(30, 30, 30)',
'dash': 'dashdot'
}
}]
}
},
config={
'editable': True,
'edits': {
'shapePosition': True
}
}
),
html.Div(
className='six columns',
children=[
html.Div(
[
dcc.Markdown(
d("""
**Zoom and Relayout Data**
""")),
html.Pre(id='relayout-data', style=styles['pre']),
]
)
]
)
])
#app.callback(
Output('relayout-data', 'children'),
[Input('basic-interactions', 'relayoutData')])
def display_selected_data(relayoutData):
print("relayoutData:" + str(relayoutData))
return json.dumps(relayoutData, indent=2)
if __name__ == '__main__':
app.run_server(debug=True)
Hey guys,
I’m an intern python developer and I just found out about dash and Plotly, its amazing!
For my app I’m using a dataset containing info about fortune 500’s revenue in 1955-2005 period.
The problem I have is that the graph won’t update itself upon selecting additional company from the dropdown list, however, if I select a new company and delete a previous one - then the graph updates. It seems like something prevents the graph to show multiple lines at once. Also, is there any way I could add the legend telling which color is what company? Below is my code:
import dash
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
#Read CSV and change col. names
df = pd.read_csv('fortune500-full.csv')
df.columns = ['Year', 'Rank', 'Company', 'Revenue', 'Profit']
#Get a list of unique company names
Companies = df['Company'].unique()
#Stylesheet
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
#App layout
app.layout = html.Div([
#Dropdown
html.Div([
html.Label('Dropdown'),
dcc.Dropdown(
id = 'Dropdown',
options=[{'label': i, 'value': i} for i in Companies],
value=["General Motors"],
multi = True
),]),
#Graph
dcc.Graph(
id='Graph',
figure={
'data': [
go.Scatter(
x=df.loc[df['Company'] == i, 'Year'],
y=df.loc[df['Company'] == i, 'Revenue'],
text =df.loc[df['Company'] == i, 'Company'],
mode='lines',
name=i
) for i in df.Company.unique()],
'layout': go.Layout(
xaxis={'title': 'Year'},
yaxis={'title': 'Revenue'},
margin={'l': 50, 'b': 30, 't': 10, 'r': 0},
legend={'x': 1, 'y': 1},
hovermode='closest'
)}),
html.Div(dcc.RangeSlider(
id='Slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=[1955, 2005],
marks={str(year): str(year) for year in df['Year'].unique()}
), style={'width': '98%', 'padding': '0px 20px 20px 20px'}),
html.Div(id='Output_slider',
style={'textAlign': 'center', 'color': ['#7FDBFF']}
)])
#app.callback(
dash.dependencies.Output('Graph', 'figure'),
[dash.dependencies.Input('Dropdown', 'value')])
def callback_a(dropdown_value):
trace = []
for val in dropdown_value:
trace.append(
go.Scatter(
x=df.loc[df['Company'] == val, 'Year'],
y=df.loc[df['Company'] == val, 'Revenue'],
text =df.loc[df['Company'] == val, 'Company'],
mode='lines',
name=val
),)
layout = go.Layout(
xaxis={'title': 'Year'},
yaxis={'title': 'Revenue'},
margin={'l': 50, 'b': 30, 't': 10, 'r': 0},
legend={'x': 1, 'y': 1},
hovermode='closest'
)
figure = {'data': trace, 'layout': layout}
return figure
#app.callback(
dash.dependencies.Output('Output_slider', 'children'),
[dash.dependencies.Input('Slider', 'value')])
def update_output(value):
return 'Wybrane lata: "{}"'.format(value)
#App
if __name__ == '__main__':
app.run_server(debug=True)
Could any of you please take a look at this and help me find root of this problem?
Kind regards.
I am attempting to create an interactive graph inside of the Dash framework. I am new to this type of setup and as a result I am starting off simple by recreating the "More About Visualizations" scatter plot found in the getting started guide with the slight addition of also adding a lowess regression. The desired outcome is that the sample graph remains identical with the fitted regression added. The code I have is:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
import statsmodels.api as sm
app = dash.Dash()
df = pd.read_csv(
'https://gist.githubusercontent.com/chriddyp/' +
'5d1ea79569ed194d432e56108a04d188/raw/' +
'a9f9e8076b837d541398e999dcbac2b2826a81f8/'+
'gdp-life-exp-2007.csv')
performance_line = pd.DataFrame(sm.nonparametric.lowess(df['life expectancy'], df['gdp per capita'], frac=0.75))
app.layout = html.Div([
dcc.Graph(
id='life-exp-vs-gdp',
figure={
'data': [
go.Scatter(
x = performance_line[0],
y = performance_line[1],
mode = 'lines',
line = dict(
width=0.5
),
name = 'Fit'
),
go.Scatter(
x=df[df['continent'] == i]['gdp per capita'],
y=df[df['continent'] == i]['life expectancy'],
text=df[df['continent'] == i]['country'],
mode='markers',
opacity=0.7,
marker={
'size': 15,
'line': {'width': 0.5, 'color': 'white'}
},
name=i
) for i in df.continent.unique()
],
'layout': go.Layout(
xaxis={'type': 'log', 'title': 'GDP Per Capita'},
yaxis={'title': 'Life Expectancy'},
margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
legend={'x': 0, 'y': 1},
hovermode='closest'
)
}
)
])
if __name__ == '__main__':
app.run_server()
This code will not work because of the for loop after the scatter plot. I have tried encompassing it in () and [], but the JSON subroutine cannot handle generators and the [] stops the breaking but does not actually plot the scatter plot. How can i get this graph with the additional lowess regression?
It seems to me like a syntax problem, list comprehensions have the following format (forgive the simplicity):
[(something with i) for i in (iterable)]
while what you're trying looks like
[(unrelated item), (something with i) for i in (iterable)]
The following slight modification should work:
[(unrelated item)]+[(something with i) for i in (iterable)]
so the final code will be something like this.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
import statsmodels.api as sm
app = dash.Dash()
df = pd.read_csv(
'https://gist.githubusercontent.com/chriddyp/' +
'5d1ea79569ed194d432e56108a04d188/raw/' +
'a9f9e8076b837d541398e999dcbac2b2826a81f8/'+
'gdp-life-exp-2007.csv')
performance_line = pd.DataFrame(sm.nonparametric.lowess(df['life expectancy'], df['gdp per capita'], frac=0.75))
app.layout = html.Div([
dcc.Graph(
id='life-exp-vs-gdp',
figure={
'data': [
go.Scatter(
x = performance_line[0],
y = performance_line[1],
mode = 'lines',
line = dict(
width=0.5
),
name = 'Fit'
)
]+[
go.Scatter(
x=df[df['continent'] == i]['gdp per capita'],
y=df[df['continent'] == i]['life expectancy'],
text=df[df['continent'] == i]['country'],
mode='markers',
opacity=0.7,
marker={
'size': 15,
'line': {'width': 0.5, 'color': 'white'}
},
name=i
) for i in df.continent.unique()
],
'layout': go.Layout(
xaxis={'type': 'log', 'title': 'GDP Per Capita'},
yaxis={'title': 'Life Expectancy'},
margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
legend={'x': 0, 'y': 1},
hovermode='closest'
)
}
)
])
if __name__ == '__main__':
app.run_server()