plotly 更新图#

无论图对象是如何构造的,都可以通过向其添加额外的轨迹并修改其属性来更新它。

plotly 添加轨迹#

可以使用 add_trace() 方法将新的轨迹添加到图对象中。这个方法接受图对象轨迹(go.Scattergo.Bar 等的实例),并将其添加到图中。这允许您从空的图开始,并按顺序向其添加轨迹。append_trace() 方法做同样的事情,尽管它不返回图。

import plotly.graph_objects as go
go.FigureWidget(); # 初始化

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

fig.show()

您还可以向图工厂或 Plotly Express 生成的图添加轨迹:

import plotly.express as px

df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species",
                 title="使用 add_trace() 方法的 Plotly Express 图")

fig.add_trace(
    go.Scatter(
        x=[2, 4],
        y=[4, 8],
        mode="lines",
        line=go.scatter.Line(color="gray"),
        showlegend=False)
)
fig.show()

给子图添加轨迹#

如果使用 plotly.subplots.make_subplots() 创建了图,那么可以使用 add_trace() 提供的 row 和 col 参数向特定的子图添加轨迹。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_trace(go.Scatter(y=[4, 2, 1], mode="lines"), row=1, col=1)
fig.add_trace(go.Bar(y=[2, 1, 3]), row=1, col=2)

fig.show()

这也适用于 Plotly Express 使用 facet_row 和/或 facet_col 参数创建的图。

import plotly.express as px

df = px.data.iris()

fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species", facet_col="species",
                 title="Adding Traces To Subplots Witin A Plotly Express Figure")

reference_line = go.Scatter(x=[2, 4],
                            y=[4, 8],
                            mode="lines",
                            line=go.scatter.Line(color="gray"),
                            showlegend=False)

fig.add_trace(reference_line, row=1, col=1)
fig.add_trace(reference_line, row=1, col=2)
fig.add_trace(reference_line, row=1, col=3)

fig.show()

添加轨迹的便利函数#

作为 add_trace() 方法的替代方法,图对象图形有一系列 add_{trace} 形式的方法(其中 {trace} 是一个轨迹类型的名称),用于构造和添加每种轨迹类型的轨迹。

下面是前面的子图示例,使用 fig.add_scatter() 添加散点轨迹,使用 fig.add_bar() 添加条形轨迹。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_scatter(y=[4, 2, 1], mode="lines", row=1, col=1)
fig.add_bar(y=[2, 1, 3], row=1, col=2)

fig.show()

魔法下划线表示法#

为了更容易地使用嵌套属性,图对象构造器和许多图对象方法都支持魔法下划线表示法。

这允许通过使用下划线将多个嵌套属性名连接在一起来引用嵌套属性。

例如,在没有魔法下划线符号的图构造函数中指定figure标题需要将 layout 参数设置为 dict(title=dict(text="A Chart"))。类似地,设置散点轨迹的线条颜色需要将 marker 属性设置为 dict(color="crimson")

import plotly.graph_objects as go

fig = go.Figure(
    data=[go.Scatter(y=[1, 3, 2], line={'color': 'crimson'})],
    layout={'title':
            {'text': 'A Graph Objects Figure Without Magic Underscore Notation'}}
)

fig.show()

使用魔法的下划线符号,您可以通过向图构造函数传递一个名为 layout_title_text 的关键字参数和传递 go.Scatter 构造函数的关键字参数 line_color

import plotly.graph_objects as go

fig = go.Figure(
    data=[go.Scatter(y=[1, 3, 2], line_color="crimson")],
    layout_title_text="A Graph Objects Figure With Magic Underscore Notation"
)

fig.show()

整个图对象 API 都支持魔法下划线表示法,它通常可以显著地简化涉及深度嵌套属性的操作。

更新轨迹#

图对象支持 update_trace() 方法,该方法可用于更新图的一个或多个轨迹的多个嵌套属性。

为了展示一些示例,将从包含横跨两个子图的条形和散点轨迹的图开始。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)
fig.add_scatter(y=[4, 2, 3.5], mode="markers",
                marker=dict(size=20, color="LightSeaGreen"),
                name="a", row=1, col=1)

fig.add_bar(y=[2, 1, 3],
            marker=dict(color="MediumPurple"),
            name="b", row=1, col=1)

fig.add_scatter(y=[2, 3.5, 4], mode="markers",
                marker=dict(size=20, color="MediumPurple"),
                name="c", row=1, col=2)

fig.add_bar(y=[1, 3, 2],
            marker=dict(color="LightSeaGreen"),
            name="d", row=1, col=2)

fig.show()

注意 scatter 轨迹和 bar 轨迹都有 marker.color 属性来控制它们的颜色。下面是使用 update_trace() 修改所有轨迹颜色的示例。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_scatter(y=[4, 2, 3.5], mode="markers",
                marker=dict(size=20, color="LightSeaGreen"),
                name="a", row=1, col=1)

fig.add_bar(y=[2, 1, 3],
            marker=dict(color="MediumPurple"),
            name="b", row=1, col=1)

fig.add_scatter(y=[2, 3.5, 4], mode="markers",
                marker=dict(size=20, color="MediumPurple"),
                name="c", row=1, col=2)

fig.add_bar(y=[1, 3, 2],
            marker=dict(color="LightSeaGreen"),
            name="d", row=1, col=2)

fig.update_traces(marker=dict(color="RoyalBlue"))

fig.show()

update_trace() 方法支持 selector 参数来控制应该更新哪些轨迹。只有具有匹配选择器属性的轨迹才会被更新。下面是使用选择器只更新条轨迹颜色的例子。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_scatter(y=[4, 2, 3.5], mode="markers",
                marker=dict(size=20, color="LightSeaGreen"),
                name="a", row=1, col=1)

fig.add_bar(y=[2, 1, 3],
            marker=dict(color="MediumPurple"),
            name="b", row=1, col=1)

fig.add_scatter(y=[2, 3.5, 4], mode="markers",
                marker=dict(size=20, color="MediumPurple"),
                name="c", row=1, col=2)

fig.add_bar(y=[1, 3, 2],
            marker=dict(color="LightSeaGreen"),
            name="d", row=1, col=2)

fig.update_traces(marker=dict(color="RoyalBlue"),
                  selector=dict(type="bar"))

fig.show()

魔法下划线表示法可以在选择器中用于匹配嵌套属性。下面是更新所有被正式着色为 "MediumPurple" 的轨迹颜色的例子。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_scatter(y=[4, 2, 3.5], mode="markers",
                marker=dict(size=20, color="LightSeaGreen"),
                name="a", row=1, col=1)

fig.add_bar(y=[2, 1, 3],
            marker=dict(color="MediumPurple"),
            name="b", row=1, col=1)

fig.add_scatter(y=[2, 3.5, 4], mode="markers",
                marker=dict(size=20, color="MediumPurple"),
                name="c", row=1, col=2)

fig.add_bar(y=[1, 3, 2],
            marker=dict(color="LightSeaGreen"),
            name="d", row=1, col=2)

fig.update_traces(marker_color="RoyalBlue",
                  selector=dict(marker_color="MediumPurple"))

fig.show()

对于带有子图的图,update_trace() 方法还支持 rowcol 参数来控制应该更新哪些轨迹。只会更新指定子图行和列中的轨迹。下面是更新第二个子图列中所有轨迹颜色的示例。

from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)

fig.add_scatter(y=[4, 2, 3.5], mode="markers",
                marker=dict(size=20, color="LightSeaGreen"),
                name="a", row=1, col=1)

fig.add_bar(y=[2, 1, 3],
            marker=dict(color="MediumPurple"),
            name="b", row=1, col=1)

fig.add_scatter(y=[2, 3.5, 4], mode="markers",
                marker=dict(size=20, color="MediumPurple"),
                name="c", row=1, col=2)

fig.add_bar(y=[1, 3, 2],
            marker=dict(color="LightSeaGreen"),
            name="d", row=1, col=2)

fig.update_traces(marker=dict(color="RoyalBlue"),
                  col=2)

fig.show()

update_trace() 方法还可以用于由图工厂或 Plotly Express 生成的图。下面是将 Plotly Express 生成的回归线更新为虚线的示例。

import pandas as pd
import plotly.express as px

df = px.data.iris()

fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species",
                 facet_col="species", trendline="ols", title="Using update_traces() With Plotly Express Figures")

fig.update_traces(
    line=dict(dash="dot", width=4),
    selector=dict(type="scatter", mode="lines"))

fig.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[13], line 6
      2 import plotly.express as px
      4 df = px.data.iris()
----> 6 fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species",
      7                  facet_col="species", trendline="ols", title="Using update_traces() With Plotly Express Figures")
      9 fig.update_traces(
     10     line=dict(dash="dot", width=4),
     11     selector=dict(type="scatter", mode="lines"))
     13 fig.show()

File ~/checkouts/readthedocs.org/user_builds/daofield/envs/latest/lib/python3.12/site-packages/plotly/express/_chart_types.py:66, in scatter(data_frame, x, y, color, symbol, size, hover_name, hover_data, custom_data, text, facet_row, facet_col, facet_col_wrap, facet_row_spacing, facet_col_spacing, error_x, error_x_minus, error_y, error_y_minus, animation_frame, animation_group, category_orders, labels, orientation, color_discrete_sequence, color_discrete_map, color_continuous_scale, range_color, color_continuous_midpoint, symbol_sequence, symbol_map, opacity, size_max, marginal_x, marginal_y, trendline, trendline_options, trendline_color_override, trendline_scope, log_x, log_y, range_x, range_y, render_mode, title, template, width, height)
     12 def scatter(
     13     data_frame=None,
     14     x=None,
   (...)
     60     height=None,
     61 ) -> go.Figure:
     62     """
     63     In a scatter plot, each row of `data_frame` is represented by a symbol
     64     mark in 2D space.
     65     """
---> 66     return make_figure(args=locals(), constructor=go.Scatter)

File ~/checkouts/readthedocs.org/user_builds/daofield/envs/latest/lib/python3.12/site-packages/plotly/express/_core.py:2267, in make_figure(args, constructor, trace_patch, layout_patch)
   2264     elif args["ecdfnorm"] == "percent":
   2265         group[var] = 100.0 * group[var] / group_sum
-> 2267 patch, fit_results = make_trace_kwargs(
   2268     args, trace_spec, group, mapping_labels.copy(), sizeref
   2269 )
   2270 trace.update(patch)
   2271 if fit_results is not None:

File ~/checkouts/readthedocs.org/user_builds/daofield/envs/latest/lib/python3.12/site-packages/plotly/express/_core.py:361, in make_trace_kwargs(args, trace_spec, trace_data, mapping_labels, sizeref)
    359 trace_patch["x"] = sorted_trace_data[args["x"]][non_missing]
    360 trendline_function = trendline_functions[attr_value]
--> 361 y_out, hover_header, fit_results = trendline_function(
    362     args["trendline_options"],
    363     sorted_trace_data[args["x"]],
    364     x,
    365     y,
    366     args["x"],
    367     args["y"],
    368     non_missing,
    369 )
    370 assert len(y_out) == len(
    371     trace_patch["x"]
    372 ), "missing-data-handling failure in trendline code"
    373 trace_patch["y"] = y_out

File ~/checkouts/readthedocs.org/user_builds/daofield/envs/latest/lib/python3.12/site-packages/plotly/express/trendline_functions/__init__.py:43, in ols(trendline_options, x_raw, x, y, x_label, y_label, non_missing)
     37     if k not in valid_options:
     38         raise ValueError(
     39             "OLS trendline_options keys must be one of [%s] but got '%s'"
     40             % (", ".join(valid_options), k)
     41         )
---> 43 import statsmodels.api as sm
     45 add_constant = trendline_options.get("add_constant", True)
     46 log_x = trendline_options.get("log_x", False)

ModuleNotFoundError: No module named 'statsmodels'