Plotly subplots with nested shared xaxis - python

I want to generate a plotly plot with two subplots with a shared nested xaxis. Unfortunately I have the problem that
either the separators for the nested xaxis are also shown in the first plot
or the vertical grid lines of the two subplots don't match.
How can I either remove the separators in case 1 or match the grids between the subplots in case 2?
Case 1: Some weird long group separators in the row=1, col=1 subplot
Case 2: The vertical grid lines of both plots don't match
The only difference between both plots is that I commented out the x=xlabels, line for df_one in the following MWE:
# MWE
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# generate data
idx_one = ["A", "B"]
idx_two = ["long text A", "long text B"]
df_one = pd.DataFrame(
data=np.random.rand(4,1),
columns=["df_one"],
index=pd.MultiIndex.from_product([idx_one, idx_two]),
)
df_two = pd.DataFrame(
data=np.random.rand(4,1),
columns=["df_two"],
index=pd.MultiIndex.from_product([idx_one, idx_two]),
)
xlabels = [
df_one.index.get_level_values(0).tolist(),
df_one.index.get_level_values(1).tolist(),
]
# plotly
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, y_title="some shared y-title")
fig.append_trace(
go.Bar(
y=df_one["df_one"],
x=xlabels,
name="df_one",
showlegend=False,
text=df_one["df_one"],
textposition='auto',
textangle=0,
texttemplate='%{text:+.1f}',
),
row=1, col=1,
)
fig.append_trace(
go.Bar(
y=df_two["df_two"],
x=xlabels,
name="df_two",
showlegend=False,
text=df_two["df_two"],
textposition='auto',
textangle=0,
texttemplate='%{text:+.1f}',
),
row=2, col=1,
)
fig.show()

Okay, I found a bit unintuitive answer by setting the x values in the first bar plot and offsetting the bars with the offset option and later changing the range with update_xaxes.
Result:
Solution:
# MWE
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# generate data
idx_one = ["A", "B"]
idx_two = ["long text A", "long text B"]
df_one = pd.DataFrame(
data=np.random.rand(4,1),
columns=["df_one"],
index=pd.MultiIndex.from_product([idx_one, idx_two]),
)
df_two = pd.DataFrame(
data=np.random.rand(4,1),
columns=["df_two"],
index=pd.MultiIndex.from_product([idx_one, idx_two]),
)
xlabels = [
df_one.index.get_level_values(0).tolist(),
df_one.index.get_level_values(1).tolist(),
]
# plotly
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, y_title="some shared y-title")
fig.append_trace(
go.Bar(
y=df_one["df_one"],
x=[*range(len(df_one["df_one"]))],
name="df_one",
showlegend=False,
text=df_one["df_one"],
textposition='auto',
textangle=0,
texttemplate='%{text:+.1f}',
offset=-0.9,
),
row=1, col=1,
)
fig.append_trace(
go.Bar(
y=df_two["df_two"],
x=xlabels,
name="df_two",
showlegend=False,
text=df_two["df_two"],
textposition='auto',
textangle=0,
texttemplate='%{text:+.1f}',
),
row=2, col=1,
)
fig.update_xaxes(
row=1, col=1,
tickmode = "array",
tickvals = [*range(len(df_one["df_one"]))],
range=[
-1,
len(df_one["df_one"])-1,
]
)
fig.show()

Related

plotly.express.timeline in subplots

Using Timelines with plotly.express, I can get a working Gantt Chart:
import plotly.express as px
import pandas as pd
df = pd.DataFrame([
dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')
])
fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
fig.update_yaxes(autorange="reversed") # otherwise tasks are listed from the bottom up
fig.show()
Following advice from here, I try to add it to a subplot with shared_xaxes = True
from plotly.subplots import make_subplots
fig_sub = make_subplots(rows=2, shared_xaxes=True)
fig_sub.append_trace(fig['data'][0], row=1, col=1)
fig_sub.append_trace(fig['data'][0], row=2, col=1)
fig_sub.show()
But it treats it like a graph_objects and doesn't display a Gantt chart.
Does anyone have any workarounds or suggestionts?
It is unclear why this is the case, but it appears that the date has been de-formatted, so once again, setting the date in the x-axis format will restore the timeline.
from plotly.subplots import make_subplots
fig_sub = make_subplots(rows=2, shared_xaxes=True)
fig_sub.append_trace(fig['data'][0], row=1, col=1)
fig_sub.append_trace(fig['data'][0], row=2, col=1)
fig_sub.update_xaxes(type='date')
fig_sub.show()

Python Plotly adding px objects to a subplot object

So I'm trying to combine two plots into one. I've made these plots with the plotly.express library rather than the plotly.graphs_objs.
Now, plotly suggests using: fig = make_subplots(rows=3, cols=1) and then append_trace or add_trace
However, this doesn't work for express objects since the append trace expects a single. trace. How can I add a express figure to a subplot? Or is this simply not possible. One option I've tried was fig.data[0] but this will only add the first line/data entry. Rn my code looks like:
double_plot = make_subplots(rows=2, cols=1, shared_xaxes=True)
histo_phases = phases_distribution(match_file_, range)
fig = px.line(match_file,
x="Minutes", y=["Communicatie", 'Gemiddelde'], color='OPPONENT')
fig.update_layout(
xaxis_title="Minuten",
yaxis_title="Communicatie per " + str(range) + "minuten",
legend_title='Tegenstander',
)
double_plot.append_trace(fig.data, row=1, col=1)
double_plot.append_trace(histo_phases.data, row=2, col=1)
Thanks in advance.
your code sample does not include creation of data frames and figures. Have simulated
it is as simple as adding each traces from figures created with plotly express to figure created with make_subplots()
for t in fig.data:
double_plot.append_trace(t, row=1, col=1)
for t in histo_phases.data:
double_plot.append_trace(t, row=2, col=1)
full code
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
df = px.data.tips()
double_plot = make_subplots(rows=2, cols=1, shared_xaxes=True)
# histo_phases = phases_distribution(match_file_, range)
histo_phases = px.histogram(df, x="total_bill")
match_file = pd.DataFrame(
{
"Minutes": np.repeat(range(60), 10),
"Communicatie": np.random.uniform(1, 3, 600),
"Gemiddelde": np.random.uniform(3, 5, 600),
"OPPONENT": np.tile(list("ABCDEF"), 100),
}
)
fig = px.line(match_file, x="Minutes", y=["Communicatie", "Gemiddelde"], color="OPPONENT")
fig.update_layout(
xaxis_title="Minuten",
yaxis_title="Communicatie per " + str(range) + "minuten",
legend_title="Tegenstander",
)
for t in fig.data:
double_plot.append_trace(t, row=1, col=1)
for t in histo_phases.data:
double_plot.append_trace(t, row=2, col=1)
double_plot

Can you alter a subplot title location in plotly?

When adding a subplot_title to my subplots in plotly my title overlaps with my axes. Can I alter the location of the subplot title like matplotlib's ax.set_title('title', y=1.5)?
Here is my plot, as you can see danceability overlaps:
Here is my code so far:
from plotly.subplots import make_subplots
categories = ['key', 'acousticness', 'danceability', 'energy', 'loudness',
'speechiness', 'tempo','key']
fig = make_subplots(rows=1, cols=2, specs=[[{"type":"polar"}, {"type":"polar"}]],
subplot_titles=('Clustering Into 8 Playlists', 'Clustering Into 11 Playlists'))
fig.add_trace(go.Scatterpolar(
r=x,
theta=categories,
fill='toself',
name='Cluster 1',
visible='legendonly'
), row=1, col=1)
fig.add_trace(go.Scatterpolar(
r=y,
theta=categories,
fill='toself',
name='Cluster 2',
visible='legendonly'
), row=1, col=2)
fig.update_layout(height=600, width=1400, title_text='Radar Plot of All Clusters (Fig.4)')
fig.show()
I gave a partial code answer, but you can do it with fig['layout']['annotations']. From the official reference here.I also referred to SO's answer.
fig.update_layout(title_text='Radar Plot of All Clusters (Fig.4)') # height=600, width=1400,
for annotation in fig['layout']['annotations']:
annotation['yanchor']='bottom'
annotation['y']=1.1
annotation['yref']='paper'
fig.show()

Plotly:How to create subplots with python?

I am wondering what is best practice to create subplots using Python Plotly. Is it to use plotly.express or the standard plotly.graph_objects?
I'm trying to create a figure with two subplots, which are stacked bar charts. The following code doesn't work. I didn't find anything useful in the official documentation. The classic Titanic dataset was imported as train_df here.
import plotly.express as px
train_df['Survived'] = train_df['Survived'].astype('category')
fig1 = px.bar(train_df, x="Pclass", y="Age", color='Survived')
fig2 = px.bar(train_df, x="Sex", y="Age", color='Survived')
trace1 = fig1['data'][0]
trace2 = fig2['data'][0]
fig = make_subplots(rows=1, cols=2, shared_xaxes=False)
fig.add_trace(trace1, row=1, col=1)
fig.add_trace(trace2, row=1, col=2)
fig.show()
I got the following figure:
What I expect is as follows:
I'm hoping that the existing answer suits your needs, but I'd just like to note that the statement
it's not possible to subplot stakedbar (because stacked bar are in facted figures and not traces
is not entirely correct. It's possible to build a plotly subplot figure using stacked bar charts as long as you put it together correctly using add_trace() and go.Bar(). And this also answers your question regarding:
I am wondering what is best practice to create subplots using Python Plotly. Is it to use plotly.express or the standard plotly.graph_objects?
Use plotly.express ff you find a px approach that suits your needs. And like in your case where you do not find it; build your own subplots using plotly.graphobjects.
Below is an example that will show you one such possible approach using the titanic dataset. Note that the column names are noe the same as yours since there are no capital letters. The essence of this approav is that you use go.Bar() for each trace, and specify where to put those traces using the row and col arguments in go.Bar(). If you assign multiple traces to the same row and col, you will get stacked bar chart subplots if you specify barmode='stack' in fig.update_layout(). Usingpx.colors.qualitative.Plotly[i]` will let you assign colors from the standard plotly color cycle sequentially.
Plot:
Code:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
url = "https://raw.github.com/mattdelhey/kaggle-titanic/master/Data/train.csv"
titanic = pd.read_csv(url)
#titanic.info()
train_df=titanic
train_df
# data for fig 1
df1=titanic.groupby(['sex', 'pclass'])['survived'].aggregate('mean').unstack()
# plotly setup for fig
fig = make_subplots(2,1)
fig.add_trace(go.Bar(x=df1.columns.astype('category'), y=df1.loc['female'],
name='female',
marker_color = px.colors.qualitative.Plotly[0]),
row=1, col=1)
fig.add_trace(go.Bar(x=df1.columns.astype('category'), y=df1.loc['male'],
name='male',
marker_color = px.colors.qualitative.Plotly[1]),
row=1, col=1)
# data for plot 2
age = pd.cut(titanic['age'], [0, 18, 80])
df2 = titanic.pivot_table('survived', [age], 'pclass')
groups=['(0, 18]', '(18, 80]']
fig.add_trace(go.Bar(x=df2.columns, y=df2.iloc[0],
name=groups[0],
marker_color = px.colors.qualitative.Plotly[3]),
row=2, col=1)
fig.add_trace(go.Bar(x=df2.columns, y=df2.iloc[1],
name=groups[1],
marker_color = px.colors.qualitative.Plotly[4]),
row=2, col=1)
fig.update_layout(title=dict(text='Titanic survivors by sex and age group'), barmode='stack', xaxis = dict(tickvals= df1.columns))
fig.show()
fig.show()
From what I know, it's not possible to subplot stakedbar (because stacked bar are in facted figures and not traces)...
On behalf of fig.show(), you can put to check if the html file is okay for you (The plots are unfortunately one under the other...) :
with open('p_graph.html', 'a') as f:
f.write(fig1.to_html(full_html=False, include_plotlyjs='cdn',default_height=500))
f.write(fig2.to_html(full_html=False, include_plotlyjs='cdn',default_height=500))
try the code below to check if the html file generate can be okay for you:
import pandas as pd
import plotly.graph_objects as go
#Remove the .astype('category') to easily
#train_df['Survived'] = train_df['Survived'].astype('category')
Pclass_pivot=pd.pivot_table(train_df,values='Age',index='Pclass',
columns='Survived',aggfunc=lambda x: len(x))
Sex_pivot=pd.pivot_table(train_df,values='Age',index='Sex',
columns='Survived',aggfunc=lambda x: len(x))
fig1 = go.Figure(data=[
go.Bar(name='Survived', x=Pclass_pivot.index.values, y=Pclass_pivot[1]),
go.Bar(name='NotSurvived', x=Pclass_pivot.index.values, y=Pclass_pivot[0])])
# Change the bar mode
fig1.update_layout(barmode='stack')
fig2 = go.Figure(data=[
go.Bar(name='Survived', x=Sex_pivot.index.values, y=Sex_pivot[1]),
go.Bar(name='NotSurvived', x=Sex_pivot.index.values, y=Sex_pivot[0])])
# Change the bar mode
fig2.update_layout(barmode='stack')
with open('p_graph.html', 'a') as f:
f.write(fig1.to_html(full_html=False, include_plotlyjs='cdn',default_height=500))
f.write(fig2.to_html(full_html=False, include_plotlyjs='cdn',default_height=500))
I managed to generate the subplots using the add_bar function.
Code:
from plotly.subplots import make_subplots
# plotly can only support one legend per graph at the moment.
fig = make_subplots(
rows=1, cols=2,
subplot_titles=("Pclass vs. Survived", "Sex vs. Survived")
)
fig.add_bar(
x=train_df[train_df.Survived == 0].Pclass.value_counts().index,
y=train_df[train_df.Survived == 0].Pclass.value_counts().values,
text=train_df[train_df.Survived == 0].Pclass.value_counts().values,
textposition='auto',
name='Survived = 0',
row=1, col=1
)
fig.add_bar(
x=train_df[train_df.Survived == 1].Pclass.value_counts().index,
y=train_df[train_df.Survived == 1].Pclass.value_counts().values,
text=train_df[train_df.Survived == 1].Pclass.value_counts().values,
textposition='auto',
name='Survived = 1',
row=1, col=1
)
fig.add_bar(
x=train_df[train_df.Survived == 0].Sex.value_counts().index,
y=train_df[train_df.Survived == 0].Sex.value_counts().values,
text=train_df[train_df.Survived == 0].Sex.value_counts().values,
textposition='auto',
marker_color='#636EFA',
showlegend=False,
row=1, col=2
)
fig.add_bar(
x=train_df[train_df.Survived == 1].Sex.value_counts().index,
y=train_df[train_df.Survived == 1].Sex.value_counts().values,
text=train_df[train_df.Survived == 1].Sex.value_counts().values,
textposition='auto',
marker_color='#EF553B',
showlegend=False,
row=1, col=2
)
fig.update_layout(
barmode='stack',
height=400, width=1200,
)
fig.update_xaxes(ticks="inside")
fig.update_yaxes(ticks="inside", col=1)
fig.show()
Resulting plot:
Hope this is helpful to the newbies of plotly like me.

Adding a secondary axis in Plotly Python

I'm working with a Dash graph object and I'm fairly new to it. I'm attempting to pass in a graph that has 2 scatter charts and a bar chart on the same figure but I'd like the bar chart (green) to be on it's own secondary y axis so it looks better than it does here:
Now from what I understand about Dash, I have to pass a go.Figure() object so I have a function which defines the data and the layout. I saw in the plotly documentation that you can use plotly express add secondary axis but I'm not sure how to do that within my frame work here. Any help would be greatly appreciated!
Here's my code:
def update_running_graph(n_intervals):
df = pd.read_csv(filename)
trace1 = go.Scatter(x=df['Timestamp'],
y=df['CLE'],
name='Crude',
mode='lines+markers')
trace2 = go.Scatter(x=df['Timestamp'],
y=df['y_pred'],
name='Model',
mode='lines+markers')
trace3 = go.Bar(x=df['Timestamp'],
y=df['ModelDiff'],
name='Diff',
)
data = [trace1, trace2,trace3]
layout = go.Layout(title='CLE vs Model')
return go.Figure(data=data, layout=layout)
To add a secondary y-axis in dash you could do the following:
def update_running_graph(n_intervals):
df = pd.read_csv(filename)
trace1 = go.Scatter(x=df['Timestamp'],
y=df['CLE'],
name='Crude',
mode='lines+markers',
yaxis='y1')
trace2 = go.Scatter(x=df['Timestamp'],
y=df['y_pred'],
name='Model',
mode='lines+markers',
yaxis='y1')
trace3 = go.Bar(x=df['Timestamp'],
y=df['ModelDiff'],
name='Diff',
yaxis='y2'
)
data = [trace1, trace2,trace3]
layout = go.Layout(title='CLE vs Model',
yaxis=dict(title='Crude and Model'),
yaxis2=dict(title='Moddel Difference',
overlaying='y',
side='right'))
return go.Figure(data=data, layout=layout)
you can add more y-axis they always need to have the form of yi with i the i-th axis. Then in the layout you can specify the layout of the i-th axis with yaxisi=dict(...).
This documentation page should be of use. Just modify to fit your code, since trace1 and trace2 appear to be on the same scale, just set trace3 to the secondary axis scale and you should be set. Below is an example with just only 2 but adding a third should not be too difficult.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
fig.add_trace(
go.Scatter(x=[1, 2, 3], y=[40, 50, 60], name="yaxis data"),
secondary_y=False,
)
fig.add_trace(
go.Scatter(x=[2, 3, 4], y=[4, 5, 6], name="yaxis2 data"),
secondary_y=True,
)
# Add figure title
fig.update_layout(
title_text="Double Y Axis Example"
)
# Set x-axis title
fig.update_xaxes(title_text="xaxis title")
# Set y-axes titles
fig.update_yaxes(title_text="<b>primary</b> yaxis title", secondary_y=False)
fig.update_yaxes(title_text="<b>secondary</b> yaxis title", secondary_y=True)
fig.show()
Cheers!

Categories

Resources