scatter plot with Dash - python-3.x

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.

Related

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)

Live update multiple Y-axis in plotly dash

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')

Interconnection of two dropdowns in plotly-dash

I have been trying to connect two dropdowns to each other i.e. If I select a value from one DropDown the path and the contents of the other DropDown should change accordingly.
e.g I have a folder of dates (06-06-2020, 07-06-2020 and 08-06-2020) so if I select a date which is 07-06-2020 the DropDown below it should display the values which are inside the date folder 07-06-2020.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import plotly.graph_objects as go
import os
import pandas as pd
os.chdir(r"C:\Users\ankit\Desktop\dash_assign\SessionCode")
dir_route = os.getcwd()
app = dash.Dash(__name__)
g_t = ["OHLC Bars", "Candlesticks"]
# external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
# csv_list = [files for files in os.listdir(dir_route)]
csv_list = []
date_list = []
path_list = []
for dates in os.listdir(dir_route):
date = dates
date_list.append(date)
app.layout = html.Div(
children=[
html.H1("Time Series Graph"),
dcc.Dropdown(id="select_date",
options=[{"label": dates, "value": str(dates)} for dates in date_list],
value=20180102,
style={
"margin": "10px"
}
),
dcc.Dropdown(id="sym",
placeholder="Select Symbol",
style={
"margin": "10px"
}),
# html.Button("PLOT", id='plot'),
dcc.Graph(id='graph')
]
)
#app.callback(Output('sym', component_property="options"),
[Input('select_date', 'value')])
def update_dates(dat):
lst = os.listdir(os.path.join(dir_route, dat))
for files in lst:
if files.endswith(".csv"):
files.split(" ")
new_file = files[0]
return new_file
#app.callback(Output('graph', 'figure'),
[Input('select_date', 'value'),
Input("sym", "options"),
])
def update_graph(date, symbols):
path = os.path.join(dir_route, date, symbols)
df = pd.read_csv(os.path.join(path+".csv"), index_col=False)
fig = go.Figure(go.Ohlc(x =df["TimeStamp"],
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Close'],
increasing_line_color='#00ff00',
decreasing_line_color='#ff0000',
name="Price",
))
return fig
if __name__ == "__main__":
# fig = update_candle("A.csv")
# fig.show()
app.run_server(debug=True, port=5001)
This is my root directory and inside it i have various csv's which should appear in the DropDown once I select the dates
csv files inside one date directory
You can use a callback to update the second dropdown. Below is an example where cascading dropdown is created from table
#app.callback(
Output(component_id='id_first_dropdown', component_property='options'),
[Input(component_id='id_second_dropdown', component_property='value')]
)
def update_dp(filter_value):
sql = "Select distinct second_dropdown_options from table where first_dropdown_value='" + filter_value + "'"
df = pd.read_sql_query(sql, conn)
df = df['second_dropdown_options']
return [{'label': i, 'value': i} for i in df.unique()]

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?

Plotly Dash Graph With Multiple Dropdown Inputs Not Working

I’m trying to create a time-series Dash line graph that has multiple interactive dropdown user input variables. I would ideally like each of the dropdown inputs to allow for multiple selections.
While I’m able to create the drop down menus successfully, the chart isn’t updating like I’d like. When I allow the dropdowns to have multiple selections, I get an error that arrays are different lengths. And when I limit the dropdowns to one selection, I get an error that [‘Vendor_Name’] is not in index. So this may be two separate problems.
Graph that doesn’t work:
Snippet of Excel data imported into DF
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
#import plotly.graph_objs as go
df = pd.read_csv("Data.csv", sep = "\t")
df['YearMonth'] = pd.to_datetime(df['YearMonth'], format = '%Y-%m')
cols = ['Product_1', 'Product_2', 'Product_3']
vendor = df['Vendor'].unique()
app = dash.Dash('Data')
app.layout = html.Div([
html.Div([
html.Div([
html.Label('Product'),
dcc.Dropdown(
id = 'product',
options = [{
'label' : i,
'value' : i
} for i in cols],
multi = True,
value = 'Product_1'
),
]),
html.Div([
html.Label('Vendor'),
dcc.Dropdown(
id = 'vendor',
options = [{
'label' : i,
'value' : i
} for i in vendor],
multi = True,
value = 'ABC')
,
]),
]),
dcc.Graph(id = 'feature-graphic')
])
#app.callback(Output('feature-graphic', 'figure'),
[Input('product', 'value'),
Input('vendor', 'value')])
def update_graph(input_vendor, input_column):
df_filtered = df[df['Vendor'] == input_vendor]
##also tried setting an index because of the error I was getting. Not sure if necessary
df_filtered = df_filtered.set_index(['Vendor'])
traces = []
df_by_col = df_filtered[[input_column, 'YearMonth']]
traces.append({
'x' :pd.Series(df_by_col['YearMonth']),
'y' : df_by_col[input_column],
'mode' : 'lines',
'type' : 'scatter',
'name' :'XYZ'}
)
fig = {
'data': traces,
'layout': {'title': 'Title of Chart'}
}
return fig
if __name__ == '__main__':
app.run_server(debug=False)
Thanks in advance for helping! Still new-ish to Python, but very excited about Dash’s capabilities. I’ve been able to create other graphs with single inputs, and have read through documentation.
Here is the approach I followed: (editing common example available in google with my approach):
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
all_options = {
'America': ['New York City', 'San Francisco', 'Cincinnati'],
'Canada': [u'Montréal', 'Toronto', 'Ottawa']
}
app.layout = html.Div([
dcc.Dropdown(
id='countries-dropdown',
options=[{'label': k, 'value': k} for k in all_options.keys()],
value='America', #default value to show
multi=True,
searchable=False
),
dcc.Dropdown(id='cities-dropdown', multi=True, searchable=False, placeholder="Select a city"),
html.Div(id='display-selected-values')
])
#app.callback(
dash.dependencies.Output('cities-dropdown', 'options'),
[dash.dependencies.Input('countries-dropdown', 'value')])
def set_cities_options(selected_country):
if type(selected_country) == 'str':
return [{'label': i, 'value': i} for i in all_options[selected_country]]
else:
return [{'label': i, 'value': i} for country in selected_country for i in all_options[country]]
if __name__ == '__main__':
app.run_server(debug=True)
Workaround here is: When there is single input present in parent dropdown, the value is in string format. But for multiple values, it comes in list format.
This code also work perfectly and gets updated automatically even when you click on cross option to remove any selected option.
Note: I have used 'placeholder' attribute instead of defining default value for it as it made no sense in this case. But you can also update the value dynamically in similar way.
1 input data
The data as it is in the csv is hard to loop.
And I would argue that it is the main reason your code does not work,
because you seem to understand the fundamental code structure.
Having put on my SQL glasses I think you should try to get it to sth like
Date, Vendor, ProductName, Value
2 callback input types change
multi is tricky because it changes switches between returning a str if only 1 item is selected and list if more than one is selected
3 callback return type
you code returns a dict but the callback declared figure as the return type
but here is the code with debugging traces of print() and sleep()
import pandas as pd
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
import time
df = pd.read_csv("Data.csv", sep="\t")
df['YearMonth'] = pd.to_datetime(df['YearMonth'], format='%Y-%m')
products = ['Product_1', 'Product_2', 'Product_3']
vendors = df['Vendor'].unique()
app = dash.Dash('Data')
app.layout = html.Div([
html.Div([
html.Div([
html.Label('Product'),
dcc.Dropdown(
id='product',
options=[{'label' : p, 'value' : p} for p in products],
multi=True,
value='Product_1'
),
]),
html.Div([
html.Label('Vendor'),
dcc.Dropdown(
id='vendor',
options=[{'label': v, 'value': v} for v in vendors],
multi=True,
value='ABC'
),
]),
]),
dcc.Graph(id='feature-graphic', figure=go.Figure())
])
#app.callback(
Output('feature-graphic', 'figure'),
[Input('product', 'value'),
Input('vendor', 'value')])
def update_graph(input_product, input_vendor):
# df_filtered[['Product_1', 'YearMonth']]
if type(input_product) == str:
input_product = [input_product]
if type(input_vendor) == str:
input_vendor= [input_vendor]
datasets = ['']
i = 1
for vendor in input_vendor:
df_filtered = df[df['Vendor'] == vendor]
for product in input_product:
datasets.append((df_filtered[['YearMonth', 'Vendor', product]]).copy())
datasets[i]['ProductName'] = product
datasets[i].rename(columns={product: 'Value'}, inplace=True)
i += 1
datasets.pop(0)
print(datasets)
traces = ['']
for dataset in datasets:
print(dataset)
time.sleep(1)
traces.append(
go.Scatter({
'x': dataset['YearMonth'],
'y': dataset['Value'],
'mode': 'lines',
'name': f"Vendor: {dataset['Vendor'].iloc[0]} Product: {dataset['ProductName'].iloc[0]}"
}))
traces.pop(0)
layout = {'title': 'Title of Chart'}
fig = {'data': traces, 'layout': go.Layout(layout)}
return go.Figure(fig)
if __name__ == '__main__':
app.run_server()
quick and dirty disclosure:
If you handle the 1. issue it will dramatically simplify everything.
So I'd try to isolate the pd.DataFrame() juggling out of the callback and into the upper I/O part.
1) don't use counters in for loops
2) my variable names aren't the best either
3) the following style is caveman's python and there must be a better way:
traces = ['']
traces.append(this_and_that)
traces.pop(0)
Generally:
using print(input_variable) and print(type(input_variable))
gets my wheels most of the time out of the mud.
after all
you should notice that each trace got its individual name which will show up in the legend. Clicking on the name in the legend will add or remove the trace without the need for#app.callback()

Resources