Plotly (Python) 子图:填充构面和共享图例
问题描述
我正在尝试创建 2 个具有共享 x 轴的图,但遇到了 2 个问题:
- 只要我使用
yaxis
和yaxis2
标题和/或刻度线自定义布局,y 轴就会开始重叠 - 我希望在 2 个地块之间共享图例,但它们是重复的
这是重现我遇到的问题的代码:
from plotly.offline import init_notebook_mode, iplotinit_notebook_mode(connected=True) # 使用 jupyter导入 plotly.graph_objs从情节导入工具将 numpy 导入为 npN = 100epoch_range = [i for i in range(N)]模型性能 = {}对于 ['acc','loss'] 中的 m:对于['train','validation']中的子:如果子 == '火车':历史目标 = 米别的:history_target = 'val_{}'.format(m)model_perf[history_target] = np.random.random(N)line_type = {'火车':字典(颜色='灰色',宽度=1,破折号='破折号'),验证":字典(颜色='蓝色',宽度=4)}fig = tools.make_subplots(rows=2, cols=1, shared_xaxes=True, shared_yaxes=False, specs = [[{'b':10000}], [{'b':10000}]])我 = 0对于 ['acc','loss'] 中的 m:我 += 1对于['train','validation']中的子:如果子 == '火车':历史目标 = 米别的:history_target = 'val_{}'.format(m)fig.append_trace({'x':epoch_range,'y': model_perf[history_target],#'类型':'分散',名称":子,'传奇组':米,'yaxis': dict(title=m),'line': line_type[sub],'showlegend':真}, 我, 1)fig['布局'].update(高度=600,宽度=800,xaxis = dict(title = 'Epoch'),yaxis = dict(title='Accuracy', tickformat=".0%"),yaxis2 = dict(title='Loss', tickformat=".0%"),标题='性能')iplot(无花果)
这是我得到的图像:
如果您对如何解决这两个问题有任何建议,我很乐意听取您的意见.
提前致谢!
按照 Farbice 的建议,我查看了 plotly.figure_factory
中的 create_facet_grid
函数(顺便说一下,它需要 plotly 2.0.12+),我确实设法重现了相同的图像,线条更少,但它给了我更少的灵活性——例如,我认为你不能使用这个函数来绘制线条,它也有图例重复问题,但如果你正在寻找一个临时的可视化,这可能是相当有效.它需要长格式的数据,见下面的例子:
# 转换成长格式将熊猫导入为 pdperf_df = (pd.DataFrame({'accuracy_train': model_perf['acc'],'accuracy_validation': model_perf['val_acc'],'loss_train': model_perf['loss'],'loss_validation':model_perf['val_loss']}).堆().reset_index().重命名(列={'level_0':'时代','level_1': '变量',0:价值"}))perf_df = pd.concat([perf_df,perf_df['变量'].str.extractall(r'(?P<metric>^.*)_(?P<set>.*$)').reset_index()[['metric','set']]],轴=1).drop(['变量'], 轴=1)perf_df.head() # 结果时代价值度量集0 0.434349 精度训练0 0.374607 准确性验证0 0.864698 损失火车0 0.007445 损失验证1 0.553727 精度训练# 绘制它无花果 = ff.create_facet_grid(perf_df,x ='时代',y ='值',facet_row='公制',color_name='设置',尺度='free_y',ggplot2=真)fig['布局'].update(高度=800,宽度=1000,yaxis1 = dict(tickformat=".0%"),yaxis2 = dict(tickformat=".0%"),标题='性能')iplot(无花果)
结果如下:
解决方案在进行了更多挖掘之后,我找到了解决这两个问题的方法.
首先,y轴重叠问题是由于布局更新中的yaxis
参数引起的,必须将其更改为yaxis1
.
图例中重复的第二个问题有点棘手,但是
I'm trying to create 2 plots with a shared x-axis and I'm having 2 problems with this:
- As soon as I customise the layout with
yaxis
andyaxis2
titles and/or tickmarks, y-axes begin to overlap - I would like the legends to be shared between the 2 plots, but instead they are duplicated
Here is the code to reproduce the problem I'm experiencing:
from plotly.offline import init_notebook_mode, iplot
init_notebook_mode(connected=True) # using jupyter
import plotly.graph_objs as go
from plotly import tools
import numpy as np
N = 100
epoch_range = [i for i in range(N)]
model_perf = {}
for m in ['acc','loss']:
for sub in ['train','validation']:
if sub == 'train':
history_target = m
else:
history_target = 'val_{}'.format(m)
model_perf[history_target] = np.random.random(N)
line_type = {
'train': dict(
color='grey',
width=1,
dash='dash'
),
'validation': dict(
color='blue',
width=4
)
}
fig = tools.make_subplots(rows=2, cols=1, shared_xaxes=True, shared_yaxes=False, specs = [[{'b':10000}], [{'b':10000}]])
i = 0
for m in ['acc','loss']:
i += 1
for sub in ['train','validation']:
if sub == 'train':
history_target = m
else:
history_target = 'val_{}'.format(m)
fig.append_trace({
'x': epoch_range,
'y': model_perf[history_target],
#'type': 'scatter',
'name': sub,
'legendgroup': m,
'yaxis': dict(title=m),
'line': line_type[sub],
'showlegend': True
}, i, 1)
fig['layout'].update(
height=600,
width=800,
xaxis = dict(title = 'Epoch'),
yaxis = dict(title='Accuracy', tickformat=".0%"),
yaxis2 = dict(title='Loss', tickformat=".0%"),
title='Performance'
)
iplot(fig)
And here is the image that I get:
If you have any suggestions on how to solve these 2 problems, I'd love to hear from you.
Manny thanks in advance!
EDIT:
Following Farbice's advice, I looked into the create_facet_grid
function from plotly.figure_factory
(which by the way requires plotly 2.0.12+), I did manage to reproduce the same image with fewer lines but it gave me less flexibility -- for example I don't think you can plot lines using this function and it also has the legend duplication issue, but if you are looking for an ad hoc viz, this might be quite effective. It requires data in a long format, see the below example:
# converting into the long format
import pandas as pd
perf_df = (
pd.DataFrame({
'accuracy_train': model_perf['acc'],
'accuracy_validation': model_perf['val_acc'],
'loss_train': model_perf['loss'],
'loss_validation': model_perf['val_loss']
})
.stack()
.reset_index()
.rename(columns={
'level_0': 'epoch',
'level_1': 'variable',
0: 'value'
})
)
perf_df = pd.concat(
[
perf_df,
perf_df['variable']
.str
.extractall(r'(?P<metric>^.*)_(?P<set>.*$)')
.reset_index()[['metric','set']]
], axis=1
).drop(['variable'], axis=1)
perf_df.head() # result
epoch value metric set
0 0.434349 accuracy train
0 0.374607 accuracy validation
0 0.864698 loss train
0 0.007445 loss validation
1 0.553727 accuracy train
# plot it
fig = ff.create_facet_grid(
perf_df,
x='epoch',
y='value',
facet_row='metric',
color_name='set',
scales='free_y',
ggplot2=True
)
fig['layout'].update(
height=800,
width=1000,
yaxis1 = dict(tickformat=".0%"),
yaxis2 = dict(tickformat=".0%"),
title='Performance'
)
iplot(fig)
And here is the result:
解决方案After doing a little more digging I've found the solution to both my problems.
First, the overlapping y-axis problem was caused by yaxis
argument in the layout update, it had to be changed to yaxis1
.
The second problem with duplications in the legend was a little trickier, but this post helped me work it out. The idea is that each trace can have a legend associated with it, so if you are plotting multiple traces, you may only want to use the legend from one of them (using the showlegend
argument), but to make sure that one legend controls the toggle of multiple subplots, you can use the legendgroup
parameter.
Here is the full code with the solution:
from plotly.offline import init_notebook_mode, iplot
init_notebook_mode(connected=True) # using jupyter
import plotly.graph_objs as go
from plotly import tools
import numpy as np
N = 100
epoch_range = [i for i in range(N)]
model_perf = {}
for m in ['acc','loss']:
for sub in ['train','validation']:
if sub == 'train':
history_target = m
else:
history_target = 'val_{}'.format(m)
model_perf[history_target] = np.random.random(N)
line_type = {
'train': dict(
color='grey',
width=1,
dash='dash'
),
'validation': dict(
color='blue',
width=4
)
}
fig = tools.make_subplots(
rows=2,
cols=1,
shared_xaxes=True,
shared_yaxes=False
)
i = 0
for m in ['acc','loss']:
i += 1
if m == 'acc':
legend_display = True
else:
legend_display = False
for sub in ['train','validation']:
if sub == 'train':
history_target = m
else:
history_target = 'val_{}'.format(m)
fig.append_trace({
'x': epoch_range,
'y': model_perf[history_target],
'name': sub,
'legendgroup': sub, # toggle train / test group on all subplots
'yaxis': dict(title=m),
'line': line_type[sub],
'showlegend': legend_display # this is now dependent on the trace
}, i, 1)
fig['layout'].update(
height=600,
width=800,
xaxis = dict(title = 'Epoch'),
yaxis1 = dict(title='Accuracy', tickformat=".0%"),
yaxis2 = dict(title='Loss', tickformat=".0%"),
title='Performance'
)
iplot(fig)
And here is the result:
相关文章