I have a plotly histogram with a shape in the background. How can I make the background object right/left dragg-able?
import numpy as np
import plotly.graph_objects as go
# generate data
data = np.random.normal(0,10,500)
# plot histogramm
fig = go.Figure()
fig.add_trace(go.Histogram(x=data))
# add shape
fig.add_shape(dict(type="rect", xref="x", yref="paper",
x0=-22, x1=22,
y0=0, y1=1,
fillcolor="Gray", opacity=0.3,
layer="below", line_width=0, editable=True))
current output:
desired output: have the shape-object draggable.
Edit: The background object doesnt have to be a "shape"
Disclaimer: I am not very experienced in Dash, but this was the only way I could implement a movable shape. I relied heavily on this answer by chriddyp in the Plotly Community forum. The parameters of dcc.Graph are pretty similar to go.Figure.
One thing to note is that only when your cursor is very much interior to the shape are you able to drag it, but if you hover anywhere else closer to the borders, clicking and dragging will change your shape's dimensions rather than move it, which may or may not not be the behavior you are after. Anyone who uses Dash regularly- feel free to update/improve upon my answer as needed.
import numpy as np
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
# generate data, set seed for reproducibility
np.random.seed(42)
data = np.random.normal(0,10,500)
app = dash.Dash(
__name__,
external_stylesheets=[
'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': data,
'name': 'Trace 1',
'type': 'histogram'
}],
'layout': {
'shapes': [{
'type': 'rect',
'x0': -22,
'x1': 22,
'xref': 'x',
'y0': 0,
'y1': 50,
'yref': 'y',
'fill': 'toself',
'line': dict(
color="Gray",
width=0.5,
),
'fillcolor': 'Gray',
'layer': 'below'
}]
}
},
config={
'editable': True,
'edits': {
'shapePosition': True
}
}
),
])
if __name__ == '__main__':
app.run_server(debug=True)
Related
I have a dashboard very similar to this one-
import datetime
import dash
from dash import dcc, html
import plotly
from dash.dependencies import Input, Output
# pip install pyorbital
from pyorbital.orbital import Orbital
satellite = Orbital('TERRA')
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('TERRA Satellite Live Feed'),
html.Div(id='live-update-text'),
dcc.Graph(id='live-update-graph'),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)
])
)
# Multiple components can update everytime interval gets fired.
#app.callback(Output('live-update-graph', 'figure'),
Input('live-update-graph', 'relayout'),
Input('interval-component', 'n_intervals'))
def update_graph_live(relayout, n):
if ctx.triggered_id == 'relayout':
* code that affects the y axis *
return fig
else:
satellite = Orbital('TERRA')
data = {
'time': [],
'Latitude': [],
'Longitude': [],
'Altitude': []
}
# Collect some data
for i in range(180):
time = datetime.datetime.now() - datetime.timedelta(seconds=i*20)
lon, lat, alt = satellite.get_lonlatalt(
time
)
data['Longitude'].append(lon)
data['Latitude'].append(lat)
data['Altitude'].append(alt)
data['time'].append(time)
# 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.append_trace({
'x': data['time'],
'y': data['Altitude'],
'name': 'Altitude',
'mode': 'lines+markers',
'type': 'scatter'
}, 1, 1)
fig.append_trace({
'x': data['Longitude'],
'y': data['Latitude'],
'text': data['time'],
'name': 'Longitude vs Latitude',
'mode': 'lines+markers',
'type': 'scatter'
}, 2, 1)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
In my case, I have three different intputs. One input gets triggered by an dcc.interval timer, like in the example. Another input gets triggered when a user zooms in on the dashboard using the Input('live-update-graph', 'relayoutData' input and the last triggeres when a button gets pressed.
All three inputs are totally independent. One updates the data stored in fig['data'], another updates the data stored in fig['layout']['xaxis'] and the last updates the stuff in fig['layout']['yaxis'].
I am concerned about the this situation:
The dcc interval input gets triggered and the function starts to update the data
The user zooms in on the dashboard and triggers the relayout data
The dcc.interval returns a figure
Now, because the relayout input got triggered second, it has stale data. There is a race condition and it is possible that the dcc interval gets undone as a result.
What can I do to avoid the race condition? I wonder if it's possible to update only a part of the figure with a callback rather than editing the whole object.
I think this code does what you want. Update data, while keeping layout. You can adapt to exactly what you would like, your example is copied anyhow and not really working (ex: you have a ctx there that is not defined)
The idea of the code below is: rather than update the complete object server side (in the callback) have different "parts" of the object (data-patch1, data-patch2, etc) and "merge" them in the browser (see deep_merge).
Depending on what you want to keep/adjust you can adjust that function and fill accordingly the data-patch.
For the code below you can just zoom in/zoom out, but you could also patch colors, sizes, etc.
# From https://github.com/plotly/dash-core-components/issues/881
import dash
import datetime
import random
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
import plotly
figure = go.Figure()
app = dash.Dash(__name__)
app.layout = html.Div(children = [
html.Div(id="patchstore",
**{'data-figure':figure, 'data-patch1':{}, 'data-patch2':{}, 'data-patch3':{}}),
dcc.Graph(id="graph"),
dcc.Interval(
id='interval-component',
interval=2*1000, # in milliseconds
n_intervals=0)
])
deep_merge = """
function batchAssign(patches) {
function recursiveAssign(input, patch){
var outputR = Object(input);
for (var key in patch) {
if(outputR[key] && typeof patch[key] == "object" && key!="data") {
outputR[key] = recursiveAssign(outputR[key], patch[key])
}
else {
outputR[key] = patch[key];
}
}
return outputR;
}
return Array.prototype.reduce.call(arguments, recursiveAssign, {});
}
"""
app.clientside_callback(
deep_merge,
Output('graph', 'figure'),
[Input('patchstore', 'data-figure'),
Input('patchstore', 'data-patch1'),
Input('patchstore', 'data-patch2'),
Input('patchstore', 'data-patch3')]
)
#app.callback(Output('patchstore', 'data-patch1'),[Input('interval-component', 'n_intervals')])
def callback_data_generation(n_intervals):
data = {
'time': [],
'Latitude': [],
'Longitude': [],
'Altitude': []
}
# Collect some data
for i in range(30):
time = datetime.datetime.now() - datetime.timedelta(seconds=i*20)
data['Longitude'].append(random.randint(1,10))
data['Latitude'].append(random.randint(1,10))
data['Altitude'].append(random.randint(1,10))
data['time'].append(time)
# 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.append_trace({
'x': data['time'],
'y': data['Altitude'],
'name': 'Altitude',
'mode': 'lines+markers',
'type': 'scatter'
}, 1, 1)
fig.append_trace({
'x': data['Longitude'],
'y': data['Latitude'],
'text': data['time'],
'name': 'Longitude vs Latitude',
'mode': 'lines+markers',
'type': 'scatter'
}, 2, 1)
if n_intervals==0:
fig.layout = None
return fig
app.run_server()
Since I use dash, I have some annoying problems with the style_header parameter of the dash_table.Datatable component.
Indeed, some of them like the colors / background colors work while others like width do not work. It seems that it is linked to the other variable 'fixed_rows' which, when set to true (to make the dashboard header fixed), blocks the width selection.
I've tried with all the versions of dash (from 1.20.0 to the very last one when I write these lines, 2.5.1) and always have this problem.
See below :
With fixed_rows={'headers': True} :
image
With fixed_rows not activated :
image
My code :
from dash import Dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_table
from tools.visual import colors
cols = ['ColumnA', 'ColumnB', 'ColumnC']
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)
app.layout = html.Div(children=[
# Dashboard
html.Div(id="table", children=[
dash_table.DataTable(
columns=[{"id": i, "name": i} for i in cols],
style_header={
'backgroundColor': colors['bg_board'],
'color': colors['text_category'],
'fontWeight': 'bold',
'textAlign': 'center'
},
style_cell={
'textAlign': 'center',
'whiteSpace': 'normal',
'height': 'auto',
'width': '200px'
},
style_cell_conditional=
[
{
'if': {'column_id': 'Maturity'},
'backgroundColor': colors['bg_board'],
'color': colors['text_category'],
}
],
style_table={
'overflow': 'auto',
},
fill_width=False,
# fixed_rows={'headers': True},
css=[{"selector": ".show-hide", "rule": "display: none"}],
id='tbl'
)
]),
])
if __name__ == '__main__':
app.run_server(debug=False, host='0.0.0.0', port=5026)
You probably solved your issue but here is my take on it for future reference.
style_cell works for headers and data, style_data works for data cells, style_header works for data.
For a fixed size to work, you need to use the 3 parameters as following:
style_cell={
# all three widths are needed
'minWidth': '75px', 'width': '75px', 'maxWidth': '75px',
},
)
I'm working on creating an interactive dashboard using dash. There is a default dataframe(say dashtable1) and everytime I click a column of dashtable1 I want the graph to update and everytime I click on a row I want another dash table(dashtable2) to update. I have partially achieved this. The following is the code snippet:
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
app = dash.Dash(__name__, external_stylesheets = [dbc.themes.BOOTSTRAP], prevent_initial_callbacks=True)
app.layout = html.Div([dbc.Row(
[dbc.Col(html.Div(id='graph', style = {'height' : 400 , 'width' : 1750}), width = 9),dbc.Col(html.Div(id='action-df'), width = 3)]
#app.callback(
Output(component_id='action-df', component_property='children'),
[Input(component_id='datatable-interactivity', component_property="derived_virtual_data"),
Input(component_id='datatable-interactivity', component_property='derived_virtual_selected_rows'),
Input(component_id='datatable-interactivity', component_property='derived_virtual_selected_row_ids'),
Input(component_id='datatable-interactivity', component_property='selected_rows'),
Input(component_id='datatable-interactivity', component_property='derived_virtual_indices'),
Input(component_id='datatable-interactivity', component_property='derived_virtual_row_ids'),
Input(component_id='datatable-interactivity', component_property='active_cell'),
Input(component_id='datatable-interactivity', component_property='selected_cells')])
def update_df(all_rows_data, slctd_row_indices, slct_rows_names, slctd_rows,
order_of_rows_indices, order_of_rows_names, actv_cell, slctd_cell):
dff = pd.DataFrame(all_rows_data)
dff.dropna(inplace =True)
action-df.iloc[slctd_row_indices[0],:]
return html.Div(children = [html.Div([
dash_table.DataTable( id = 'action-df',
data=action_df.to_dict('records'),
page_size=10,
fixed_rows={'headers': True},
style_cell_conditional=[ # align text columns to left. By default they are aligned to right
{
'if': {'column_id': c},
'textAlign': 'left'
} for c in action_df.columns
],
style_data={ # overflow cells' content into multiple lines
'whiteSpace': 'normal',
'height': 'auto'
},
columns=[{'name': ["Changeable Parameters",i], 'id': i} for i in action_df.columns ],
cell_selectable = True,
style_data_conditional=[{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(88, 237, 217)'
}, {'if': {'row_index': 'even'},
'backgroundColor': 'rgb(143, 255, 240)'}],
style_header={
'backgroundColor': 'rgb(0, 219, 190)',
'fontWeight': 'bold',
'text-align': 'center'},
fill_width = False,
style_cell={
'height': 'auto',
# all three widths are needed
'minWidth': '180px', 'width': '180px', 'maxWidth': '180px',
'whiteSpace': 'normal',
'font_family': "Coda Caption",
'font_size': '18px'
},
style_table={
'overflowY': 'scroll'},
merge_duplicate_headers=True
)
])
)
#app.callback(Output(component_id = 'graph',component_property = 'children'),
[Input(component_id = 'datatable-interactivity', component_property = 'selected_columns' )]
)
def linechart(slctd_columns):
if 'TIMESTAMP' not in slctd_columns:
fig = px.line(df_merged.iloc[:30,:], x="TIMESTAMP", y= slctd_columns)
fig.update_layout(
title="PARAMETER VARIATION OVER THE LAST MONTH",
xaxis_title="TIMESTAMP",
yaxis_title= str(slctd_columns[0]),
font=dict(
family="Coda Caption",
size=18,
color="#121212"
)
)
return dcc.Graph(id = 'graph',
figure = fig)
This code perfectly works out when I select a row/column for the first time, but stops updating after that. I'm new to Dash, so any help would be highly appreciated!
I am trying capture mouse hover events using Dash. I capture the position of the mouse using hoverData.
The problem appears when I filter the time series using the range selector or the range slider. The plot correctly reduces to the selected time, but when I hover it with the mouse it resets to the main view (whole main series).
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
app.layout = html.Div([
dcc.Graph(
id='stock-plot'
),
], className="container")
#app.callback(
Output('stock-plot', 'figure'),
[Input('stock-plot', 'hoverData')])
def drawStockPrice(hoverData):
traces = [go.Scatter(
x=df.Date,
y=df['AAPL.High'],
mode='lines',
opacity=0.7,
connectgaps=True),
]
return {'data': traces,
'layout': go.Layout(colorway=["#5E0DAC", '#FF4F00', '#375CB1', '#FF7400', '#FFF400', '#FF0056'],
height=600,
title=f"Closing prices",
xaxis={"title": "Date",
'rangeselector': {'buttons': list([{'count': 1, 'label': '1M',
'step': 'month',
'stepmode': 'backward'},
{'count': 6, 'label': '6M',
'step': 'month',
'stepmode': 'backward'},
{'step': 'all'}])},
'rangeslider': {'visible': True}, 'type': 'date'},
yaxis={"title": "Price (USD)"},
)}
if __name__ == '__main__':
app.run_server(debug=True)
I am sure there should be a better solution but this is what I got (Dash v1.6.0):
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv")
layout = go.Layout( colorway=["#5E0DAC", '#FF4F00', '#375CB1', '#FF7400', '#FFF400', '#FF0056'],
height=600,
title=f"Closing prices",
xaxis={"title": "Date",
'rangeselector': {'buttons': list([{'count': 1, 'label': '1M',
'step': 'month',
'stepmode': 'backward'},
{'count': 6, 'label': '6M',
'step': 'month',
'stepmode': 'backward'},
{'step': 'all'}]
),
},
'rangeslider': {'visible': True},
'type': 'date',
},
yaxis={"title": "Price (USD)"},
)
traces = [go.Scatter( x=df.Date,
y=df['AAPL.High'],
mode='lines',
opacity=0.7,
connectgaps=True
)]
app.layout = html.Div([
dcc.Graph(
id='stock-plot',
figure={
'data': traces,
'layout': layout
}
),
], className="container")
#app.callback(
Output('stock-plot', 'figure'),
[Input('stock-plot', 'hoverData'),
Input('stock-plot', 'relayoutData')],
[State('stock-plot', 'figure')]
)
def drawStockPrice(hoverData, selected, figure):
data = figure['data']
layout = figure['layout']
if selected is not None and 'xaxis.range' in selected:
layout['xaxis']['range'] = selected['xaxis.range']
return {'data': data,
'layout': layout
}
if __name__ == '__main__':
app.run_server(debug=True)
Since Dash is a fairly new framework for making interactive web-based graphs, there is not too many information that are specific or detailed for beginners. In my case I need to update a simple bar graph using a callback function.
The data does not render on the browser even though the server runs fine without prompting any errors.
Need help sorting out and understanding why data does not render.
import dash
from dash.dependencies import Output, Event
import dash_core_components as dcc
import dash_html_components as html
import plotly
import plotly.graph_objs as go
app = dash.Dash(__name__)
colors = {
'background': '#111111',
'background2': '#FF0',
'text': '#7FDBFF'
}
app.layout = html.Div( children = [
html.Div([
html.H5('ANNx'),
dcc.Graph(
id='cx1'
)
])
]
)
#app.callback(Output('cx1', 'figure'))
def update_figure( ):
return {
'data': [
{'x': ['APC'], 'y': [9], 'type': 'bar', 'name': 'APC'},
{'x': ['PDP'], 'y': [8], 'type': 'bar', 'name': 'PDP'},
],
'layout': {
'title': 'Basic Dash Example',
'plot_bgcolor': colors['background'],
'paper_bgcolor': colors['background']
}
}
if __name__ == '__main__':
app.run_server(debug=True)
You can use callback in a such way (I create dropdown menu for this):
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import plotly.graph_objs as go
import pandas as pd
app = dash.Dash(__name__)
df = pd.DataFrame({'x': ['APC', 'PDP'], 'y': [9, 8]})
colors = {
'background': '#111111',
'background2': '#FF0',
'text': '#7FDBFF'
}
app.layout = html.Div(children=[
html.Div([
html.H5('ANNx'),
html.Div(
id='cx1'
),
dcc.Dropdown(id='cx2',
options=[{'label': 'APC', 'value': 'APC'},
{'label': 'PDP', 'value': 'PDP'},
{'label': 'Clear', 'value': None}
]
)
])])
#app.callback(Output('cx1', 'children'),
[Input('cx2', 'value')])
def update_figure(value):
if value is None:
dff = df
else:
dff = df.loc[df["x"] == value]
return html.Div(
dcc.Graph(
id='bar chart',
figure={
"data": [
{
"x": dff["x"],
"y": dff["y"],
"type": "bar",
"marker": {"color": "#0074D9"},
}
],
"layout": {
'title': 'Basic Dash Example',
"xaxis": {"title": "Authors"},
"yaxis": {"title": "Counts"},
'plot_bgcolor': colors['background'],
'paper_bgcolor': colors['background']
},
},
)
)
if __name__ == '__main__':
app.run_server(debug=True)
If you write output in your calback function then you need to provide input as well, it can be, slider, date picker or dropdown menu and etc. but in your case you dont need any input and output as your graph is not dynamic in this case. check here https://dash.plot.ly/getting-started-part-2
so in your case it is enough just to put id and figure into dcc.Graph component:
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
colors = {
'background': '#111111',
'background2': '#FF0',
'text': '#7FDBFF'
}
app.layout = html.Div( children = [
html.Div([
html.H5('ANNx'),
dcc.Graph(
id='cx1', figure={
'data': [
{'x': ['APC'], 'y': [9], 'type': 'bar', 'name': 'APC'},
{'x': ['PDP'], 'y': [8], 'type': 'bar', 'name': 'PDP'},
],
'layout': {
'title': 'Basic Dash Example',
'plot_bgcolor': colors['background'],
'paper_bgcolor': colors['background']
}}
)
])
]
)
if __name__ == '__main__':
app.run_server(debug=True)