Plotly子图添加形状:数字信号绘制问题求助
解决Plotly子图中为指定子图添加自定义形状的问题
问题描述
需要用Plotly绘制自定义格式的数字信号:信号值为0时显示细线,值为1时显示粗线。当前实现方式是绘制水平线,在值为1的区间添加填充矩形。但使用子图绘制多个信号时,所有形状只会显示在第一个子图中,需实现为任意子图添加对应形状。
用户原始代码:
import numpy as np from plotly.subplots import make_subplots import plotly.graph_objects as go #Creating digital signals in array y=np.zeros((2,40)) x=np.arange(0,4,.1) y[0,:]=np.sign(np.cos(x*np.pi)) y[1,:]=np.sign(np.cos(x*1.5*np.pi)) y[y==-1]=0 dig_signals = make_subplots(rows=2,cols=1,subplot_titles=["signal 1", "signal 2"] ,shared_xaxes=True,x_title="time [sec]",vertical_spacing=.2) for k in range(2): tiempo_flancos=[] #to save the amount of state changes if y[k,0]==1: tiempo_flancos.append(x[0]) for i in range(1,len(x)): if y[k,i]!=y[k,i-1]: tiempo_flancos.append(x[i]) if y[k,-1]==1: tiempo_flancos.append(x[-1]) dictionaries=[] #this list is needed to create shapes in plotly for i in range(int(len(tiempo_flancos)/2)): dictionaries.append(dict(x0=tiempo_flancos[i*2],y0=.75, x1=tiempo_flancos[1+i*2],y1=-.75, line_width=0,fillcolor="steelblue")) dig_signals.add_trace(go.Scatter(x=x,y=np.repeat(0,len(x))),row=k+1,col=1) dig_signals.update_layout(yaxis=dict(range[-3,3]),showlegend=False,shapes=dictionaries) dig_signals.update_traces(line_color="steelblue", line_width=2.5) dig_signals.update_yaxes(visible=False) dig_signals
解决方案
问题核心是:未指定形状所属的子图坐标轴,且每次循环调用update_layout(shapes=dictionaries)会覆盖之前的形状配置。需做以下修改:
- 为每个形状指定子图坐标轴:Plotly子图的坐标轴以
x1/y1、x2/y2命名(对应第1/2个子图),在创建形状字典时添加xref和yref属性,明确指定形状归属的子图。 - 累加形状而非覆盖:每次循环将新生成的形状列表追加到布局的现有形状中,避免覆盖。
- 正确设置子图y轴范围:使用
update_yaxes指定row参数,为每个子图设置y轴范围。
修改后的完整代码:
import numpy as np from plotly.subplots import make_subplots import plotly.graph_objects as go # 创建数字信号数组 y = np.zeros((2, 40)) x = np.arange(0, 4, .1) y[0, :] = np.sign(np.cos(x * np.pi)) y[1, :] = np.sign(np.cos(x * 1.5 * np.pi)) y[y == -1] = 0 dig_signals = make_subplots(rows=2, cols=1, subplot_titles=["signal 1", "signal 2"], shared_xaxes=True, x_title="time [sec]", vertical_spacing=.2) for k in range(2): tiempo_flancos = [] # 存储状态变化的时间点 if y[k, 0] == 1: tiempo_flancos.append(x[0]) for i in range(1, len(x)): if y[k, i] != y[k, i-1]: tiempo_flancos.append(x[i]) if y[k, -1] == 1: tiempo_flancos.append(x[-1]) dictionaries = [] # 为当前子图创建形状,指定xref和yref for i in range(int(len(tiempo_flancos)/2)): dictionaries.append(dict( x0=tiempo_flancos[i*2], y0=.75, x1=tiempo_flancos[1+i*2], y1=-.75, line_width=0, fillcolor="steelblue", xref=f"x{k+1}", # 指定对应子图的x轴 yref=f"y{k+1}" # 指定对应子图的y轴 )) # 添加轨迹到对应子图 dig_signals.add_trace(go.Scatter(x=x, y=np.repeat(0, len(x))), row=k+1, col=1) # 累加新形状到布局中,而非覆盖 dig_signals.update_layout( showlegend=False, shapes=dig_signals.layout.shapes + dictionaries ) # 更新当前子图的轨迹样式 dig_signals.update_traces( line_color="steelblue", line_width=2.5, row=k+1, col=1 ) # 设置当前子图的y轴范围和可见性 dig_signals.update_yaxes( range=[-3, 3], visible=False, row=k+1, col=1 ) dig_signals.show()
关键修改说明
- 指定形状的子图归属:通过
xref=f"x{k+1}"和yref=f"y{k+1}",让每个形状绑定到对应的子图坐标轴,解决了形状只显示在第一个子图的问题。 - 累加形状配置:
shapes=dig_signals.layout.shapes + dictionaries确保每次循环添加的形状不会覆盖之前的,所有子图的形状都会被保留。 - 精准更新子图属性:调用
update_traces和update_yaxes时指定row和col参数,确保修改仅作用于当前子图,避免全局影响。
内容的提问来源于stack exchange,提问作者def user
相关产品推荐
相关产品推荐

