Python Plotly display other information on Hover - python

Here is the code that I have tried:
# import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.read_csv("resultant_data.txt", index_col = 0, sep = ",")
display=df[["Velocity", "WinLoss"]]
pos = lambda col : col[col > 0].sum()
neg = lambda col : col[col < 0].sum()
Related_Display_Info = df.groupby("RacerCount").agg(Counts=("Velocity","count"),
WinLoss=("WinLoss","sum"),
Positives=("WinLoss", pos),
Negatives=("WinLoss", neg),
)
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(
go.Scatter(x=display.index, y=display["Velocity"], name="Velocity", mode="markers"),
secondary_y=False
)
fig.add_trace(
go.Scatter(x=Related_Display_Info.index,
y=Related_Display_Info["WinLoss"],
name="Win/Loss",
mode="markers",
marker=dict(
color=(
(Related_Display_Info["WinLoss"] < 0)
).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]
)
),
secondary_y=True,
)
# Add figure title
fig.update_layout(
title_text="Race Analysis"
)
# Set x-axis title
fig.update_xaxes(title_text="<b>Racer Counts</b>")
# Set y-axes titles
fig.update_yaxes(title_text="<b>Velocity</b>", secondary_y=False)
fig.update_yaxes(title_text="<b>Win/Loss/b>", secondary_y=True)
fig.update_layout(hovermode="x unified")
fig.show()
The output is:
But I was willing to display the following information when I hover on the point:
RaceCount = From Display dataframe value Number of the race corresponding to the dot I hover on.
Velocity = From Display Dataframe value Velocity at that point
Counts = From Related_Display_Info Column
WinLoss = From Related_Display_Info Column
Positives = From Related_Display_Info Column
Negatives = From Related_Display_Info Column
Please can anyone tell me what to do to get this information on my chart?
I have checked this but was not helpful since I got many errors: Python/Plotly: How to customize hover-template on with what information to show?
Data:
RacerCount,Velocity,WinLoss
111,0.36,1
141,0.31,1
156,0.3,1
141,0.23,1
147,0.23,1
156,0.22,1
165,0.2,1
174,0.18,1
177,0.18,1
183,0.18,1
114,0.32,1
117,0.3,1
120,0.29,1
123,0.29,1
126,0.28,1
129,0.27,1
120,0.32,1
144,0.3,1
147,0.3,1
159,0.27,1
165,0.26,1
168,0.25,1
156,0.29,1
165,0.26,1
168,0.26,1
165,0.28,1
213,0.17,1
243,0.15,1
249,0.14,1
228,0.54,1
177,0.67,1
180,0.66,1
183,0.65,1
192,0.66,1
195,0.62,1
198,0.6,1
180,0.66,1
222,0.56,1
114,0.41,1
81,0.82,1
102,0.56,1
111,0.55,1
90,1.02,1
93,1.0,1
90,1.18,1
90,1.18,1
93,1.1,1
96,1.07,1
99,1.04,1
102,0.99,1
105,0.94,1
108,0.92,1
111,0.9,1
162,0.66,1
159,0.63,1
162,0.65,-1
162,0.66,-1
168,0.64,-1
159,0.68,-1
162,0.67,-1
174,0.62,-1
168,0.65,-1
171,0.64,-1
198,0.55,-1
300,0.47,-1
201,0.56,-1
174,0.63,-1
180,0.61,-1
171,0.64,-1
174,0.62,-1
303,0.47,-1
312,0.48,-1
258,0.51,-1
261,0.51,-1
264,0.5,-1
279,0.47,-1
288,0.48,-1
294,0.47,-1
258,0.52,-1
261,0.51,-1
267,0.5,-1
222,0.53,-1
171,0.64,-1
177,0.63,-1
177,0.63,-1

Essentially, this code ungroups the data frame before plotting to create the hovertemplate you're looking for.
As stated in the comments, the data has to have the same number of rows to be shown in the hovertemplate. At the end of my answer, I added the code all in one chunk.
Since you have hovermode as x unified, you probably only want one of these traces to have hover content.
I slightly modified the creation of Related_Display_Info. Instead of WinLoss, which is already in the parent data frame, I modified it to WinLoss_sum, so there wouldn't be a naming conflict when I ungrouped.
Related_Display_Info = df.groupby("RacerCount").agg(
Counts=("Velocity","count"), WinLoss_sum=("WinLoss","sum"),
Positives=("WinLoss", pos), Negatives=("WinLoss", neg))
Now it's time to ungroup the data you grouped. I created dui (stands for display info ungrouped).
dui = pd.merge(df, Related_Display_Info, how = "outer", on="RacerCount",
suffixes=(False, False))
I created the hovertemplate for both traces. I passed the entire ungrouped data frame to customdata. It looks like the only column that isn't in the template is the original WinLoss.
# create hover template for all traces
ht="<br>".join(["<br>RacerCount: %{customdata[0]}",
"Velocity: %{customdata[1]:.2f}",
"Counts: %{customdata[3]}",
"Winloss: %{customdata[4]}",
"Positives: %{customdata[5]}",
"Negatives: %{customdata[6]}<br>"])
The creation of fig is unchanged. However, the traces are both based on dui. Additionally, the index isn't RacerCount, so I used the literal field instead.
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(go.Scatter(x=dui["RacerCount"], y=dui["Velocity"],
name="Velocity", mode="markers",
customdata=dui, hovertemplate=ht),
secondary_y=False)
fig.add_trace(
go.Scatter(x = dui["RacerCount"], y=dui["WinLoss_sum"], customdata=dui,
name="Win/Loss", mode="markers",
marker=dict(color=((dui["WinLoss_sum"] < 0)).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]),
hovertemplate=ht),
secondary_y=True)
All the code altogether (for easier copy + paste)
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.read_clipboard(sep = ',')
display=df[["Velocity", "WinLoss"]]
pos = lambda col : col[col > 0].sum()
neg = lambda col : col[col < 0].sum()
Related_Display_Info = df.groupby("RacerCount").agg(
Counts=("Velocity","count"), WinLoss_sum=("WinLoss","sum"),
Positives=("WinLoss", pos), Negatives=("WinLoss", neg))
# ungroup the data for the hovertemplate
dui = pd.merge(df, Related_Display_Info, how = "outer", on="RacerCount",
suffixes=(False, False))
# create hover template for all traces
ht="<br>".join(["<br>RacerCount: %{customdata[0]}",
"Velocity: %{customdata[1]:.2f}",
"Counts: %{customdata[3]}",
"Winloss: %{customdata[4]}",
"Positives: %{customdata[5]}",
"Negatives: %{customdata[6]}<br>"])
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(go.Scatter(x=dui["RacerCount"], y=dui["Velocity"],
name="Velocity", mode="markers",
customdata=dui, hovertemplate=ht),
secondary_y=False)
fig.add_trace(
go.Scatter(x = dui["RacerCount"], y=dui["WinLoss_sum"], customdata=dui,
name="Win/Loss", mode="markers",
marker=dict(color=((dui["WinLoss_sum"] < 0)).astype('int'),
colorscale=[[0, 'green'], [1, 'red']]),
hovertemplate=ht),
secondary_y=True)
# Add figure title
fig.update_layout(
title_text="Race Analysis"
)
# Set x-axis title
fig.update_xaxes(title_text="<b>Racer Counts</b>")
# Set y-axes titles
fig.update_yaxes(title_text="<b>Velocity</b>", secondary_y=False)
fig.update_yaxes(title_text="<b>Win/Loss/b>", secondary_y=True)
fig.update_layout(hovermode="x unified")
fig.show()

Related

Connecting data points with lines in a Plotly boxplot in Python

I am working on some boxplots. I found this code very helpful and I managed to replicate it for my needs:
import plotly.express as px
import numpy as np
import pandas as pd
np.random.seed(1)
y0 = np.random.randn(50) - 1
y1 = np.random.randn(50) + 1
df = pd.DataFrame({'graph_name':['trace 0']*len(y0)+['trace 1']*len(y1),
'value': np.concatenate([y0,y1],0),
'color':np.random.choice([0,1,2,3,4,5,6,7,8,9], size=100, replace=True)}
)
fig = px.strip(df,
x='graph_name',
y='value',
color='color',
stripmode='overlay')
fig.add_trace(go.Box(y=df.query('graph_name == "trace 0"')['value'], name='trace 0'))
fig.add_trace(go.Box(y=df.query('graph_name == "trace 1"')['value'], name='trace 1'))
fig.update_layout(autosize=False,
width=600,
height=600,
legend={'traceorder':'normal'})
fig.show()
I am now trying to put some lines connecting the datapoints with the same colors, but I am lost. Any idea?
Something similar to this:
My first idea was to add lines to your figure by using plotly shapes and specifying the start and end points in x- and y-axis coordinates. However, when you use px.strip, plotly implements jittering (adding randomly generated small values, say between -0.1 and 0.1, to the x-coordinates under the hood to avoid points overlapping), but as far as I know, there is no way to retrieve the exact x-coordinates of each point.
However we can get around this by using go.Scatter to plot all the paired points individually, adding jittering as needed to the x-values and connecting each pair of points with a line. We are basically implementing px.strip ourselves but with full control of the exact coordinates of each point.
In order to toggle colors the same way that px.strip allows you to, we need to assign all points of the same color to the same legendgroup, and also only show the legend entry the first time a color is plotted (as we don't want an legend entry for each point)
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import pandas as pd
np.random.seed(1)
y0 = np.random.randn(50) - 1
y1 = np.random.randn(50) + 1
## sort both sets of data so we can easily connect them with line annotations
y0.sort()
y1.sort()
df = pd.DataFrame({'graph_name':['trace 0']*len(y0)+['trace 1']*len(y1),
'value': np.concatenate([y0,y1],0)}
# 'color':np.random.choice([0,1,2,3,4,5,6,7,8,9], size=100, replace=True)}
)
fig = go.Figure()
## i will set jittering to 0.1
x0 = np.array([0]*len(y0)) + np.random.uniform(-0.1,0.1,len(y0))
x1 = np.array([1]*len(y0)) + np.random.uniform(-0.1,0.1,len(y0))
## px.colors.sequential.Plasma contains 10 distinct colors
## colors_list = np.random.choice(px.colors.qualitative.D3, size=50)
## for simplicity, we repeat it 5 times instead of selecting randomly
## this guarantees the colors appear in order in the legend
colors_list = px.colors.qualitative.D3*5
color_number = {i:color for color,i in enumerate(px.colors.qualitative.D3)}
## keep track of whether the color is showing up for the first time as we build out the legend
colors_legend = {color:False for color in colors_list}
for x_start,x_end,y_start,y_end,color in zip(x0,x1,y0,y1,colors_list):
## if the color hasn't been added to the legend yet, add a legend entry
if colors_legend[color] == False:
fig.add_trace(
go.Scatter(
x=[x_start,x_end],
y=[y_start,y_end],
mode='lines+markers',
marker=dict(color=color),
line=dict(color="rgba(100,100,100,0.5)"),
legendgroup=color_number[color],
name=color_number[color],
showlegend=True,
hoverinfo='skip'
)
)
colors_legend[color] = True
## otherwise omit the legend entry, but add it to the same legend group
else:
fig.add_trace(
go.Scatter(
x=[x_start,x_end],
y=[y_start,y_end],
mode='lines+markers',
marker=dict(color=color),
line=dict(color="rgba(100,100,100,0.5)"),
legendgroup=color_number[color],
showlegend=False,
hoverinfo='skip'
)
)
fig.add_trace(go.Box(y=df.query('graph_name == "trace 0"')['value'], name='trace 0'))
fig.add_trace(go.Box(y=df.query('graph_name == "trace 1"')['value'], name='trace 1'))
fig.update_layout(autosize=False,
width=600,
height=600,
legend={'traceorder':'normal'})
fig.show()

stacked barplot in plotly

My input:
names_col = ['Count','Percentage']
dat = [['Matching', 63],['Mismatching', 37]]
plot_df = pd.DataFrame(data=dat,columns=names_col)
I just trying plot within plotly simple bar char where stacked.
my code:
fig = px.bar(p_df, x='Count', y='Percentage', color='Count' ,title='My plot', barmode='stack')
fig.show();
And what I get:
That not what I expected. I want something like this:
Here code within seaborn:
p=p_df.set_index('Count').T.plot(kind='bar', stacked=True, figsize=(12,8),rot=0)
p.set_title('BBPS.2')
for x in p.containers:
p.bar_label(x, label_type='edge', weight='bold')
p.bar_label(x, label_type='center', weight='bold', color='white')
plt.show();
By setting the x axis to 'Count' you are defining the bars to not be stacked.
You could either find a different parameter for the x axis or add a dummy column with the same value for both rows so they have the same x value:
import pandas as pd
import plotly.express as px
names_col = ['Count','Percentage', 'dummy']
dat = [['Matching', 63, 0],['Mismatching', 37, 0]]
plot_df = pd.DataFrame(data=dat,columns=names_col)
fig = px.bar(plot_df, x='dummy', y='Percentage', color='Count' ,title='My plot')
fig.show()
The result:
You need to set the base to the first bar in order to stack them. Right now you have merely defined two separate bars. Take a look at this code from a dev.to post:
fig3 = go.Figure(
data=[
go.Bar(
name="Original",
x=data["labels"],
y=data["original"],
offsetgroup=0,
),
go.Bar(
name="Model 1",
x=data["labels"],
y=data["model_1"],
offsetgroup=1,
),
go.Bar(
name="Model 2",
x=data["labels"],
y=data["model_2"],
offsetgroup=1,
base=data["model_1"],
)
],
layout=go.Layout(
title="Issue Types - Original and Models",
yaxis_title="Number of Issues"
)
)
fig3.show()
That resulted in a plot that looks like this:

How to get rid of scribbled lines in plotly line plot?

I am trying to plot a subplot using plotly where I have some line plots and all the plots in the subplot needs to share the same x-axis as shown.
fig = make_subplots(
rows=5,
cols=1,
vertical_spacing=0.05,
subplot_titles=['Count / Anzahl', 'min_nValue', 'max_nValue', 'avg_nValue', 'sum_nValue'],
shared_xaxes=True,
)
fig.append_trace(go.Scatter(
x=df_dict_nValueAgg['Erste_15_Minuten']['KurzName'],
y=df_dict_nValueAgg['Erste_15_Minuten']['min_nValueNorm'],
name = "min_nValue_" + "Erste_15_Minuten",
mode='lines+markers',
#legendgroup = 2
), row=2, col=1)
fig.append_trace(go.Scatter(
x=df_dict_nValueAgg['Erste_15_Minuten']['KurzName'],
y=df_dict_nValueAgg['Erste_15_Minuten']['max_nValueNorm'],
name = "max_nValue_" + "Erste_15_Minuten",
mode='lines+markers',
#legendgroup = 2
), row=2, col=1)
.
.
.
# couple of plots more
.
.
fig.update_layout(
legend_orientation="v",
height=1000,
width=2000,
title_text=currentEventTitle+pastEventTitle+nAttributes,
)
fig.update_xaxes(tickangle=45)
fig.write_image('fig1.png')
fig.show()
which gives me this figure
So I filter the data for each
The last three plots produces scribbled lines. Now I understand that since I am filtering the data based on four values of a column i.e. Erste_15_Minuten, Zweite_15_Minuten, Dritte_15_Minuten and Letzte_15_Minuten the number of xticks for the last three plots are unequal or maybe in different order. Is there a way where I can avoid this problem? Switching to Bar Plot would avoid this problem but I need to use only line plot. Thank you in advance.
from looking at your code. There are multiple data frames of same format in a dict
there is no guarantee that these dataframes are in same KurzName order
have simulated data to match above understanding
then have provided a way to re-order data frames to be consistent with third for generating line plots
merge to first dataframe on KurzName
use index of first dataframe to define sort order
looking at image
bar chart - ok, not order dependent
first line chart is scribbled, second is not
hence forcing order of data frames has resolved the issue
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# fmt: off
words = ['adipisci', 'aliquam', 'amet', 'consectetur', 'dolor', 'dolore', 'dolorem', 'eius', 'est', 'etincidunt', 'ipsum', 'labore', 'magnam', 'modi', 'neque', 'non', 'numquam', 'porro', 'quaerat', 'quiquia', 'quisquam', 'sed', 'sit', 'tempora', 'ut', 'velit', 'voluptatem']
# fmt: on
r = np.random.choice(words, [2, 30])
r = np.char.add(r[0], np.char.add("_", r[1]))
# Erste_15_Minuten, Zweite_15_Minuten, Dritte_15_Minuten and Letzte_15_Minuten the number
df_dict_nValueAgg = {}
for k in [
"Erste_15_Minuten",
"Zweite_15_Minuten",
"Dritte_15_Minuten",
"Letzte_15_Minuten",
]:
np.random.shuffle(r)
df_dict_nValueAgg[k] = pd.DataFrame(
{
"KurzName": r,
"Count": np.random.randint(100, 300, len(r)),
"min_nValueNorm": np.random.uniform(0, 0.5, len(r)),
"max_nValueNorm": np.random.uniform(0.5, 1, len(r)),
}
)
fig = make_subplots(
rows=5,
cols=1,
vertical_spacing=0.05,
subplot_titles=[
"Count / Anzahl",
"min_nValue",
"max_nValue",
"avg_nValue",
"sum_nValue",
],
shared_xaxes=True,
)
for k in df_dict_nValueAgg.keys():
fig.add_trace(
go.Bar(
x=df_dict_nValueAgg[k]["KurzName"], y=df_dict_nValueAgg[k]["Count"], name=k
),
row=1,
col=1,
)
# this will be scibbled as each dataframe is in a different order
for k in df_dict_nValueAgg.keys():
fig.add_trace(
go.Scatter(
x=df_dict_nValueAgg[k]["KurzName"],
y=df_dict_nValueAgg[k]["max_nValueNorm"],
name=k + " scribble max",
),
row=4,
col=1,
)
# force order of dataframes to be same as first
for i, k in enumerate(df_dict_nValueAgg.keys()):
df = df_dict_nValueAgg[k]
if i > 0:
df = df.merge(
df_dict_nValueAgg[list(df_dict_nValueAgg.keys())[0]]
.loc[:, "KurzName"]
.reset_index(),
on="KurzName",
).sort_values("index")
fig.add_trace(
go.Scatter(
x=df["KurzName"],
y=df["max_nValueNorm"],
name=k + " max",
),
row=5,
col=1,
)
fig

Plotly: How to make a plotly dropdown menu for figures with wholly different data and layouts?

I am trying to make an interactive plot with a dropdown menu that selects from a series of wholly unrelated figures (i.e. plots that rely on different data structures and that have very different layouts). All of the dropdown menu examples I have seen are based on either a single set of data or multiple datasets but that use a relatively simple plot layout. This is not applicable to my case where I am trying to merge dozens of plots with very different layouts and underlying data. Below is a working example of the plots that I am trying to merge. The layouts are highly different across each plot:
import plotly.graph_objs as go
import plotly.express as px
import pandas as pd
# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
bar_y = ['Bar1', 'Bar2'],
bar_x = [2,3],
bar_z = [1,2]
))
# Make bar graph
fig1 = px.bar(df1,
x="bar_x",
y='bar_y',
color='bar_z',
orientation='h',
)
# Add layout attributes
fig1.update_layout(
xaxis_title="<b> Bar graph title <b>",
yaxis_title="<b> Bar x axis <b>",
legend_title="<b> Bar y axis <b>",
xaxis = dict(
showgrid=True,
ticks="",
showline = False,
gridcolor = 'white'
)
)
# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
line_y = [3,2,1, 1,2,3],
line_x = [1,2,3,1,2,3],
line_group = ['line1','line1','line1','line2','line2','line2']
))
# Make an ugly line graph
fig2 = px.line(
df2,
x= 'line_x',
y= 'line_y',
color = 'line_group'
)
# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
shapes=[dict(
type= 'line',
fillcolor = 'black',
line_width=2,
yref= 'y', y0= 0, y1= 0,
xref= 'x', x0= 1, x1= 3,
)],
xaxis_title="<b> Line graph title <b>",
yaxis_title="<b> Line x axis <b>",
legend_title="<b> Line y axis <b>",
template='simple_white',
hoverlabel=dict(bgcolor="white")
)
# Create a dropdown menu. Below is close to what I'd like to do, but the data statements are not working correctly and the shape in fig2 is causing problems...
fig3 = go.Figure()
fig3.update_layout(
updatemenus=[
dict(
active=0,
buttons=list([
dict(label="Bar Graph",
method="update",
args=[fig1.to_dict()['data'],
fig1.to_dict()['layout']]
),
dict(label="Line Graph",
method="update",
args=[fig2.to_dict()['data'],
fig2.to_dict()['layout']]
),
]))
]
)
It appears that I am almost able to correctly update the layout of each dropdown constituent plot based on the layout of each original graph. However, is it possible to update the data via this sort of method
as well?
I may be missing the point completely here. And it may also be overkill to unleash a Dash app in this case. But I would like to show you how the following setup can enable you to return completely different figure objects using a dcc.Dropdown(). The code snippet below will produce the following app:
If you now select fig2, you'll get this:
We can talk more about the details if this is something you can use. Also, the design with the very wide dropdown button is admittedly not the prettiest one, but I assume that design isn't the primary objective here.
Complete 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
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
bar_y = ['Bar1', 'Bar2'],
bar_x = [2,3],
bar_z = [1,2]
))
# Make bar graph
fig1 = px.bar(df1,
x="bar_x",
y='bar_y',
color='bar_z',
orientation='h',
)
# Add layout attributes
fig1.update_layout(
xaxis_title="<b> Bar graph title <b>",
yaxis_title="<b> Bar x axis <b>",
legend_title="<b> Bar y axis <b>",
xaxis = dict(
showgrid=True,
ticks="",
showline = False,
gridcolor = 'white'
)
)
# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
line_y = [3,2,1, 1,2,3],
line_x = [1,2,3,1,2,3],
line_group = ['line1','line1','line1','line2','line2','line2']
))
# Make an ugly line graph
fig2 = px.line(
df2,
x= 'line_x',
y= 'line_y',
color = 'line_group'
)
# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
shapes=[dict(
type= 'line',
fillcolor = 'black',
line_width=2,
yref= 'y', y0= 0, y1= 0,
xref= 'x', x0= 1, x1= 3,
)],
xaxis_title="<b> Line graph title <b>",
yaxis_title="<b> Line x axis <b>",
legend_title="<b> Line y axis <b>",
template='simple_white',
hoverlabel=dict(bgcolor="white")
)
# app = JupyterDash(__name__)
app = dash.Dash()
figs = ['fig1', 'fig2']
app.layout = html.Div([
html.Div([
dcc.Graph(id='plot'),
html.Div([
dcc.Dropdown(
id='variables',
options=[{'label': i, 'value': i} for i in figs],
value=figs[0]
)
])
])
])
#app.callback(
Output('plot', 'figure'),
[Input('variables', 'value')])
def update_graph(fig_name):
if fig_name == 'fig1':
# fig=go.Figure(go.Scatter(x=[1,2,3], y = [3,2,1]))
return fig1
if fig_name == 'fig2':
# fig=go.Figure(go.Bar(x=[1,2,3], y = [3,2,1]))
return fig2
# app.run_server(mode='external', debug=True)
app.run_server(debug=True,
use_reloader=False # Turn off reloader if inside Jupyter
)

How to use dropdown for data selection in Python and Plotly

I have grouped data with more than 2 groups. I then plot it with a grouped barchart using plotly for a specific set of 2 groups. How can I create 2 dropdown menus that select which group to be plotted as trace1 and which group as trace2?
The example below uses hardcoded groups 1 for trace1 and group 2 for trace2. I would like to control these with the dropdown menus.
import pandas as pd
import plotly as py
import plotly.graph_objs as go
d = {'x': ['a','b','c','a','b','c','a','b','c'], 'y': [1,2,3,10,20,30,100,200,300], 'group': [1,1,1,2,2,2,3,3,3]}
df = pd.DataFrame(data=d)
trace1 = go.Bar(
x=df['x'],
y=df[df['group']==1].y,
name='trace1'
)
trace2 = go.Bar(
x=df['x'],
y=df[df['group']==2].y,
name='trace2'
)
data = [trace1, trace2]
layout = go.Layout(
barmode='group'
)
fig = go.Figure(data=data, layout=layout)
py.offline.plot(fig, filename='grouped-bar')
Plot:
The following suggestion should let you do exactly what you're looking for.
Just select the source of the traces using the two dropdown menus:
Plot 1 - Selection is group 1 vs group 1:
Plot 2 - Selection is group 2 vs group 3:
Code:
# Imports
import plotly.graph_objs as go
import pandas as pd
import numpy as np
# data
d = {'x': ['a','b','c','a','b','c','a','b','c'], 'y': [1,2,3,10,20,30,100,200,300], 'group': [1,1,1,2,2,2,3,3,3]}
df = pd.DataFrame(data=d)
# split df by groups and organize them in a dict
groups = df['group'].unique().tolist()
dfs={}
for g in groups:
dfs[str(g)]=df[df['group']==g]
# get column names from first dataframe in the dict
#colNames = list(dfs[list(dfs.keys())[0]].columns)
#colNames=colNames[:2]
# one trace for each column per dataframe
fig=go.Figure()
# set up the first trace
fig.add_trace(go.Bar(x=dfs['1']['x'],
y=dfs['1']['y'],
visible=True)
)
# set up the second trace
fig.add_trace(go.Bar(x=dfs['1']['x'],
y=dfs['1']['y'],)
)
#f=fig.to_dict()
# plotly start
# buttons for menu 1, names
updatemenu=[]
buttons=[]
# button with one option for each dataframe
for df in dfs.keys():
#print(b, df)
buttons.append(dict(method='restyle',
label=df,
visible=True,
args=[{'y':[dfs[str(df)]['y'].values],
'type':'bar'}, [0]],
)
)
# another button with one option for each dataframe
buttons2=[]
for df in dfs.keys():
buttons2.append(dict(method='restyle',
label=df,
visible=True,
args=[{'y':[dfs[str(df)]['y'].values],
'type':'bar'}, [1]],
)
)
# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
your_menu2=dict()
updatemenu.append(your_menu2)
#updatemenu[1]
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
updatemenu[1]['buttons']=buttons2
updatemenu[1]['y']=0.5
# add dropdown menus to the figure
fig.update_layout(showlegend=False, updatemenus=updatemenu)
# add notations to the dropdown menus
fig.update_layout(
annotations=[
go.layout.Annotation(text="<b>group/<br>trace:</b>",
x=-0.15, xref="paper",
y=1.15, yref="paper",
align="left", showarrow=False),
go.layout.Annotation(text="<b>group/<br>trace:</b>",
x=-0.15, xref="paper", y=0.6,
yref="paper", showarrow=False),
]
)
fig.show()

Categories