Plotly:如何设置分组子图?
Plotly: How to set up grouped subplots?
我正在尝试使用 Plotly 将分组箱线图绘制为包含来自数据框的数据的网格。例如,如果我们有一个数据框,
import plotly.express as px
df = px.data.tips()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
我可以如下绘制单个图(例如:day = Friday):
import plotly.graph_objects as go
import plotly.express as px
df_plot=df[df['day']== 'Fri']
fig = px.box(df_plot, x='time', y="total_bill", color="sex",width=600, height=400)
fig.show()
如果我想绘制多个子图,例如,每天一个,我不确定如何在 plotly 中进行绘制。这是我试过的:
fig = make_subplots(rows=2, cols=2)
for v in range(4):
for i, met in enumerate(df['day'].unique()): #'Sun', 'Sat', 'Thur', 'Fri'
df_plot=df[df['day']== met]
for t in px.box(df_plot, x="time", y=f"total_bill", color='sex').data:
fig.add_trace(t, row=(v//2)+1, col=(v%2)+1)
fig.update_layout(
boxmode="group", margin={"l": 0, "r": 0, "t": 20, "b": 0}
).update_traces(showlegend=True)
这给出了一个看起来很奇怪的情节。我究竟做错了什么?另外,在带有 plotly 的子图网格中,如何绘制一个图例而不是每个图的图例。
- 使用 plotly express 的最简单方法是使用 facet 参数 px box
- 复制您的示例,2 列和 2 行
import plotly.express as px
df = px.data.tips()
df.loc[df["day"].eq("Sun")]
px.box(df.loc[df["day"].eq("Sun")], x='time', y="total_bill", color="sex",width=600, height=400).show()
px.box(df, x='time', y="total_bill", color="sex", facet_col="day", facet_col_wrap=2, width=600, height=400).show()
您已经收到了似乎产生了预期结果的答案。但是由于您似乎很想知道如何使用指定的设置来执行此操作,我想向您展示如何也可以这样做。而且我还确保下面的完整代码段也处理了图例。
情节
您的原始设置问题似乎源于 fig.add_trace(t, row=(v//2)+1, col=(v%2)+1)
。你的情节变得奇怪的原因是 for t in px.box(df_plot, x="time", y=f"total_bill", color='sex').data:
产生的痕迹没有正确地索引到子情节。 (v//2)+1
看起来不错,但是 col=(v%2)+1)
有点问题。您在这里需要的是一个在 1 and 2
之间变化的索引。在下面的代码片段中,您可以看到我已经使用以下方法解决了这个问题:
from itertools import cycle
cols = cycle([1,2])
c = next(cols)
这可能不是最有效的方法,但我一直使用它来应对其他挑战,在这种情况下它也很管用。如果您打印出 c
,您会看到您得到:[1, 1, 2, 2, 1, 1, 2, 2]
。这正是您需要确保第一个子图的前两条轨迹在子图 1
中的 row = 1
和 col = 1
处结束,依此类推。
关于您关于传说的次要问题,这是通过以下方式处理的:
fig.add_trace(t.update(showlegend=True if i == 0 else False), row=r, col=c)
完整片段
import plotly.express as px
from plotly.subplots import make_subplots
from itertools import cycle
df = px.data.tips()
fig = make_subplots(rows=2, cols=2, subplot_titles = df['day'].unique())
cols = cycle([1,2])
for i, met in enumerate(df['day'].unique()): #'Sun', 'Sat', 'Thur', 'Fri'
df_plot=df[df['day']== met].sort_values(['time', 'sex'], ascending = False)
c = next(cols)
for t in px.box(df_plot, x="time", y="total_bill", color='sex').data:
r = (i//2)+1
# print(c)
fig.add_trace(t.update(showlegend=True if i == 0 else False), row=r, col=c)
fig.update_layout(
boxmode="group", margin={"l": 0, "r": 0, "t": 20, "b": 0}
)#.update_traces(showlegend=True)
fig.show()
编辑:
如果您想在此设置中添加任何内容,例如 y-axis 标题,只需添加:
titles = cycle(df['day'].unique())
fig.for_each_yaxis(lambda y: y.update(title = next(titles)))
或者如果我误解了你而你只是想将 total_bill
添加到 yaxes 中,只需使用:
fig.update_yaxes(title = 'total_bill')
我正在尝试使用 Plotly 将分组箱线图绘制为包含来自数据框的数据的网格。例如,如果我们有一个数据框,
import plotly.express as px
df = px.data.tips()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
我可以如下绘制单个图(例如:day = Friday):
import plotly.graph_objects as go
import plotly.express as px
df_plot=df[df['day']== 'Fri']
fig = px.box(df_plot, x='time', y="total_bill", color="sex",width=600, height=400)
fig.show()
如果我想绘制多个子图,例如,每天一个,我不确定如何在 plotly 中进行绘制。这是我试过的:
fig = make_subplots(rows=2, cols=2)
for v in range(4):
for i, met in enumerate(df['day'].unique()): #'Sun', 'Sat', 'Thur', 'Fri'
df_plot=df[df['day']== met]
for t in px.box(df_plot, x="time", y=f"total_bill", color='sex').data:
fig.add_trace(t, row=(v//2)+1, col=(v%2)+1)
fig.update_layout(
boxmode="group", margin={"l": 0, "r": 0, "t": 20, "b": 0}
).update_traces(showlegend=True)
这给出了一个看起来很奇怪的情节。我究竟做错了什么?另外,在带有 plotly 的子图网格中,如何绘制一个图例而不是每个图的图例。
- 使用 plotly express 的最简单方法是使用 facet 参数 px box
- 复制您的示例,2 列和 2 行
import plotly.express as px
df = px.data.tips()
df.loc[df["day"].eq("Sun")]
px.box(df.loc[df["day"].eq("Sun")], x='time', y="total_bill", color="sex",width=600, height=400).show()
px.box(df, x='time', y="total_bill", color="sex", facet_col="day", facet_col_wrap=2, width=600, height=400).show()
您已经收到了似乎产生了预期结果的答案。但是由于您似乎很想知道如何使用指定的设置来执行此操作,我想向您展示如何也可以这样做。而且我还确保下面的完整代码段也处理了图例。
情节
您的原始设置问题似乎源于 fig.add_trace(t, row=(v//2)+1, col=(v%2)+1)
。你的情节变得奇怪的原因是 for t in px.box(df_plot, x="time", y=f"total_bill", color='sex').data:
产生的痕迹没有正确地索引到子情节。 (v//2)+1
看起来不错,但是 col=(v%2)+1)
有点问题。您在这里需要的是一个在 1 and 2
之间变化的索引。在下面的代码片段中,您可以看到我已经使用以下方法解决了这个问题:
from itertools import cycle
cols = cycle([1,2])
c = next(cols)
这可能不是最有效的方法,但我一直使用它来应对其他挑战,在这种情况下它也很管用。如果您打印出 c
,您会看到您得到:[1, 1, 2, 2, 1, 1, 2, 2]
。这正是您需要确保第一个子图的前两条轨迹在子图 1
中的 row = 1
和 col = 1
处结束,依此类推。
关于您关于传说的次要问题,这是通过以下方式处理的:
fig.add_trace(t.update(showlegend=True if i == 0 else False), row=r, col=c)
完整片段
import plotly.express as px
from plotly.subplots import make_subplots
from itertools import cycle
df = px.data.tips()
fig = make_subplots(rows=2, cols=2, subplot_titles = df['day'].unique())
cols = cycle([1,2])
for i, met in enumerate(df['day'].unique()): #'Sun', 'Sat', 'Thur', 'Fri'
df_plot=df[df['day']== met].sort_values(['time', 'sex'], ascending = False)
c = next(cols)
for t in px.box(df_plot, x="time", y="total_bill", color='sex').data:
r = (i//2)+1
# print(c)
fig.add_trace(t.update(showlegend=True if i == 0 else False), row=r, col=c)
fig.update_layout(
boxmode="group", margin={"l": 0, "r": 0, "t": 20, "b": 0}
)#.update_traces(showlegend=True)
fig.show()
编辑:
如果您想在此设置中添加任何内容,例如 y-axis 标题,只需添加:
titles = cycle(df['day'].unique())
fig.for_each_yaxis(lambda y: y.update(title = next(titles)))
或者如果我误解了你而你只是想将 total_bill
添加到 yaxes 中,只需使用:
fig.update_yaxes(title = 'total_bill')