Plotly graph_objects 3D散点子图hovertemplate不识别customdata
问题背景
参考Plotly官方子图教程构建包含两个3D散点图的可视化,基础绘图搭建完成后hovertemplate功能始终无法正常生效。
原本沿用单Scatter3D图(直接构建、不通过graph_objects创建)中验证可用的配置逻辑:传入与待渲染点数量一致的customdata数组,预期实现customdata与体素1:1对应,悬停时展示对应字段值;但实际悬停时仅显示hovertemplate参数设置的字面字符串,不会替换占位符内容。
问题复现代码如下:
import plotly.graph_objects as go from plotly.subplots import make_subplots import json import pandas as pd import numpy as np json_data = {} with open("voxels_with_calculated.json", "r") as in_file: json_data = json.load(in_file) json_df = pd.DataFrame(json_data) # 单条数据格式示例 #{ # "x": 1, # "y": -31, # "z": -63, # "M": -1, # "E": -1, # "F": -1, # "CE": -1, # "MA": -1, # "R": -1 #} x,y,z = json_df.x, json_df.y, json_df.z fig = make_subplots( rows=1, cols=2, specs=[[{"type": "scatter3d"}, {"type": "scatter3d"}]], subplot_titles=["Original Voxels", "Voxels to World"] ) fig.add_trace( go.Scatter3d( x=x, y=y, z=z, customdata=[json_df.M, json_df.E, json_df.F, json_df.CE, json_df.MA, json_df.R], hovertemplate="<b> dfdfd %{customdata[0]}</b> ", mode='markers' ), row=1, col=1 ) fig.add_trace( go.Scatter3d( x=x, y=y, z=z, customdata=[json_df.M, json_df.E, json_df.F, json_df.CE, json_df.MA, json_df.R], hovertemplate="<b> HAHA %{customdata}</b>", mode='markers' ), row=1, col=2 ) """ # 单图(不通过graph_objects创建)场景下可用的配置 fig.update_traces( hovertemplate="<br>".join([ "M: %{customdata[0]}", "E: %{customdata[1]}", "F: %{customdata[2]}", "CE: %{customdata[3]}", "MA: %{customdata[4]}", "R: %{customdata[5]}", "U: %{customdata[6]}" ]) ) """ fig.show()
无论如何调整配置,鼠标悬停时都仅显示字面的%{customdata}字符串,单图场景下可用的update_traces配置hovertemplate的写法在子图场景下也无法生效。

故障原因
核心问题是customdata参数传入的数组形状不符合Plotly的要求:
- 代码中传入的
[json_df.M, json_df.E, json_df.F, json_df.CE, json_df.MA, json_df.R]是按列拼接的6个Series,整体形状为(自定义字段数, 点数量) - Plotly要求
customdata的形状必须为(点数量, 自定义字段数),即每一行对应一个渲染点的所有自定义字段,每一列对应一个自定义字段的所有点值
单图场景下Plotly会自动对形状不符的customdata做转置容错,但子图场景下该自动容错逻辑不触发,直接导致hovertemplate中的占位符无法被解析,原样输出字符串。
修复方案
调整customdata的传入格式,保证数组形状匹配即可,不需要修改其他子图配置:
- 直接从DataFrame中选取需要的字段列,转为numpy数组传入,会自动生成符合形状要求的
(点数量, 字段数)结构 - 可在hovertemplate末尾添加
<extra></extra>,隐藏悬停框侧边默认显示的trace名称,优化展示效果
修复后的trace添加代码示例:
fig.add_trace( go.Scatter3d( x=x, y=y, z=z, # 修正customdata形状,直接取对应列的数组即可 customdata=json_df[["M", "E", "F", "CE", "MA", "R"]].values, hovertemplate="<br>".join([ "<b>Original Voxels</b>", "M: %{customdata[0]}", "E: %{customdata[1]}", "F: %{customdata[2]}", "CE: %{customdata[3]}", "MA: %{customdata[4]}", "R: %{customdata[5]}", "<extra></extra>" ]), mode='markers' ), row=1, col=1 ) # 第二个子图的trace做相同修改即可 fig.add_trace( go.Scatter3d( x=x, y=y, z=z, customdata=json_df[["M", "E", "F", "CE", "MA", "R"]].values, hovertemplate="<br>".join([ "<b>Voxels to World</b>", "M: %{customdata[0]}", "E: %{customdata[1]}", "F: %{customdata[2]}", "CE: %{customdata[3]}", "MA: %{customdata[4]}", "R: %{customdata[5]}", "<extra></extra>" ]), mode='markers' ), row=1, col=2 )
修改后悬停时即可正常替换占位符,展示对应点的自定义字段值。
内容的提问来源于stack exchange,提问作者J. Maria
相关产品推荐
相关产品推荐

