我想知道Plotly是否可以开始支持嵌套X/多个X轴? 例如,如果使用标准“提示”数据,添加在(五美分图、JMP 或 Origin )中实现的功能将是有益的。 即
import plotly.express as px
df = px.data.tips()
%load_ext autoreload
%autoreload 2
%matplotlib inline
import fivecentplots as fcp
import pandas as pd
import numpy as np
import os, sys, pdb
osjoin = os.path.join
db = pdb.set_trace
fcp.boxplot(df=df, y=‘tip’, groups=[‘time’, ‘sex’, ‘day’], legend=‘smoker’)
会生成: 嵌套 X 轴条形图
如果此功能已经存在 - 请添加评论。
这是可行的,但比您当前使用的方法需要更多步骤。使用plotly 实现这一目标的一种解决方案是创建具有多维轴的子图,一个用于午餐,一个用于晚餐,两者之间的空间为零,因此它看起来像一个单独的图。
import plotly.express as px
import pandas as pd
import numpy as np
from pandas.api.types import CategoricalDtype
df = px.data.tips()
#set order for days
days = CategoricalDtype(
['Thur', 'Fri', 'Sat', 'Sun'],
ordered=True
)
df['day'] = df['day'].astype(days)
#sort df
df.sort_values(['time', 'sex', 'day'], inplace=True)
#create framework
fig = make_subplots(rows=1,
cols=2,
shared_yaxes=True,
horizontal_spacing=0,
column_widths=[7/11, 4/11])
#create "Dinner" boxplots
fig.add_trace(go.Box(x=[df['sex'][df['time']=='Dinner'].tolist(), df['day'][df['time']=='Dinner'].tolist()],
y=df['tip'][df['time']=='Dinner'],
boxpoints=False,
pointpos=0,
line=dict(color='gray',
width=1),
fillcolor='white',
showlegend=False),
row=1,
col=1)
#add "Dinner" smokers
fig.add_trace(go.Scatter(x=[df['sex'][(df['time']=='Dinner') & (df['smoker']=='Yes')].tolist(), df['day'][(df['time']=='Dinner') & (df['smoker']=='Yes')].tolist()],
y=df['tip'][(df['time']=='Dinner') & (df['smoker']=='Yes')],
mode='markers',
marker=dict(color='red',
symbol='circle-open',
size=10),
name='Yes'
),
row=1,
col=1)
#add "Dinner" non-smokers
fig.add_trace(go.Scatter(x=[df['sex'][(df['time']=='Dinner') & (df['smoker']=='No')].tolist(), df['day'][(df['time']=='Dinner') & (df['smoker']=='No')].tolist()],
y=df['tip'][(df['time']=='Dinner') & (df['smoker']=='No')],
mode='markers',
marker=dict(color='green',
symbol='cross-thin-open',
size=10),
name='No'
),
row=1,
col=1)
df_mean = df[['sex', 'day', 'tip']][df['time']=='Dinner'].groupby(['sex', 'day']).mean().reset_index().dropna()
#add "Dinner" mean line
fig.add_trace(go.Scatter(x=[df_mean['sex'].tolist(), df_mean['day'].tolist()],
y=df_mean['tip'].tolist(),
showlegend=False,
marker=dict(color='black')
),
row=1,
col=1)
#create "Lunch" boxplots
fig.add_trace(go.Box(x=[df['sex'][df['time']=='Lunch'].tolist(), df['day'][df['time']=='Lunch'].tolist()],
y=df['tip'][df['time']=='Lunch'],
boxpoints=False,
pointpos=0,
line=dict(color='gray',
width=1),
fillcolor='white',
showlegend=False),
row=1,
col=2)
#add "Lunch" smokers
fig.add_trace(go.Scatter(x=[df['sex'][(df['time']=='Lunch') & (df['smoker']=='Yes')].tolist(), df['day'][(df['time']=='Lunch') & (df['smoker']=='Yes')].tolist()],
y=df['tip'][(df['time']=='Lunch') & (df['smoker']=='Yes')],
mode='markers',
marker=dict(color='red',
symbol='circle-open',
size=10),
showlegend=False
),
row=1,
col=2)
#add "Lunch" non-smokers
fig.add_trace(go.Scatter(x=[df['sex'][(df['time']=='Lunch') & (df['smoker']=='No')].tolist(), df['day'][(df['time']=='Lunch') & (df['smoker']=='No')].tolist()],
y=df['tip'][(df['time']=='Lunch') & (df['smoker']=='No')],
mode='markers',
marker=dict(color='green',
symbol='cross-thin-open',
size=10),
showlegend=False
),
row=1,
col=2)
df_mean = df[['sex', 'day', 'tip']][df['time']=='Lunch'].groupby(['sex', 'day']).mean().reset_index().dropna()
#add "Lunch" mean line
fig.add_trace(go.Scatter(x=[df_mean['sex'].tolist(), df_mean['day'].tolist()],
y=df_mean['tip'].tolist(),
showlegend=False,
marker=dict(color='black')
),
row=1,
col=2)
fig.update_xaxes(title='Dinner', col=1)
fig.update_xaxes(title='Lunch', col=2)
fig.update_yaxes(title='tip', col=1)
fig.update_layout(legend_title='Smoker')
fig.show()
使用循环使其更快以及其他细微更改
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = px.data.tips()
#set order for days
days = CategoricalDtype(['Thur', 'Fri', 'Sat', 'Sun'],
ordered=True)
df['day'] = df['day'].astype(days)
#sort df
df = df.sort_values(['time', 'sex', 'day']).reset_index(drop=True)
#create framework
fig = make_subplots(rows=1,
cols=2,
shared_yaxes=True,
horizontal_spacing=0,
column_widths=[4/11, 7/11])
col_order = {'Lunch':1, 'Dinner':2}
for t, col in col_order.items():
cond = df['time']==t
#add mean line
df_mean = df[cond].groupby(['sex', 'day'], observed=True)['tip'].mean().reset_index()
fig.add_trace(go.Scatter(x=[df_mean['sex'].tolist(), df_mean['day'].tolist()],
y=df_mean['tip'].tolist(),
showlegend=False,
marker=dict(color='black')
),
row=1,
col=col)
#add boxplots
fig.add_trace(go.Box(x=[df[cond]['sex'].tolist(), df[cond]['day'].tolist()],
y=df[cond]['tip'],
boxpoints=False,
pointpos=0,
line=dict(color='gray',
width=1),
fillcolor='white',
showlegend=False),
row=1,
col=col)
#add smokers and non-smokers
sl = True if col==1 else False #avoid duplicate legend entries
smoker_dict = {'Yes':['red', 'circle-open'], 'No':['green', 'cross-thin-open']}
for s, marker_vals in smoker_dict.items():
color = marker_vals[0]
symbol = marker_vals[1]
cond = (df['time']==t) & (df['smoker']==s)
fig.add_trace(go.Box(x=[df[cond]['sex'].tolist(), df[cond]['day'].tolist()],
y=df[cond]['tip'],
marker=dict(color=color,
symbol=symbol,
size=10),
fillcolor='rgba(255,255,255,0)',
line_color='rgba(255,255,255,0)',
boxpoints='all',
pointpos=0,
name=s,
showlegend=sl,
),
row=1,
col=col)
fig.update_xaxes(title=t, col=col)
fig.update_yaxes(title='tip', col=1)
fig.update_xaxes(showline=True,
linecolor='black',
linewidth=1,
mirror=True)
fig.update_yaxes(showline=True,
linecolor='black',
linewidth=1,
mirror=True)
fig.update_yaxes(mirror=False, col=1) #center line was too thick
fig.update_traces(selector=dict(type='box'), jitter=1) #optional jitter
fig.update_layout(legend_title='Smoker',
plot_bgcolor='whitesmoke',
height=800, width=800)
fig.show()