图对象#
参考:Graph Objects | Python | Plotly
什么是图对象?#
plotly.graph_objects
模块(通常按顺序导入)包含一个自动生成的 Python类层次结构,这些类在这个图模式中表示非叶节点。术语“图对象”指的是这些类的实例。plotly.graph_objects
模块中有两大主类:Figure
和 FigureWidget
(兼容 ipywidgets
的变体),它们都表示整个图。这些类的实例有许多方便的方法来用 Python 操作它们的属性(例如 .update_layout()
或 .add_trace()
,它们都接受“魔法下划线”表示法),以及渲染它们(例如.show()
)并将它们导出为各种格式(例如 .to_json()
或 .write_image()
或 .write_html()
)。
注意:Plotly Express
中的函数(这是 Plotly 库的推荐入口点)都建立在图对象之上,并且全返回 plotly.graph_objects.Figure
实例。
图的每个非叶子属性都由 plotly
中的一个 plotly.graph_objects
层次结构的类实例表示。例如,一个 fig
可以有一个属性layout.margin
包含属性 t
、l
、b
和 r
,它们是树的叶子:它们没有子节点。fig.layout
的字段是 plotly.graph_objects.Layout
类的一个对象,并且 fig.layout.margin
表示 margin
节点,它有字段 t
、l
、b
和 r
,其中包含各自叶节点的值。注意,指定所有这些值不需要使用“魔法下划线”符号创建中间对象: go.Figure(layout_margin=dict(t=10, b=10, r=10, l=10))
。
列表中包含的对象是属性data
的值,称为“轨迹”,可以是 40 多种类型中的一种,每种类型在plotly.graph_objects
中都有相应的类。例如,scatter
类型的轨迹由plotly.graph_objects.Scatter
类的实例表示。这意味着一个以go.Figure(data=[go.Scatter(x=[1,2], y=[3,4)])
将JSON 表示 {"data": [{"type": "scatter", "x": [1,2], "y": [3,4]}]}
。
何时直接使用图对象#
创建图的推荐方法是使用plotly.express
模块中的函数,统称为 Plotly express,它都返回 plot.graph_objects.Figure
的实例,所以使用 plotly
库生成的每个图,实际上都使用了图对象,除非是手工从字典中构建的。也就是说,某些类型的图形还不能用 Plotly Express 创建,比如使用特定的 3D 轨迹类型(如网格或等值面)的图。此外,从使用 Plotly Express 创建的图形开始创建某些图形非常麻烦,例如具有不同类型的子图、双轴图 或具有多个不同类型轨迹的分面图。
注意,Plotly Express 在一个函数调用中生成的图很容易在创建时定制,并且在创建后使用update_*
和add_*
方法进行操作。Plotly Express 生成的图总是可以使用图对象从头开始构建,但这种方法通常需要 5-100 行代码,而不是 1 行。下面是一个简单的例子,演示如何从相同的数据生成相同的图形对象,一次使用 Plotly Express,另一次不使用。本例中的数据是 “long form” 的,但 Plotly Express 也接受 “wide form” 的数据,而且 Plotly Express 相对于图对象节省的行数是可以比较的。更复杂的图形,如 sunbursts, parallel coordinates, facet plots 或 animations 需要更多的图对象代码行,而使用 Plotly Express 从一种表示转换到另一种表示通常只需要更改几个字符。
import pandas as pd
df = pd.DataFrame({
"Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
"Contestant": ["Alex", "Alex", "Alex", "Jordan", "Jordan", "Jordan"],
"Number Eaten": [2, 1, 3, 1, 3, 2],
})
import plotly.express as px
fig = px.bar(df, x="Fruit", y="Number Eaten", color="Contestant", barmode="group")
fig.show()
不使用 px
:
import plotly.graph_objects as go
fig = go.Figure()
for contestant, group in df.groupby("Contestant"):
fig.add_trace(go.Bar(x=group["Fruit"], y=group["Number Eaten"], name=contestant,
hovertemplate="Contestant=%s<br>Fruit=%%{x}<br>Number Eaten=%%{y}<extra></extra>"% contestant))
fig.update_layout(legend_title_text = "Contestant")
fig.update_xaxes(title_text="Fruit")
fig.update_yaxes(title_text="Number Eaten")
fig.show()
混合子图#
重要
Plotly Express 不支持创建具有任意混合子图的图形,即具有不同类型子图的图形。Plotly Express 仅支持 facet 图和边际分布子图。要制作带有混合子图的图形,请将 make_subplots()
函数与下图所述的图对象结合使用。
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
go.FigureWidget();
# read in volcano database data
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/volcano_db.csv",
encoding="iso-8859-1",
)
# frequency of Country
freq = df
freq = freq.Country.value_counts().reset_index().rename(columns={"index": "x"})
# read in 3d volcano surface data
df_v = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv")
# Initialize figure with subplots
fig = make_subplots(
rows=2, cols=2,
column_widths=[0.6, 0.4],
row_heights=[0.4, 0.6],
specs=[[{"type": "scattergeo", "rowspan": 2}, {"type": "bar"}],
[ None , {"type": "surface"}]])
# Add scattergeo globe map of volcano locations
fig.add_trace(
go.Scattergeo(lat=df["Latitude"],
lon=df["Longitude"],
mode="markers",
hoverinfo="text",
showlegend=False,
marker=dict(color="crimson", size=4, opacity=0.8)),
row=1, col=1
)
# Add locations bar chart
fig.add_trace(
go.Bar(x=freq["x"][0:10],y=freq["Country"][0:10], marker=dict(color="crimson"), showlegend=False),
row=1, col=2
)
# Add 3d surface of volcano
fig.add_trace(
go.Surface(z=df_v.values.tolist(), showscale=False),
row=2, col=2
)
# Update geo subplot properties
fig.update_geos(
projection_type="orthographic",
landcolor="white",
oceancolor="MidnightBlue",
showocean=True,
lakecolor="LightBlue"
)
# Rotate x-axis labels
fig.update_xaxes(tickangle=45)
# Set theme, margin, and annotation in layout
fig.update_layout(
template="plotly_dark",
margin=dict(r=10, t=25, b=40, l=60),
annotations=[
dict(
text="Source: NOAA",
showarrow=False,
xref="paper",
yref="paper",
x=0,
y=0)
]
)
fig.show()
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pandas/core/indexes/base.py:3805, in Index.get_loc(self, key)
3804 try:
-> 3805 return self._engine.get_loc(casted_key)
3806 except KeyError as err:
File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()
File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()
File pandas/_libs/hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()
File pandas/_libs/hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'x'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
Cell In[2], line 41
29 fig.add_trace(
30 go.Scattergeo(lat=df["Latitude"],
31 lon=df["Longitude"],
(...)
36 row=1, col=1
37 )
39 # Add locations bar chart
40 fig.add_trace(
---> 41 go.Bar(x=freq["x"][0:10],y=freq["Country"][0:10], marker=dict(color="crimson"), showlegend=False),
42 row=1, col=2
43 )
45 # Add 3d surface of volcano
46 fig.add_trace(
47 go.Surface(z=df_v.values.tolist(), showscale=False),
48 row=2, col=2
49 )
File /opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pandas/core/frame.py:4102, in DataFrame.__getitem__(self, key)
4100 if self.columns.nlevels > 1:
4101 return self._getitem_multilevel(key)
-> 4102 indexer = self.columns.get_loc(key)
4103 if is_integer(indexer):
4104 indexer = [indexer]
File /opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
3807 if isinstance(casted_key, slice) or (
3808 isinstance(casted_key, abc.Iterable)
3809 and any(isinstance(x, slice) for x in casted_key)
3810 ):
3811 raise InvalidIndexError(key)
-> 3812 raise KeyError(key) from err
3813 except TypeError:
3814 # If we have a listlike key, _check_indexing_error will raise
3815 # InvalidIndexError. Otherwise we fall through and re-raise
3816 # the TypeError.
3817 self._check_indexing_error(key)
KeyError: 'x'