可视化神器Plotly玩转多子图绘制

公众号:尤而小屋
作者:Peter
编辑:Peter

可视化神器Plotly玩转多子图绘制

大家好,我是Peter~

很长时间没有Plotly绘图的文章,之前已经介绍如何绘制柱状图、饼图、小提琴图、桑基图等,今天给大家带来的是一篇关于Plotly如何绘制多子图的文章,在一个画布中如何实现多种类型图形的绘制。

Plotly连载文章

多子图绘制

首先看看实际的效果:

Plotly中有两种方式来绘制子图,基于plotly_express和 graph_objects。

但是plotly_express只支持 facet_plots(切面图) 和 marginal distribution subplots(边际分布子图),只有graph_objects是基于make_subplots模块才能够绘制真正意义上的多子图。下面通过实际例子来讲解。

import pandas as pd
import numpy as np

import plotly_express as px
import plotly.graph_objects as go

# 绘制子图
from plotly.subplots import make_subplots

最重要的是要导入make_subplots模块。

基于plotly_express

plotly_express绘制“子图”是通过参数marginal_xmarginal_y 来实现的,表示在边际上图形的类型,可以是"histogram", “rug”, “box”, or “violin”。

基于facet_plots

切面图是由具有相同轴集的多个子图组成的图形,其中每个子图显示数据的子集,也称之为:trellis(网格) plots or small multiples。直接上官方英文,感觉更合适,暂时找不到比较恰当的中文来翻译。

不同图形切面展示

先导入内置的消费数据集:

1、基于散点图的切面图形

fig = px.scatter(tips,  #  数据
                 x="total_bill", # xy轴 
                 y="tip", 
                 color="smoker", # 颜色
                 facet_col="day"  # 列方向切面字段
                )
fig.show()

2、基于柱状图的切面展示

# 2、柱状图切面

fig = px.bar(tips, 
             x="size", 
             y="total_bill", 
             color="day", 
             facet_row="smoker"  # 行方向切面字段:是否抽烟
            )
fig.show()

控制子图个数

3、wrapping column Facets:控制显示元素个数的切面图

当我们指定的某个切面的字段的取值有很多种不同的情况,我们可以通过facet_col_wrap参数来控制每行最多显示的图形个数,wrap可以理解成:被限制的意思,就是每行限制显示多少。

使用内置的GDP数据集:

# 3、被限制每行图形个数的切面图

fig = px.scatter(gdp,   # 数据集
                 x='gdpPercap',  # x、y、颜色、点的大小size
                 y='lifeExp', 
                 color='continent', 
                 size='pop',
                 facet_col='continent', # 列切面字段
                 facet_col_wrap=3 # 每行最多3个图形
                )
fig.show()

上面的切面图形的col字段是洲,最多只有5个洲。下面的图形选用时间year:

fig = px.scatter(gdp,   # 数据集
                 x='gdpPercap',  # x、y、颜色、点的大小size
                 y='lifeExp', 
                 color='continent', 
                 size='pop',
                 facet_col='year', # 列切面字段
                 facet_col_wrap=3  # 每行最多3个图形
                )
fig.show()

每行最多显示4个图形:

fig = px.scatter(gdp, 
                 x='gdpPercap', 
                 y='lifeExp', 
                 color='continent', 
                 size='pop',
                 facet_col='year', 
                 facet_col_wrap=4  # 每行最多4个图形
                )
fig.show()

子图坐标轴设置

默认情况下子图的y轴是相同的:

# 独立轴的切面:默认情况是相同的y轴
# 默认下y轴的取值范围相同
fig = px.scatter(tips, 
                 x="total_bill", 
                 y="tip", 
                 color='day', 
                 facet_row="time"
                )
fig.show()

通过参数设置不共享y轴:

fig = px.scatter(tips, 
                 x="total_bill", 
                 y="tip", 
                 color='day', 
                 facet_row="time"  # 列方向上切面图
                )

# 设置不共享y轴,对应的是facet_row
fig.update_yaxes(matches=None)

fig.show()

如果是facet_col在列方向上的切面,则可以设置不共享x轴

fig = px.scatter(tips, 
                 x="total_bill", 
                 y="tip", 
                 color='day', 
                 facet_col="time"  # 列方向上切面图
                )

# 设置不共享x轴,对应的是facet_col
fig.update_xaxes(matches=None)

fig.show()

子图标题设置
fig = px.scatter(tips,
                 x="total_bill", 
                 y="tip", 
                 color="time",
                 facet_col="smoker"
                )
fig.show()

通过设置改变子图的标题:

fig = px.scatter(tips, 
                 x="total_bill", 
                 y="tip", 
                 color="time",
                 facet_col="smoker"
                )

# 增加代码:对每行标题通过=切割,取出最后的元素
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))  

fig.show()

基于边际图Marginal

该方法主要是通过marginal_x和marginal_y来实现的。首先导入数据集:

基于不同图形边际图

1、基于散点图:

fig = px.scatter(iris,  # 数据集 
                 x="sepal_length",  # 指定xy轴
                 y="sepal_width",
                 marginal_x="rug",  # 边际图形类型:直方图
                 marginal_y="histogram"  # 轴须图
                )

fig.show()

2、基于密度热图的边际图形设置:

fig = px.density_heatmap(
    iris,  # 数据
    x="sepal_width",  # 两个轴
    y="sepal_length",  
    marginal_x="violin",  # 边际图:小提琴和箱型图
    marginal_y="box")

fig.show()

3、边际图颜色设置

fig = px.scatter(iris,
                 x="sepal_length", 
                 y="sepal_width", 
                 color="species",   # 颜色的设置同样适用于边际图
                 marginal_x="violin", 
                 marginal_y="box",
                 title="边际图颜色设置")
fig.show()

边际图和切面图连用

fig = px.scatter(
    tips, 
    x="total_bill",
    y="tip", 
    color="sex", 
    facet_col="day",  # 日期字段切面
    marginal_x="violin"  # 边际图用小提琴图
)

fig.show()

基于graph_objects

graph_objects方式其实是通过make_subplots函数来实现的。一定要先导入:

# 这种方式一定要导入的模块

from plotly.subplots import make_subplots
import plotly.graph_objects as go

基础子图

# 两个基本参数:设置行、列
fig = make_subplots(rows=1, cols=2)  # 1行2列

# 添加两个数据轨迹,构成两个图形
fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[5, 10, 15]),
    row=1, col=1  # 第一行第一列
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[60, 70, 80]),
    row=1, col=2  # 第一行第二列
)

# 设置图形的宽高和标题
fig.update_layout(height=600, 
                  width=800, 
                  title_text="子图制作")
fig.show()

fig = make_subplots(rows=3, cols=1)  # 3行1列

# 添加3个数据轨迹
fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[5, 10, 15]),
    row=1, col=1  # 1*1
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[60, 70, 80]),
    row=2, col=1  # 2*1
)

fig.add_trace(
    go.Scatter(x=[50, 60, 70], y=[110, 120, 130]),
    row=3, col=1  # 3*1
)

fig.update_layout(height=600, 
                  width=800, 
                  title_text="子图制作")
fig.show()

多行多列子图

多行多列的时候,我们可以指定第一个图形的位置,表示图形从哪里开始。默认左上角是第一个图形的位置。

\fig = make_subplots(rows=2, cols=2,# 2行2列
                    start_cell="bottom-left"# 第一个图形的位置,两个选择:bottom-left', 'top-left
                   )  

# 添加4个数据轨迹
fig.add_trace(
    go.Bar(x=[1, 2, 3], y=[5, 10, 15]),
    row=1, col=1  # 1*1
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[60, 70, 80]),
    row=1, col=2  # 1*2
)

fig.add_trace(
    go.Scatter(x=[50, 60, 70], y=[110, 120, 130]),
    row=2, col=1  # 2*1
)
fig.add_trace(
    go.Bar(x=[50, 60, 70], y=[110, 120, 130]),
    row=2, col=2  # 2*2
)

fig.update_layout(height=600, 
                  width=800, 
                  title_text="子图制作")
fig.show()

多子图标题设置

很多时候我们要给每个子图取名,用subplot_titles来实现

fig = make_subplots(rows=2, cols=2,
                    start_cell="bottom-left", # 'bottom-left', 'top-left
                    subplot_titles=["子图1","子图2","子图3","子图4"]  # 每个子图的名字
                   )  

# 添加4个数据轨迹
fig.add_trace(
    go.Bar(x=[1, 2, 3], y=[5, 10, 15]),
    row=1, col=1  # 1*1
)

fig.add_trace(
    go.Scatter(x=[20, 30, 40], y=[60, 70, 80]),
    row=1, col=2  # 1*2
)

fig.add_trace(
    go.Scatter(x=[50, 60, 70], y=[110, 120, 130]),
    row=2, col=1  # 2*1
)
fig.add_trace(
    go.Bar(x=[50, 60, 70], y=[110, 120, 130]),
    row=2, col=2  # 2*2
)

fig.update_layout(height=600, 
                  width=800, 
                  title_text="多行多列子图制作")
fig.show()

多子图标注Annotations

在子图中我们还可以给数据添加标注:

fig = make_subplots(rows=1, cols=2,
                    subplot_titles=["子图1","子图2"]  # 子图名字
                   )  

# 添加数据
fig.add_trace(
    go.Bar(x=[1, 2, 3], 
           y=[5, 10, 15],
           text=["文字1", "文字2", "文字3"], # 标注内容
           textposition="inside"    # 位置
          ),
    row=1, col=1  # 1*1
)

# 添加数据
fig.add_trace(
    go.Scatter(x=[1, 2, 3], 
           y=[5, 10, 15],
           mode="markers+text",  # 散点图的数据显示形式
           text=["文字4", "文字5", "文字6"],  # 标注内容
           textposition="bottom center"    # 位置
          ),
    row=1, col=2  # 1*2
)


fig.update_layout(height=600, 
                  width=800, 
                  title_text="多子图添加标注")
fig.show()

子图宽度设置

上面绘制的多子图都是大小相同的,我们可以通过参数来进行设置显示不同的大小:column_widths

fig = make_subplots(rows=1, 
                    cols=2,
                    column_widths=[0.35,0.65],  # 重点:两个子图的宽度占比
                    subplot_titles=["子图1","子图2"]  # 名字
                   )  

fig.add_trace(
    go.Bar(x=[1, 2, 3], 
           y=[5, 10, 15],
           text=["文字1", "文字2", "文字3"], # 标注内容
           textposition="inside"    # 位置
          ),
    row=1, col=1  # 1*1
)

# 添加数据
fig.add_trace(
    go.Scatter(x=[1, 2, 3], 
           y=[5, 10, 15],
           mode="markers+text",  # 散点图的数据显示形式
           text=["文字4", "文字5", "文字6"],  # 标注内容
           textposition="bottom center"    # 位置
          ),
    row=1, col=2  # 1*2
)

fig.update_layout(height=600, 
                  width=800, 
                  title_text="多子图添加标注")
fig.show()

共享x轴

共享x轴指的是针对在同列行中的多个图形共享x轴:

fig = make_subplots(rows=3, 
                    cols=1,
                    # 重点参数
                    shared_xaxes=True,  # 设置共享x轴
                    vertical_spacing=0.03,  # 图之间的间隙大小
                    subplot_titles=["子图1","子图2","子图3"]  # 名字
                   )  

fig.add_trace(
    go.Bar(x=[1, 2, 3], 
           y=[5, 10, 15],
           text=["文字1", "文字2", "文字3"], 
           textposition="inside"    # 位置
          ),
    row=1, col=1  # 1*1
)

# 添加数据
fig.add_trace(
    go.Scatter(x=[4, 5, 6], 
           y=[5, 10, 15],
           mode="markers+text",  
           text=["文字4", "文字5", "文字6"],  
           textposition="bottom center"    
          ),
    row=2, col=1  # 2*1
)


# 添加数据
fig.add_trace(
    go.Scatter(x=[10, 20, 30], 
           y=[25, 30, 45],
           mode="markers+text",  # 散点图的数据显示形式
           text=["文字7", "文字8", "文字9"],  # 标注内容
           textposition="bottom center"    # 位置
          ),
    row=3, col=1  # 3*1
)


fig.update_layout(height=600, 
                  width=1000, 
                  title_text="多子图添加标注")
fig.show()

共享y轴

共享y轴指的是针对在同一行中的多个图形共享y轴:

fig = make_subplots(rows=2, cols=2,   # 2*2
                    subplot_titles=["子图1","子图2","子图3","子图4"],
                    shared_yaxes=True  # 重点:共享y轴
                   )

fig.add_trace(go.Scatter(x=[1, 2, 3], y=[2, 3, 4]),  # 两个轴的数据
              row=1, col=1)  # 行列位置

fig.add_trace(go.Scatter(x=[20, 30, 40], y=[5, 6, 7]),
              row=1, col=2)

fig.add_trace(go.Bar(x=[20, 30, 40], y=[20, 40, 10]),
              row=2, col=1)

fig.add_trace(go.Scatter(x=[40, 50, 60], y=[70, 80, 90]),
              row=2, col=2)

fig.update_layout(height=600, width=600,
                  title_text="多子图共享y轴")
fig.show()

坐标轴自定义

from plotly.subplots import make_subplots
import plotly.graph_objects as go


fig = make_subplots(
    rows=2, 
    cols=2, 
    subplot_titles=("Plot 1", "Plot 2", "Plot 3", "Plot 4")
)

# 添加不同数据
fig.add_trace(go.Scatter(x=[1, 2, 3], 
                         y=[4, 5, 6]), 
              row=1, col=1)

fig.add_trace(go.Scatter(x=[20, 30, 40], 
                         y=[50, 60, 70]), 
              row=1, col=2)
fig.add_trace(go.Scatter(x=[300, 400, 500], 
                         y=[600, 700, 800]), 
              row=2, col=1)

fig.add_trace(go.Scatter(x=[4000, 5000, 6000], 
                         y=[7000, 8000, 9000]), 
              row=2, col=2)

# 自定义x轴
fig.update_xaxes(title_text="xaxis—1 标题", row=1, col=1)  # 正常显示
fig.update_xaxes(title_text="xaxis-2 标题", range=[10, 50], row=1, col=2)  # 设置范围range
fig.update_xaxes(title_text="xaxis-3 标题", showgrid=False, row=2, col=1)  # 不显示网格线
fig.update_xaxes(title_text="xaxis-4 标题", type="log", row=2, col=2)  # 基于对数

# 自定义y轴
fig.update_yaxes(title_text="yaxis 1 标题", row=1, col=1)
fig.update_yaxes(title_text="yaxis 2 标题", range=[40, 80], row=1, col=2)
fig.update_yaxes(title_text="yaxis 3 标题", showgrid=False, row=2, col=1)
fig.update_yaxes(title_text="yaxis 4 标题", row=2, col=2)

# Update title and height
fig.update_layout(title_text="自定义子图轴坐标", height=700)

fig.show()

共享颜色轴

使用的参数是coloraxis

fig = make_subplots(rows=1, cols=2, 
                    shared_yaxes=True)   # 在y轴方向上共享

fig.add_trace(go.Bar(x=[1, 2, 3], y=[4, 5, 6],
                    marker=dict(color=[4, 5, 6],
                                coloraxis="coloraxis")),
              1, 1)  # 直接表示位置在(1,1)

fig.add_trace(go.Bar(x=[1, 2, 3], y=[2, 3, 5],
                    marker=dict(color=[2, 3, 5],
                                coloraxis="coloraxis")),
              1, 2) # 位置在(1,2)

fig.update_layout(coloraxis=dict(colorscale='Bluered'),   # 颜色轴
                  showlegend=False)  # 不显示图例

fig.show()

子图位置自定义

子图位置的自定义是通过参数spec来实现的,spec是一个2维的列表集合,列表中包含rows和cols两个参数。

比如我们想绘制2*2的图形,但是只有3个图形,那么肯定有个图形会占据2行1列或者是1行2列的位置,通过例子来解释。

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{}, {}],  # 1*1,1*2
           [{"colspan": 2}, None]],  # 2*1的位置占据两列,2*2的位置没有图
    subplot_titles=("子图1","子图2", "子图3"))

fig.add_trace(go.Scatter(x=[1, 2], y=[5, 6]),
                 row=1, col=1) # 1*1

fig.add_trace(go.Scatter(x=[4, 6], y=[8, 9]),
                 row=1, col=2)  # 1*2

fig.add_trace(go.Scatter(x=[1, 2, 3], y=[2, 5, 2]),
                 row=2, col=1)  # 2*1占据两行

fig.update_layout(showlegend=False,   # 不显示图例
                  title_text="子图位置自定义")
fig.show()

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"rowspan":2}, {}],  # 1*1 占据两列,1*2
           [None,{}]],  # 2*1的位置没有图
    subplot_titles=("子图1","子图2", "子图3"))

fig.add_trace(go.Scatter(x=[1, 2], y=[5, 6]),
                 row=1, col=1) # 1*1  占据两列

fig.add_trace(go.Scatter(x=[4, 6], y=[8, 9]),
                 row=1, col=2)  # 1*2

fig.add_trace(go.Scatter(x=[1, 2, 3], y=[2, 5, 2]),
                 row=2, col=2)  # 2*1

fig.update_layout(showlegend=False,   # 不显示图例
                  title_text="子图位置自定义")
fig.show()

让我们看一个更为复杂的例子:

fig = make_subplots(
    rows=5, cols=2,  # 5*2的图形
    specs=[[{}, {"rowspan": 2}], # 1*1 ; 2*1的位置占据两行rows
           [{}, None],  # 2*1; 2*2的位置已经被上面的2*1占据
           [{"rowspan": 2, "colspan": 2}, None],  # 3*1的位置占据2行2列所以,第3、4行的两列只有单个图形
           [None, None],
           [{}, {}]]  # 5*1;5*2
)

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(1,1)"), row=1, col=1)

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(1,2)"), row=1, col=2)

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(2,1)"), row=2, col=1)

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(3,1)"), row=3, col=1)

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(5,1)"), 5,1) # row和col可以省略
fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name="(5,2)"), 5, 2)

fig.update_layout(height=600, 
                  width=600, 
                  title_text="多子图位置自定义")

fig.show()

子图类型自定义

子图可供选择的图形类型:

  • “xy”: 二维的散点scatter、柱状图bar等

  • “scene”: 3维的scatter3d、球体cone

  • “polar”: 极坐标图形如scatterpolar, barpolar等

  • “ternary”: 三元图如scatterternary

  • “mapbox”: 地图如scattermapbox

  • “domain”: .针对有一定域的图形,如饼图pie, parcoords, parcats,

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"type": "xy"}, {"type": "polar"}],
           [{"type": "domain"}, {"type": "scene"}]],  # 通过type来指定类型
)

fig.add_trace(go.Bar(y=[2, 3, 1]),
              1, 1)

fig.add_trace(go.Barpolar(theta=[0, 45, 90], r=[2, 3, 1]),
              1, 2)

fig.add_trace(go.Pie(values=[2, 3, 1]),
              2, 1)

fig.add_trace(go.Scatter3d(x=[2, 3, 1], y=[0, 0, 0],
                           z=[0.5, 1, 2], mode="lines"),
              2, 2)

fig.update_layout(height=700, showlegend=False)

fig.show()

多子图类型和位置的连用:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"type": "xy"}, {"type": "polar"}],  # 设置类型
           [{"colspan":2,"type": "domain"},None]], # 设置位置:2*1的位置占据两列; 2*2没有图形
)

fig.add_trace(go.Bar(y=[2, 3, 1]),
              1, 1)

fig.add_trace(go.Barpolar(theta=[0, 45, 90], r=[2, 3, 1]),
              1, 2)

fig.add_trace(go.Pie(values=[2, 3, 1]),
              2, 1)  # 

fig.update_layout(height=700, showlegend=False)

fig.show()

参数解释

最后附上官网地址,多多学习:https://plotly.com/python/subplots/

plotly.subplots.make_subplots(rows=1,   # 行列数值决定位置
                              cols=1, 
                              shared_xaxes=False,  # 是否共享xy轴
                              shared_yaxes=False, 
                              start_cell='top-left',   # 第一个图形的位置
                              print_grid=False,  # 是否输出表格参数
                              horizontal_spacing=None,   # 垂直和水平方向上的间隔
                              vertical_spacing=None, 
                              subplot_titles=None,   # 子图标题
                              column_widths=None,  # 列宽和行高
                              row_heights=None, 
                              specs=None,  # 子图类型
                              insets=None, # 
                              column_titles=None, # 行和列的标题
                              row_titles=None, 
                              x_title=None,  # xy轴axis的标题
                              y_title=None, 
                              figure=None, 
                              **kwargs)

通过下面的方式查看帮助文档:

from plotly.subplots import make_subplots
help(make_subplots)
  • 12
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值