I'm trying to visualize the price development of different financial indices (A, B, C) in an interactive line chart embedded in Python Dash. I want to allow users to select multiple indices and compare them accordingly in the same plot over a specific period of time. At the same time, the plot should also change accordingly when unselecting indices. So far, I was able to plot only one index. The issue I'm having now is that the the plot does not change at all when adding additional indices. I've tried to solve this issue myself for the last couple of hours, but without success, unfortunately.
I'm using Jupyter Notebook. Here's my code with a data sample:
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
data = [['2020-01-31', 100, 100, 100], ['2020-02-28', 101, 107, 99], ['2020-03-31', 104, 109, 193], ['2020-04-30', 112, 115, 94], ['2020-05-31', 112, 120, 189]]
df = pd.DataFrame(data, columns = ['DATE', 'A', 'B', 'C'])
df = df.set_index('DATE')
df
# create the Dash app
app = dash.Dash()
# set up app layout
app.layout = html.Div(children=[
html.H1(children='Index Dashboard'),
dcc.Dropdown(id='index-dropdown',
options=[{'label': x, 'value': x}
for x in df.columns],
value='A',
multi=True, clearable=True),
dcc.Graph(id='price-graph')
])
# set up the callback function
#app.callback(
Output(component_id='price-graph', component_property='figure'),
[Input(component_id='index-dropdown', component_property='value')]
)
def display_time_series(selected_index):
filtered_index = [df.columns == selected_index]
fig = px.line(df, x=df.index, y=selected_index,
labels={'x', 'x axis label'})
fig.update_layout(
title="Price Index Development",
xaxis_title="Month",
yaxis_title="Price",
font=dict(size=13))
return fig
# Run local server
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)
As I'm relatively new to Python Dash, any help or advice would be extremely appreciated!
You're not applying your filter inside your callback to your data and the filter itself doesn't work.
Instead you can do something like this:
#app.callback(
Output(component_id="price-graph", component_property="figure"),
[Input(component_id="index-dropdown", component_property="value")],
)
def display_time_series(selected_index):
dff = df[selected_index] # Only use columns selected in dropdown
fig = px.line(dff, x=df.index, y=selected_index, labels={"x", "x axis label"})
fig.update_layout(
title="Price Index Development",
xaxis_title="Month",
yaxis_title="Price",
font=dict(size=13),
)
return fig
Related
The graph above is created by using the following code.
import plotly.graph_objects as go
candlestick = go.Candlestick(x=data.index,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
increasing_line_color='red',
decreasing_line_color='green')
fig = go.Figure(data=[candlestick])
fig.show()
After the graph is generated, I can change its X axis by clicking "Zoom in" and "Zoom out" buttons on the top right corner.
However, I can't find any way to change its Y axis. I mean, for example, I want to set the minimum value of Y axis to 175, and the maximum value of Y axis to 177.
Is it possible to dynamically change the range of Y axis AFTER the graph is generated?
I know I can use fig.update_yaxes(range=[175,177]) to update the range of Y axis. But running this command requires me to restart my program, which is very inconvenient.
The graph can be updated with fig.update_*. I will answer with an example from the official reference, where the y-axis is restricted.
import plotly.graph_objects as go
import pandas as pd
from datetime import datetime
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
fig = go.Figure(data=[go.Candlestick(x=df['Date'],
open=df['AAPL.Open'],
high=df['AAPL.High'],
low=df['AAPL.Low'],
close=df['AAPL.Close'])])
fig.update_yaxes(range=[100,120])
fig.show()
Example without y-axis restriction
I create the following solution based on this document.
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
import plotly.graph_objects as go
candlestick = go.Candlestick(x=data.index,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
increasing_line_color='red',
decreasing_line_color='green')
fig = go.Figure(data=[candlestick])
def get_start_end_range(rng):
if rng:
if "xaxis.range" in rng.keys():
return rng['xaxis.range'][0], rng['xaxis.range'][1]
if 'xaxis.range[0]' in rng.keys():
return rng['xaxis.range[0]'], rng['xaxis.range[1]']
return None, None
app = Dash(__name__)
app.layout = html.Div(children=[
html.H1(children='Signals Graph'),
dcc.Graph(
id='signals-graph',
figure=fig
)
])
# Use callback to dynamically adjust the scale of Y axis
#app.callback(
Output(component_id="signals-graph", component_property="figure"),
Input(component_id="signals-graph", component_property="relayoutData"),
)
def scaleYaxis(rng):
print(f"rng: {rng}")
xstart, xend = get_start_end_range(rng)
if xstart != None:
try:
range_data = data.loc[
xstart : xend,
["high", "low", "open", "close"],
]
if len(range_data) > 0:
fig["layout"]["yaxis"]["range"] = [range_data['low'].min()*0.95, range_data['high'].max()*1.05]
except Exception as e:
print(f"Something wrong occured: {e}")
finally:
print(f"update x axis: {[xstart, xend]}")
fig["layout"]["xaxis"]["range"] = [xstart, xend]
return fig
app.run_server()
The solution above uses callback to dynamically adjust the scale of Y axis.
The above code would crate a server listening to port 8050 at 127.0.0.1.
You can visit http://127.0.0.1:8050 to see this graph.
I'm currently trying to create a graph with plotly,
My goal would be to create a combined Barplot / Data table both controled with a range slider in order to controle the values with the date. I've succeded to create the barplot controled with the range slider.
I can't manage to control the table :/
Here is a combined plot but where the range slider is attached to the table, as you can see it does not control the date but the table view
https://plotly.com/~tristan1551/31/
Here is an exemple of a barplot i've done with a range slider https://plotly.com/~tristan1551/23/
Another idea would be to only to control the table with the ranger slider, i can't manager to do that too.
Is there a way to achive what i want to do ?
Thank you for your herlp :)
to synchronise a table with a range slider on a figure you can use a dash callback
below code creates a bar chart with a rangeslider
attaches a callback to changes in figure to get position of rangeslider
constructs table based on these inputs
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State
import pandas as pd
import numpy as np
import plotly.express as px
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/2014_apple_stock.csv")
df["AAPL_x"] = pd.to_datetime(df["AAPL_x"])
fig = px.bar(df, x="AAPL_x", y="AAPL_y").update_layout(
xaxis={
"range": [df["AAPL_x"].quantile(0.9), df["AAPL_x"].max()],
"rangeslider": {"visible": True},
}
)
# Build App
app = JupyterDash(__name__)
app.layout = html.Div(
[dcc.Graph(id="bargraph", figure=fig), html.Div(id="bartable", children=[])],
)
#app.callback(
Output("bartable", "children"),
Input("bargraph", "relayoutData"),
)
def updateTable(graphData):
global df
if graphData and "xaxis.range" in graphData.keys():
d1 = pd.to_datetime(graphData["xaxis.range"][0])
d2 = pd.to_datetime(graphData["xaxis.range"][1])
else:
d1 = df["AAPL_x"].quantile(0.9)
d2 = df["AAPL_x"].max()
dft = df.loc[df["AAPL_x"].between(d1, d2)]
return dash_table.DataTable(
columns=[{"name": c, "id": c} for c in dft.columns],
data=dft.to_dict("records"),
)
# Run app and display result inline in the notebook
app.run_server(mode="inline")
I am creating a dashboard in dash for a course at university. I created 3 histograms however, there are many unique values which give a long range of x values. In my plots I would like to show only the 10 or 20 values that have the highest count (top 10 values). Can someone help me out?
import plotly.express as px
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("forensics "),
dcc.Graph(id='graph'),
dcc.Graph(id='graph1'),
dcc.Graph(id='graph2'),
html.Label([
"select market",
dcc.Dropdown(
id='market', clearable=False,
value='whitehousemarket', options=[
{'label': c, 'value': c}
for c in posts['marketextract'].unique()
])
]),
])
# Define callback to update graph
#app.callback(
Output('graph', 'figure'),
Output('graph1', 'figure'),
Output('graph2', 'figure'),
[Input("market", "value")]
)
def update_figure(market):
fig=px.histogram(x=posts['datetime'].loc[posts['marketextract']==market])
fig1=px.histogram(x=posts['username'].loc[posts['marketextract']==market])
fig2=px.histogram(x=posts['drugs'].loc[posts['marketextract']==market])
return [fig, fig1, fig2]
# Run app and display result inline in the notebook
app.run_server(mode='inline')
To my knowledge, px.histogram() does not have a method to exclude certain observations of bins. But judging by the look of your data (please consider sharing a proper sample), what you're doing here is just showing the different counts of some user names. And you can easily do that through a combination of df.groupby() and px.histogram. Or px.bar() or go.Bar() for that matter, but we'll stick with px.histogram since that is what you're seeking help with. Anyway, using random selections of country names from px.gapminder you can use:
dfg = df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).head(10).reset_index()
fig = px.histogram(dfg, x='name', y = 'count')
And get:
If you drop .head(10) you'll get this instead:
And I hope this is the sort of functionality you were looking for. And don't be intimidated by the long df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).reset_index(). I'm not a pandas expert, so you could quite possibly find a more efficient approach. But it does the job. Here's the complete code with some sample data:
# imports
import pandas as pd
import plotly.express as px
import random
# data sample
gapminder = list(set(px.data.gapminder()['country']))[1:20]
names = random.choices(gapminder, k=100)
# data munging
df = pd.DataFrame({'name':names})
dfg = df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).reset_index()
dfg.columns = ['name', 'count']
# plotly
fig = px.histogram(dfg, x='name', y = 'count')
fig.layout.yaxis.title.text = 'count'
fig.show()
I am trying to plot 3 pie charts side by side. I don't understand why the following code is making the pie charts go across the page diagonally left to write rather than horizontally left to write in one line.
Here's my code:
app.layout = html.Div([
html.Div([
dcc.Graph(id='TPiePlot',
figure={
'data': [go.Pie(labels=labels1,
values=values1,
marker=dict(colors=colors, line=dict(color='#fff', width=1)),
hoverinfo='label+value+percent', textinfo='value',
domain={'x': [0, .25], 'y': [0, 1]}
)
],
'layout': go.Layout(title='T',
autosize=True
)
}
),
dcc.Graph(id='RPiePlot',
figure={
'data': [go.Pie(labels=labels2,
values=values2,
marker=dict(colors=colors, line=dict(color='#fff', width=1)),
hoverinfo='label+value+percent', textinfo='value',
domain={'x': [0.30, .55], 'y': [0, 1]}
)
],
'layout': go.Layout(title='R',
autosize=True
)
}
),
dcc.Graph(id='RoPiePlot',
figure={
'data': [go.Pie(labels=labels3,
values=values3,
marker=dict(colors=colors, line=dict(color='#fff', width=1)),
hoverinfo='label+value+percent', textinfo='value',
domain={'x': [0.60, 0.85], 'y': [0, 1]}
)
],
'layout': go.Layout(title='Ro',
autosize=True
)
}
)
])
])
Here is what's happening with option 1 from accepted answer (which is the one I need to go with). I'm getting three different sizes plus legend covering some of the pie chart:
I'm struggling to understand how to re-size dash graphs using CSS because the whole container increases in size rather than the actual graph and I don't know how to target just the graphs themself to make size bigger. Is there a way around this?
Plotly's domain is used for subplots. In your case you are plotting three individual plots one after the other and for each you are setting the domain separately.
You have at least two options:
Use the approach you are using now, i.e. 3 individual plots, and use CSS to define their position
Create one plot with three figures and use domain to adjust their position.
Option 1
import dash
import flask
import dash_html_components as html
import plotly.graph_objs as go
import dash_core_components as dcc
server = flask.Flask('app')
app = dash.Dash('app', server=server,
external_stylesheets=['https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'])
labels = [['monkeys', 'elephants'],
['birds', 'dinosaurs'],
['unicorns', 'giraffes']]
values = [[50, 40],
[100, 10],
[100, 20]]
data = []
for label, value in zip(labels, values):
data.append(html.Div([dcc.Graph(figure={'data': [go.Pie(labels=label,
values=value,
hoverinfo='label+value+percent', textinfo='value'
)]})
], className='col-sm-4'))
app.layout = html.Div(data, className='row')
app.run_server()
Option 2
import dash
import flask
import dash_html_components as html
import plotly.graph_objs as go
import dash_core_components as dcc
server = flask.Flask('app')
app = dash.Dash('app', server=server)
labels = [['monkeys', 'elephants'],
['birds', 'dinosaurs'],
['unicorns', 'giraffes']]
values = [[50, 40],
[100, 10],
[100, 20]]
data = []
x1 = 0
x2 = 0.25
for label, value in zip(labels, values):
data.append(go.Pie(labels=label,
values=value,
hoverinfo='label+value+percent', textinfo='value',
domain={'x': [x1, x2], 'y': [0, 1]}
)
)
x1 = x1 + 0.30
x2 = x1 + 0.25
app.layout = html.Div([
html.Div([dcc.Graph(figure={'data': data})])
])
app.run_server()
I'm using Plotly Dash to build a stacked bar chart with 3 trace values.
I'm trying to access the state of the trace values so that I can filter a dataframe and pass the resulting DF back to the plot, as opposed to simply hiding the traces on de-select.
for example, I have a dataframe :
Item Status Value
1 First 2000
1 Second 3490
1 Third 542
2 First 641
2 Second 564
3 First 10
My traces are 3 values (first, Second, Third) pertaining to a linear process where each value is a status marking the advancement of an item.
My intention is to be able to select statuses from further down the progression so only those items that have advanced to a certain step are plotted.
As I select more advanced statuses in the trace legend, my plotted x-values should drop off since fewer advance that far, even though they all share the majority of the statuses
The only solution I can think of is to make checkboxes for each trace value and use those inputs in a callback, but that seems redundant to the select/de-select traces functionality built in.
You looking for something like that?
Code:
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({'Item': [1, 1, 1, 2, 2, 3],
'Status': ["First", "Second", "Third",
"First", "Second", "First"],
'Value': [2000, 3490, 542, 641, 564, 10]})
colors = {
'background': '#111111',
'background2': '#FF0',
'text': '#7FDBFF'
}
df1 = df.loc[df["Status"] == "First"]
df2 = df.loc[df["Status"] == "Second"]
df3 = df.loc[df["Status"] == "Third"]
trace1 = go.Bar(
x=df1["Item"],
y=df1["Value"],
name='First',
)
trace2 = go.Bar(
x=df2["Item"],
y=df2["Value"],
name='Second',
)
trace3 = go.Bar(
x=df3["Item"],
y=df3["Value"],
name='Third',
)
app.layout = html.Div(children=[
html.Div([
html.H5('Your Plot'),
dcc.Graph(
id='cx1',
figure=go.Figure(data=[trace1, trace2, trace3],
layout=go.Layout(barmode='stack')))],)])
if __name__ == '__main__':
app.run_server(debug=True)
Output: