Live update multiple Y-axis in plotly dash - python-3.x

I am trying to read sensor values from raspberry pi sense hat and live plotting 3 sensor data on same y-axis and time on x-axis. But only one graph is visible out of 3. For this example I am using a random number generator for sensor values. How to show live data for all 3 values on y-axis? What am I doing wrong?
# from dash.dependencies import Output, Event
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import random
import plotly.graph_objs as go
from collections import deque
# define a function to get CPU temperature data
def check_CPU_temp():
a = random.randint(0,9)
b = random.randint(0,9)
c = random.randint(0,9)
return a, b, c
X = deque(maxlen=50)
X.append(1)
acc_X = deque(maxlen=50)
acc_Y = deque(maxlen=50)
acc_Z = deque(maxlen=50)
a, b, c = check_CPU_temp()
acc_X.append(a)
acc_Y.append(b)
acc_Z.append(c)
def update_values(acc_X, acc_Y, acc_Z):
a1, b1, c1 = check_CPU_temp()
acc_X.append(a1)
acc_Y.append(b1)
acc_Z.append(c1)
return acc_X, acc_Y, acc_Z
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id='live-graph', animate=True),
dcc.Interval(
id='refresh',
interval=1 * 1000,
n_intervals=0
),
]
)
#app.callback(Output('live-graph', 'figure'), [Input("refresh", "n_intervals")])
def update_graph_scatter(n_intervals):
X.append(X[-1] + 1)
update_values(acc_X, acc_Y, acc_Z)
data_x = go.Scatter(
x=list(X),
y=list(acc_X),
name='acc x',
showlegend=True,
mode= 'lines',
yaxis='y'
)
data_y = go.Scatter(
x=list(X),
y=list(acc_Y),
name='acc y',
showlegend=True,
mode= 'lines',
yaxis='y1'
)
data_z = go.Scatter(
x=list(X),
y=list(acc_Z),
name='acc z',
showlegend=True,
mode= 'lines',
yaxis='y2'
)
layout = go.Layout(xaxis=dict(range=[min(X), max(X)],),
yaxis=dict(
range=[0, 10],
title='y'),
yaxis1=dict(
range=[0, 10],
side='right',
title='y1'),
yaxis2=dict(
range=[0, 10],
side='left',
title='y2'))
return {'data': [data_x, data_y, data_z], 'layout': layout}
if __name__ == '__main__':
app.run_server(debug=True, host='192.168.1.16', port='8050')

I was able to solve this using fig.addtrace()
import dash
from datetime import datetime as dt
from dash.dependencies import Output, Input
from dash import dcc
from dash import html
from collections import deque
from plotly.subplots import make_subplots
import plotly.graph_objs as go
from sense_hat import SenseHat
sense = SenseHat()
sense.set_imu_config(True, True, True) # accelerometer, magnetometer , gyroscope
sense.clear()
# define a function to get acceleration data
def check_acc_data():
acc = sense.get_accelerometer_raw()
return round(acc["x"],3), round(acc["y"],3), round(acc["z"],3)
def update_values(times, acc_X, acc_Y, acc_Z):
times.append(dt.strptime(str(dt.now()), '%Y-%m-%d %H:%M:%S.%f'))
a1, b1, c1 = check_acc_data()
acc_X.append(a1)
acc_Y.append(b1)
acc_Z.append(c1)
return times, acc_X, acc_Y, acc_Z
times = deque(maxlen=50)
acc_X = deque(maxlen=50)
acc_Y = deque(maxlen=50)
acc_Z = deque(maxlen=50)
update_values(times, acc_X, acc_Y, acc_Z)
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id='example_graph', figure={}),
dcc.Interval(
id='graph-update',
interval=250,
n_intervals=0
),
]
)
#app.callback(
Output("example_graph", "figure"),
[Input('graph-update', 'n_intervals')]
)
def update_figure(n):
update_values(times, acc_X, acc_Y, acc_Z)
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_X), mode="lines+markers", name='Acc X'),
secondary_y=False
)
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_Y), mode="lines+markers", name='Acc Y'),
secondary_y=True,
)
fig.add_trace(
go.Scatter(x=list(times), y=list(acc_Z), mode="lines+markers", name='Acc Z'),
secondary_y=True,
)
# fig.update_yaxes(secondary_y=False)
# fig.update_yaxes(secondary_y=True)
fig.update_layout(yaxis=dict(range=[-2.5,2.5]),
yaxis1=dict(range=[-2.5,2.5]),
yaxis2=dict(range=[-2.5,2.5]))
return fig
if __name__ == '__main__':
app.run_server(host='192.168.1.16',port='8050')

Related

scatter plot with Dash

I am coding a Dash page to plot some data for myself.
I want to have some dynamic features on some of my plots.
On my dash I have one dropdown, one pie chart and one scatter chart.
Here is my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
app = dash.Dash()
app.layout = html.Div([
html.Label(['Select the Type:']),
dcc.Dropdown(
id='type_filter',
options=[{'label': i, 'value': i} for i in df.Type.unique()],
value='All'
),
dcc.Graph(
id='allocation_chart'
),
dcc.Graph(
id='allocation_history_chart'
),
])
#app.callback(
Output('allocation_chart','figure'),
[
Input('type_filter', 'value')
]
)
def update_allocation(type_):
if type_ == 'All':
filtered = df
else:
mask = df['Type'] == type_
filtered = df.loc[mask]
data = [
go.Pie(
labels=filtered['Asset'],
values=filtered['Total']
)
]
return {
'data': data,
'layout': go.Layout(title='Asset Allocation by Type')
}
#app.callback(
Output('allocation_history_chart','figure'),
[
Input('type_filter','value')
]
)
def update_allocation_history(type_):
if type_ == 'All':
filtered = df_index
else:
mask = df['Type'] == type_
filtered = df_index.loc[mask]
data = go.Figure()
for ticker in filtered.index:
data.add_trace(
go.Scatter(
x = filtered.loc[ticker].index,
y = filtered.loc[ticker],
name = ticker
)
)
return {
'data': [data],
'layout': go.Layout(title='History of Asset')
}
if __name__ == '__main__':
app.run_server()
here is the Dash:
Here is the scatter chart ran in my Notebook:
Everything is working - Dash is running well, i can see the dropdown and the pie chart - except the scatter chart - the second one.
I absolutely don't understand why because when I run the scatter chart in my Notebook it's working pretty well.
If you have any idea, don't hesitate.

I am trying to create a population pyramid graph using Dash with Plotly

i have a directory containing three files, years.csv, 2014.csv and 2015.csv. i want to plot a population pyramid graph for the two files but i want pandas to pick the dataframe from the years.csv with respect to the slider value.
my years.csv looks like, on the slider when i select 2014, from the code you can see, its an int that i convert into a string and append .csv to it. but all i want is that final string interpreted as df = pd.read_csv('2014.csv') so that i can be able to generate graphs of all the years as long as that file is in the directoy.
years
0
2014(2014.csv)
1
2015(2015.csv)
from dash import Dash, dcc, html, Input, Output
# import plotly.express as px
import plotly.graph_objects as gp
import pandas as pd
# df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
df = pd.read_csv('years.csv')
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='graph-with-slider'),
dcc.Slider(
df['year'].min(),
df['year'].max(),
step=None,
value=df['year'].min(),
marks={str(year): str(year) for year in df['year'].unique()},
id='year-slider'
)
])
#app.callback(
Output('graph-with-slider', 'figure'),
Input('year-slider', 'value'))
def update_figure(selected_year):
new_df = str(df[df.year == selected_year]) + ".csv"
print(new_df)
# fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
# size="pop", color="continent", hover_name="country",
# log_x=True, size_max=55)
y_age = new_df['Age']
x_M = new_df['Male']
x_F = new_df['Female'] * -1
# fig.update_layout(transition_duration=500)
# Creating instance of the figure
fig = gp.Figure()
# Adding Male data to the figure
fig.add_trace(gp.Bar(y= y_age, x = x_M,
name = 'Male',
orientation = 'h'))
# Adding Female data to the figure
fig.add_trace(gp.Bar(y = y_age, x = x_F,
name = 'Female', orientation = 'h'))
# Updating the layoutout for our graph
fig.update_layout(title = 'Population Pyramid of Uganda-2015',
title_font_size = 22, barmode = 'relative',
bargap = 0.0, bargroupgap = 0,
xaxis = dict(tickvals = [-600000, -400000, -200000,
0, 200000, 400000, 600000],
ticktext = ['6k', '4k', '2k', '0',
'2k', '4k', '6k'],
title = 'Population in Thousands',
title_font_size = 14)
)
# fig.show()
return fig
if __name__ == '__main__':
app.run_server(debug=True)

Individually color ticks of a plotly.graph_objects.Bar

I have a multi-index dataframe dfc which I want to plot as a bar chart with the color of the tick on the yaxis depending on the value of dfc.iloc[i].values[1] for any value i.
Unnamed: 1 claimed_benefit perceived_benefit
My Burberry - Eau de Parfum je me sens bien 0 0.000000
Her Intense - Eau de Parfum convient bien moi 0 0.000000
Her Intense - Eau de Parfum sensuelle / sexy 0 0.000000
Her Intense - Eau de Parfum nettoyer 0 0.000000
Her Intense - Eau de Parfum haute qualite 0 0.000000
... ... ... ...
Mr. Burberry Indigo - Eau de Toilette nouveau / jamais respire avant 0 0.666667
In order to achieve that I tried this answer by updating the ticktext value of the yaxis property in the layout, as it seems that plotly has full LaTeX support.
traces = []
ticks = []
colors = []
for i in range(len(dfc)):
if dfc.iloc[i].name == my_dropdown:
trace_claimed = go.Bar(y=[dfc.iloc[i].values[0]], x=[dfc.iloc[i].values[2]],
name=dfc.iloc[i].values[0] + ' Perceived', orientation='h')
tick = dfc.iloc[i].values[0]
if dfc.iloc[i].values[1] > 0:
color = 'red'
else:
color = 'blue'
ticks.append(tick)
colors.append(color)
traces.append(trace_claimed)
# traces.append(trace_perceived)
keys = dict(zip(ticks, colors))
ticktext = [get_color(v, k) for k, v in keys.items()]
figure = go.Figure(data=traces,
layout=go.Layout(title='Score des parfums sur les attributs',
barmode='stack')
)
figure.update_layout(
yaxis=dict(tickmode='array', ticktext=ticktext, tickvals=ticks)
)
However it only produces a weird text for the ticks:
Here is the ticktext value:
['$\\color{blue}{je me sens bien}$', '$\\color{blue}{harsh / agressif}$', '$\\color{blue}{boisé}$', '$\\color{blue}{écœurant}$', '$\\color{blue}{strength1}$', ..., '$\\color{red}{frais}$', '$\\color{blue}{pour le soir / nuit}$', '$\\color{blue}{doux}$']
Here is a minimal reproducible example:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
from os.path import abspath, dirname, join
app = Dash(__name__)
def get_color(color, text):
s = '$\color{' + str(color) + '}{' + str(text) + '}$'
return s
df = pd.read_csv('some_file.csv')
def layout():
return html.Div([
dcc.Dropdown(
id='perfume-dropdown',
options=[{'label': x, 'value': x} for x in df.index.unique()],
value='My Burberry - Eau de Parfum'
),
html.Div(id='dd-output-container'),
html.Div([
dcc.Graph(id='graph-attributes')
])
])
#app.callback(
Output(component_id='graph-attributes', component_property='figure'),
[Input(component_id="perfume-dropdown", component_property="value")]
)
def update_graph(my_dropdown):
dfc = df.sort_values(by='perceived_benefit', ascending=True)
traces = []
ticks = []
colors = []
for i in range(len(dfc)):
if dfc.iloc[i].name == my_dropdown:
trace_claimed = go.Bar(y=[dfc.iloc[i].values[0]], x=[dfc.iloc[i].values[2]],
name=dfc.iloc[i].values[0] + ' Perceived', orientation='h')
tick = dfc.iloc[i].values[0]
if dfc.iloc[i].values[1] > 0:
color = 'red'
else:
color = 'blue'
ticks.append(tick)
colors.append(color)
traces.append(trace_claimed)
# traces.append(trace_perceived)
keys = dict(zip(ticks, colors))
ticktext = [get_color(v, k) for k, v in keys.items()]
print(ticktext)
figure = go.Figure(data=traces,
layout=go.Layout(title='Score des parfums sur les attributs',
barmode='stack')
)
figure.update_layout(
yaxis=dict(tickmode='array', ticktext=ticktext, tickvals=ticks)
)
return figure
Using the approach from here with your code, and the following some_file.csv:
name,claimed,perceived
A,0,1
B,1,2
C,0,3
D,1,4
We can achieve this (with my sample):
Through adding two things:
pip install dash_defer_js_import
and
import dash_defer_js_import as dji
mathjax_script = dji.Import(src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_SVG")
[...]
def layout():
return html.Div([
dcc.Dropdown(
id='perfume-dropdown',
options=[{'label': x, 'value': x} for x in df.index.unique()],
value='My Burberry - Eau de Parfum'
),
html.Div(id='dd-output-container'),
html.Div([
dcc.Graph(id='graph-attributes')
]),
mathjax_script # use the script here
])
All in all:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
from os.path import abspath, dirname, join
from dash import Dash
app = Dash(__name__)
import dash_defer_js_import as dji
mathjax_script = dji.Import(src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_SVG")
def get_color(color, text):
s = '$\color{' + str(color) + '}{' + str(text) + '}$'
return s
df = pd.read_csv('some_file.csv')
def layout():
return html.Div([
dcc.Dropdown(
id='perfume-dropdown',
options=[{'label': x, 'value': x} for x in df.index.unique()],
value='My Burberry - Eau de Parfum'
),
html.Div(id='dd-output-container'),
html.Div([
dcc.Graph(id='graph-attributes')
]),
mathjax_script
])
#app.callback(
Output(component_id='graph-attributes', component_property='figure'),
[Input(component_id="perfume-dropdown", component_property="value")]
)
def update_graph(my_dropdown):
dfc = df.sort_values(by='perceived', ascending=True)
traces = []
ticks = []
colors = []
for i in range(len(dfc)):
if dfc.iloc[i].name == my_dropdown:
trace_claimed = go.Bar(y=[dfc.iloc[i].values[0]], x=[dfc.iloc[i].values[2]],
name=dfc.iloc[i].values[0] + ' Perceived', orientation='h')
tick = dfc.iloc[i].values[0]
if dfc.iloc[i].values[1] > 0:
color = 'red'
else:
color = 'blue'
ticks.append(tick)
colors.append(color)
traces.append(trace_claimed)
# traces.append(trace_perceived)
keys = dict(zip(ticks, colors))
ticktext = [get_color(v, k) for k, v in keys.items()]
print(ticktext)
figure = go.Figure(data=traces,
layout=go.Layout(title='Score des parfums sur les attributs',
barmode='stack')
)
figure.update_layout(
yaxis=dict(tickmode='array', ticktext=ticktext, tickvals=ticks)
)
return figure
if __name__ == '__main__':
app.layout = layout()
app.run_server(debug=True)
Picture without a dropdown menu:

How do you plot a ohlc chart with a set range (invariable) of 1 hour using dash-plotly?

This is my code:
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
from datetime import timedelta
file_name = 'temp_copy.csv'
file_path = 'C:\\Users\\xxx\\' + str( file_name )
df = pd.read_csv( 'file_path' )
df['period_close'] = pd.to_datetime( df.period_close )
df['year'] = pd.DatetimeIndex( df['period_close'] ).year
df['month'] = pd.DatetimeIndex( df['period_close'] ).month
df['month_str'] = df.month.apply(str).str.zfill(2)
df['slider_id'] = ( df.year - 2017 ) * 12 + df.month # has to be linear
df['slider_label'] = df.year.apply(str).str.cat( df.month_str ).apply(int)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
ids = df.slider_id.unique().tolist()
labels = [{ 'label': str( i ) } for i in df.slider_label.unique().tolist() ]
marks = dict(zip( ids, labels ))
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(id="my-graph"),
html.Div([
dcc.RangeSlider(id="select-range",
marks=marks,
min=df.slider_id.min(),
max=df.slider_id.max(),
value=[7, 8]
)
],
style={"padding-top": 100,}
)
], className="container")
#app.callback(
Output("my-graph", 'figure'),
[Input("select-range", 'value')]
)
def update_figure(selected):
print( selected[0] )
fromYear = int( selected[0] / 12 + 2017 )
fromMonth = int( selected[0] % 12 )
toYear = int( selected[1] / 12 + 2017 )
toMonth = int( selected[1] % 12 )
dff = df[
( df.year >= fromYear )
& ( df.month >= fromMonth )
& ( df.year <= toYear )
& ( df.month <= toMonth )
]
trace = go.Candlestick(x=dff['period_close'],
open=dff['price'],
high=dff['high'],
low=dff['low'],
close=dff['close'],
increasing={'line': {'color': '#00CC94'}},
decreasing={'line': {'color': '#F50030'}}
)
return {
'data': [trace],
'layout': go.Layout(
title=f"Prices",
xaxis={
'rangeslider': {
'visible': True,
'range': [ df.period_close.min(), df.period_close.min() + timedelta(hours=1) ]
},
'autorange': True,
},
yaxis={
"title": f'Price'
}
)}
server = app.server # the Flask app
if __name__ == '__main__':
app.run_server(debug=True)
I can not provide you with the data, but it starts at 2017 and provides the columns: open, high, low, close, period_close. I found the rangeslider wouldnt do what I wanted, although i may have done something wrong. It required an int value, in a linear format for equal spacing so i have simply got the year - 2017 + month to have a linear spread.
My problem in detail:
I have a huge dataset of 1 minute OHLC data from 2017 (July) through to Jan 2019. I want to be able to plot a 1 hour (roughly) window across my screen, with a range slider underneath that i can drag to get different timeframes. If possible, I would like the slider to not be variable in width ( ~1 hour or so ).
I don't know of a way to make the range slider lock in the width. However, you could just use the normal slider.
dcc.Slider(
min=0,
max=19,
marks={
x: '{} to {}'.format(x, x+1)
for x in range(20)
},
value=0
)
You'd have to change it, of course, but that's the general idea. Would that work?

trace not showing on plotly when second y axis added

I'm plotting temperature and humidity readings from raspberry pi to plotly.
On the same y axis they appear fine.
When I add a second y axis via layout, the temperature trace (trace1) doesn't show.
On plotly itself it shows the data for both trace1 and trace2 but it's not plotting trace1 for some reason.
Any ideas?!
import plotly.plotly as py
import json
import time
import datetime as dt
import plotly.graph_objs as go
import numpy as np
import sqlite3
import pandas as pd
con = sqlite3.connect('/home/pi/environment.db')
#c = conn.cursor()
df = pd.read_sql_query("SELECT date_time, temp, humid FROM readings",
con)
df['temp_MA'] = df.temp.rolling(10).mean()
df['humid_MA'] = df.humid.rolling(10).mean()
trace1 = go.Scatter(
name = 'Temperature degC',
x=df.date_time,
y=df.temp_MA,
yaxis = 'y1'
)
trace2 = go.Scatter(
name = 'Rel Humidity %',
x=df.date_time,
y=df.humid_MA,
yaxis = 'y2'
)
data = [trace1, trace2]
layout = go.Layout(
yaxis=dict(
title='Temperature deg C',
side='left'
),
yaxis2=dict(
title='Humidity Rel %',
side='right'
)
)
fig = go.Figure(data=data, layout=layout)
py.plot(fig)
con.close()
The plotly graph is here:
https://plot.ly/~uilfut/58/temperature-degc-vs-rel-humidity/
Just in case anyone else discovers this thread from googling - I figured out the missing line of code...
Under layout, yaxis2, you have to specify overlaying='y'.
It works now :)
layout = go.Layout(
yaxis=dict(
title='Temperature deg C',
side='left'
),
yaxis2=dict(
title='Humidity Rel %',
side='right',
overlaying='y'
)
)

Resources