Say I have a function that returns a chart. I want a user to be able to select from a drop down menu and their selections become the inputs to the function. Here is an MRE, but the actual charts and data I'm using are much more complicated so I don't want to use shortcuts or change which data is passed into the chart.
I've already read this documentation. https://plotly.com/python/dropdowns/
Here is the MRE:
import plotly.express as px
data_canada = px.data.gapminder().query("country == 'Canada'")
def charts(input):
if input == 'A':
fig = px.bar(data_canada, x='year', y='pop')
if input == 'B':
fig = px.bar(data_canada, x='year', y='lifeExp')
fig.show()
So what I need from here is how to create drop down menus that is an input to this function.
One way to solve this is to use updatemenus, where you basically update the visibility of the two traces, based on the selection in the dropdown:
import plotly.express as px
data_canada = px.data.gapminder().query("country == 'Canada'")
fig = px.bar(data_frame=data_canada, x='year', y=['pop', 'lifeExp'], title="LifeExp") # Plotly 4.8 and above!
fig.update_layout(
showlegend=False, # hide the legend so the default is not confusing
updatemenus=[
dict(
active=0,
buttons=list([
dict(label="LifeExp",
method="update",
args=[{"visible": [False, True]},
{"title": "LifeExp"}]),
dict(label="Population",
method="update",
args=[{"visible": [True, False]},
{"title": "Population"}])
])
)]
)
This should give the following result with the Gapminder data example:
What happens here is the following: First, you generate the figure with 2 traces and a default legend. Then, you add the dropdown and you make it control the visibility of the traces. The first option in the dropdown will be the default.
Note: this support for "wide-format" selection of columns is only available from Plotly 4.8.2 and higher (all numeric types are considered "the same" in wide-format from that version on
Related
Hello guys i need help i have a piece of code that adds a list of horizontal lines to a Plotly figure in python. i want to change the code to were we will use a slider to add the horizontal lines to the figure and relayout it. As the slider moves right more horizontal lines will be added to the figure when the slider moves left the horizontal lines will be removed from the figure. Below is the bit of my code so far
for v in range(len(sortadlist)):
fig.add_hline(y=sortadlist[v][0], line_color='brown', line_width=1.5, row=1, col=1)
fig.add_shape(type="rect",
y0=round(sortadlist[v][0],2)-.3, y1=round(sortadlist[v][0],2)+.3, x0=-1, x1=len(df),
fillcolor="darkcyan",
opacity=0.15)
All the code above does is loop through a list of numbers and uses the fig.add_hline to add the horizontal line to the figure. I need help creating a slider that will add the horizontal lines to the figure
This is how the figure currently looks i want a slider to help with adding more horizontal lines to the figure and remove them also
Since the entire code is not available, the sample data was handled by obtaining the company's stock price. First of all, horizontal lines and shapes do not have a show/hide attribute, so they are not compatible with sliders. So I have created a code to draw a line according to the appropriate price list in the line chart of the scatter chart. Once the line chart is hidden, the first line chart is made visible.
The structure of the graph is a candlestick with 6 lines and the candlestick is always displayed. A loop process is used to create a list of lines to be shown or hidden.
import yfinance as yf
import plotly.graph_objects as go
import numpy as np
df = yf.download("AAPL", start="2022-01-01", end="2023-01-01", progress=False)
df.reset_index(inplace=True)
pricelist = np.arange(130,190,10)
fig = go.Figure()
fig.add_trace(go.Candlestick(x=df['Date'],
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Close'],
name='AAPL'
)
)
fig.add_hrect(y0=df['Close'].median()-10,
y1=df['Close'].median()+10,
annotation_text="Median+-10",
annotation_position="top right",
fillcolor="darkcyan",
opacity=0.25,
line_width=0)
for p in pricelist:
fig.add_trace(go.Scatter(x=df['Date'],
y=[p]*len(df['Date']),
line_color='blue',
name=str(p),
showlegend=False,
visible=False,
)
)
fig.data[1].visible = True
steps = []
for i in np.arange(1,len(fig.data)):
step = dict(
method="update",
args=[{"visible": [False] * len(fig.data)},
{"title": "Price lines: " + str(pricelist[i-1])}],
label=str(pricelist[i-1])
)
step["args"][0]["visible"][0] = True
step["args"][0]["visible"][i] = True
steps.append(step)
sliders = [dict(
active=10,
currentvalue={"prefix": "Price: "},
pad={"t": 50},
steps=steps
)]
fig.update_layout(
sliders=sliders
)
fig.update_layout(height=600, xaxis_rangeslider_visible=False)
fig.show()
I'm trying to create an interactive PCA plot using plotly-express and graph objects in python (go.Scatter).
The plot should have 2 dropdowns menus (for x-axis and y-axis) to change between the first 5 PCA in the data.
Each data point also belongs to a treatment group either Before, After, or QC.
I was able to plot the PCA1 and PCA2 with plotly-express package but when trying to add the 2 dropdown menus that will update the plot between the 5 PCA it become a mess.
The example data is in my GitHub link,the first 5 columns are the first 5 PCAs.
The code the generate PC1 vs PC2 is:
labels={'0': 'PC 1 (22.0%)',
'1': 'PC 2 (19.6%)',
'2': 'PC 3 (11.1%)',
'3': 'PC 4 (8.2%)',
'4': 'PC 5 (3.9%)',
'color': 'Group'}
fig1 = px.scatter(components_df, x=0 , y=2 ,
color = 'Class',
width=1000, height=700,
template='presentation',
labels=labels,
title="PCA Score Plot (PC{} vs. PC{})".format(1, 2) ,
hover_data=['idx', 'SampleID']
)
fig1.show()
and it looks like this :
I'm trying to add 2 dropdown menus like I draw above to update the x-axis and the y-axis with the different PC's.
So first step was to add_trace on the figure to add other PCs to the figure but dont know how to add graph object to plotly-express to that what i did:
fig = go.Figure()
for Class, group in components_df.groupby("Class"):
# print(group[0])
fig.add_trace(go.Scatter(x=group[0], y=group[1], name=Class, mode='markers',
hovertemplate="Class=%s<br>PC1=%%{x}<br>PC2=%%{y}<extra></extra>"% Class))
for Class, group in components_df.groupby("Class"):
# print(group[0])
fig.add_trace(go.Scatter(x=group[0], y=group[2], name=Class, mode='markers',
hovertemplate="Class=%s<br>PC1=%%{x}<br>PC3=%%{y}<extra></extra>"% Class))
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
buttons=list(
[dict(label = 'All',
method = 'update',
args = [{'visible': [True, True, True, True,True]},
{'title': 'All',
'showlegend':True}]),
dict(label = 'PC1 PC1',
method = 'update',
args = [{'visible': [True, False, False, False, False]}, # the index of True aligns with the indices of plot traces
{'title': 'PC1 PC1',
'showlegend':True}]),
dict(label = 'PC1 PC2',
method = 'update',
args = [{'visible': [False, True, False, False, False]},
{'title': 'AAPL',
'showlegend':True}]),
dict(label = 'PC1 PC3',
method = 'update',
args = [{'visible': [False, False, True, False, False]},
{'title': 'AMZN',
'showlegend':True}]),
])
)
])
and that is the result:
There are many problems with that:
when changing the different options in the dropdown menu also the legends change (they suppose the stay fixed)
when changing the different options in the dropdown menu it does not lools like the data should be
it does not look nice like in the plotly-express.
there is only one dropdown
The code is base on many explanations in the documentation and blogs:
How to change plot data using dropdowns
Dropdown Menus in Python
Adding interactive filters
Setting the Font, Title, Legend Entries, and Axis Titles in Python
Any hint will be appreciated on how to add correct add_trac or correct dropdown menu
Thank you!!!
it's all about being highly structured and systematic. Plotly Express does generate a decent base chart. Use fig1.to_dict() to view graph object structures it has built
challenge I found with adding updatemenus to Plotly Express figure - it's a multi-trace figure with trace defining marker color. This can be simplified to a single trace figure with an array defining marker color
then it's a case of building updatemenus. This I have done as nested list comprehensions. Outer loop axis (each menu), inner loop principle component (each menu item)
Updates
magic colors - fair critique. I had used a hard coded dict for color mapping. Now programmatically build cmap Reverted back to static definition of cmap as dict comprehension is not wanted. Changed to a pandas approach to building cmap with lambda function
"y": 1 if ax == "x" else 0.9 We are building two drop downs, one for xaxis and one for yaxis. Hopefully it's obvious that the positions of these menus needs to be different. See docs: https://plotly.com/python/reference/layout/updatemenus/ For similar reason active property s being set. Make sure drop downs show what is actually plotted in the figure
legend refer back to point I made about multi-trace figures. Increases complexity! Have to use synthetic traces and this technique Plotly: How to update one specific trace using updatemenus?
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
components_df = pd.read_csv(
"https://raw.githubusercontent.com/TalWac/stakoverflow-Qustion/main/components_df.csv"
)
labels = {
"0": "PC 1 (22.0%)",
"1": "PC 2 (19.6%)",
"2": "PC 3 (11.1%)",
"3": "PC 4 (8.2%)",
"4": "PC 5 (3.9%)",
"color": "Group",
}
# cmap = {
# cl: px.colors.qualitative.Plotly[i]
# for i, cl in enumerate(
# components_df.groupby("Class", as_index=False).first()["Class"]
# )
# }
# revert back to static dictionary as dynamic building is not wanted
# cmap = {'After': '#636EFA', 'Before': '#EF553B', 'QC': '#00CC96'}
# use lambda functions instead of dict comprehension
df_c = components_df.groupby("Class", as_index=False).first()
df_c["color"] = df_c.apply(lambda r: px.colors.qualitative.Plotly[r.name], axis=1)
cmap = df_c.set_index("Class").loc[:,"color"].to_dict()
fig1 = go.Figure(
go.Scatter(
x=components_df["0"],
y=components_df["1"],
customdata=components_df.loc[:, ["idx", "SampleID", "Class"]],
marker_color=components_df["Class"].map(cmap),
mode="markers",
hovertemplate="Class=%{customdata[2]}<br>x=%{x}<br>y=%{y}<br>idx=%{customdata[0]}<br>SampleID=%{customdata[1]}<extra></extra>",
)
).update_layout(
template="presentation",
xaxis_title_text=labels["0"],
yaxis_title_text=labels["1"],
height=700,
)
fig1.update_layout(
updatemenus=[
{
"active": 0 if ax == "x" else 1,
"buttons": [
{
"label": f"{ax}-PCA{pca+1}",
"method": "update",
"args": [
{ax: [components_df[str(pca)]]},
{f"{ax}axis": {"title": {"text": labels[str(pca)]}}},
[0],
],
}
for pca in range(5)
],
"y": 1 if ax == "x" else 0.9,
}
for ax in ["x", "y"]
]
).update_traces(showlegend=False)
# add a legend by using synthetic traces. NB, this will leave markers at 0,0
fig1.add_traces(
px.scatter(
components_df.groupby("Class", as_index=False).first(),
x="0",
y="1",
color="Class",
color_discrete_map=cmap,
)
.update_traces(x=[0], y=[0])
.data
)
I am plotting chart using below code:
fig = px.line(df, x='Time', y=['one','two'], color= df.index)
fig['layout']['xaxis']['autorange'] = "reversed"
fig.update_layout(legend_title="Price")
fig.show()
Dataframe i am working with like is below:
Time one two
100 9:30 129 243
110 10:30 234 453
120 11:00 155 234
Want to add dropdown menu to select from index and show one row at a time in chart.
example if i select 110 from drop down it should only show chart for that row.
Is there any easy fix for it.
Thank you in adavance.
Here's my solution:
In order to set the proper options for the dropdown menu, it would be helpful to have a function that creates the list of options (shown below)
# Create proper buttons list
def makeButtonsList(idxs):
buttons = []
for i, idx in enumerate(idxs):
visibleArr = np.full((2*df.index.shape[0],),
False, dtype=bool) # 2x number of booleans since one/two vals are separate plots
visibleArr[2*i] = True # Set two booleans next to each other (representing one & two) to true
visibleArr[(2*i)+1] = True
buttons.append(dict(label=str(idx),
method='update',
args=[{'visible': list(visibleArr)}])) # 'Visible' arg determines which plots are shown
# depending on which dropdown is selected
return buttons
Next create the traces for the data (with your sample data, I created a bar chart but you could easily modify this)
traces = []
for i in range(df.Time.shape[0]):
rowData = df.iloc[i, :]
time = rowData.Time
one = rowData.one
two = rowData.two
traces.append(go.Bar(x=[time], y=[one], name='One'))
traces.append(go.Bar(x=[time], y=[two], name='Two'))
where df is the dataframe you are working with.
Finally put it all together and create the Plotly plot!
# Import packages
import pandas as pd
import numpy as np
import plotly.graph_objs as go
import plotly.express as px
# Create proper buttons list
def makeButtonsList(idxs):
buttons = []
for i, idx in enumerate(idxs):
visibleArr = np.full((2*df.index.shape[0],),
False, dtype=bool) # 2x number of booleans since one/two vals are separate plots
visibleArr[2*i] = True # Set two booleans next to each other (representing one & two) to true
visibleArr[(2*i)+1] = True
buttons.append(dict(label=str(idx),
method='update',
args=[{'visible': list(visibleArr)}])) # 'Visible' arg determines which plots are shown
# depending on which dropdown is selected
return buttons
# Create traces
traces = []
for i in range(df.Time.shape[0]):
rowData = df.iloc[i, :]
time = rowData.Time
one = rowData.one
two = rowData.two
traces.append(go.Bar(x=[time], y=[one], name='One'))
traces.append(go.Bar(x=[time], y=[two], name='Two'))
# Create figure
fig = go.Figure(data=traces)
# Add dropdown options
fig.update_layout(
updatemenus=[
dict(
buttons=makeButtonsList(df.index),
direction="down",
pad={"r": 10, "t": 10},
showactive=True,
x=0.55,
xanchor="left",
y=1.2,
yanchor="top"
),
]
)
# Add annotation for index selected
fig.update_layout(
annotations=[
dict(text="Index:", showarrow=False,
x=0, y=1.15, yref="paper", align="left")
],
xaxis_title = 'Time',
yaxis_title = 'Value',
)
# Show the plot
fig.show()
Here is a sample plot:
BONUS:
If you think this method is tedious, and a slider bar would do the job just fine, Plotly supports animation of bar charts. Here is the following code you could use:
fig = px.bar(df, x='Time', y=['one','two'], animation_frame=df.index)
fig.update_layout(title='Data', barmode='group')
fig.show()
Here is the resulting plot:
I can't seem to figure out the documentation. Basically I want to create a dropdown menu and each selection outputs a different chart. Here is an MRE. plotly.express is imported as px.
race = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/race_debt_ubi')
education = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/education_debt_ubi')
income = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/income_debt_ubi')
fig_race = px.bar(race, x='race', y='percent_has_debt', text='percent_has_debt')
fig_education = px.bar(education, y='percent_has_debt', text='percent_has_debt')
fig_income = px.bar(income, y='percent_has_debt', text='percent_has_debt')
Basically I want to create a dropdown menu of ['race', 'education', 'income'] that outputs the corresponding chart.
I've made your data into a graph that you can select in a drop-down with the 'go' in 'plotly'. The default is to show all the values. I modified it with the help of the official reference and this site.
import plotly.graph_objects as go
import pandas as pd
race = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/race_debt_ubi')
education = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/education_debt_ubi')
income = pd.read_csv('https://github.com/ngpsu22/Ed_Debt-vs.-UBI/raw/main/income_debt_ubi')
fig = go.Figure()
fig.add_trace(go.Bar(x=race['race'], y=race['percent_has_debt'], name='race'))
fig.add_trace(go.Bar(x=[0,1,2,3], y=education['percent_has_debt'], name='education'))
fig.add_trace(go.Bar(x=[0,1,2,3,4,5], y=income['percent_has_debt'], name='income'))
fig.update_layout(
updatemenus=[go.layout.Updatemenu(
active=0,
buttons=list([
dict(label="None",
method="update",
args=[{'visible':[False,False,False]},
{'title':'AL','showlegend':True}]),
dict(label="race",
method="update",
args=[{'visible':[True,False,False]},
{'title':'RACE','showlegend':True}]),
dict(label="education",
method="update",
args=[{'visible':[False,True,False]},
{'title':'EDUCATION','showlegend':True}]),
dict(label="income",
method="update",
args=[{'visible':[False,False,True]},
{'title':'INCOME','showlegend':True}]),
]
))])
fig.show()
I'm trying to create a drop down menu using the following code:
import plotly.express as px
df = px.data.gapminder()
fig = px.line(df, x="year", y="lifeExp", color="country")
buttons = [
{'method' : 'update', 'label' : val, 'args' : df[df['continent'].eq(val)]['lifeExp']}
for val in df['continent'].unique()
]
# construct menus
updatemenus = [{'buttons': buttons,
'direction': 'down',
'showactive': True,}]
# update layout with buttons, and show the figure
fig.update_layout(updatemenus=updatemenus)
fig.show()
I've added the list comprehension to generate the y values, but I"m not sure what I'm currently doing wrong here.
I'm expecting to be able to select a different continent and have the plot update