Given a figure with several traces, how to add a vertical line to several of these traces?

I am using plotly graph_objects to construct a figure which has several scatter plots on top of each other, with a shared x-axis.

If i use fig.add_vline(x=t) to add a vertical line to the figure I created that way, the vertical is only shown in the last (bottom) trace (see image).

However I would like to add the vline to a different or any number of selected traces in the figure.

How can this be done using the figure object (not during the time the figure object gets constructed and the traces get added)?

hi @johann.petrak
Are you able to share an MRE?

Would using facet subplots be an option here?

1 Like

HI @johann.petrak as @adamschroeder already mentioned, it’s hard to tell without knowing, how you create the graph you are showing.

Updated example with code below. So I get some fig from a function over which I do not have control. This function has added three traces to the figure, and I want to e.g. add vertical lines to traces 1 and 2 but not three. However if I use the code indicated below to add the line, it always ends up in trace 3 as shown in the updated example image.
How can I specify the traces where the vline is added to?

import plotly.graph_objects as go
import math
def makefig():
    xs = list(range(200))
    fig=go.Figure()
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x/10) for x in xs],name="d1", yaxis="y"))
    fig.update_layout(yaxis=dict(domain=[0,0.3], showline=True, side="left", autorange=True))
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x/5) for x in xs],name="d2", yaxis="y2"))
    fig.update_layout(yaxis2=dict(domain=[0.3,0.6], showline=True, side="right", autorange=True))
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x) for x in xs],name="d3", yaxis="y3"))
    fig.update_layout(yaxis3=dict(domain=[0.6,0.9], showline=True, side="left", autorange=True))
    fig.update_traces(showlegend=False)
    fig.update_layout(height=800)
    return fig

fig = makefig()
fig.add_vline(22)
fig.add_vline(55)
fig.write_image("vlines02.png")
fig.show(renderer="png")

I have added an update (could not find a way to update my original post)

I am not sure how the facet subplot page could help … as I wrote, I am using the fig.add_vline method to add the vertical line, but the line gets added to just the last trace and I want to add it to a specific trace of the ones that make up the figure.

I have added a new post with the code and specific output for that code.
As mentioned the issue is that I just get a figure object that is constructed with several traces and when I then use the method to add the vline, the line gets added just to the last trace.
I would like to add vertical lines to arbitrary of those traces though,

Hey @johann.petrak using the fig.add_vline() ultimately just adds shapes (a line shape) to a given y-axis. As default value, the y-domain is used.

I don’t think you an actually change that. But knowing that v_lines are just shapes in a given axis, you might write your custom add_vline() function:

def add_vlines(figure: go.Figure, x_coordinate:int, y_axis_indicator:str="") -> go.Figure:
    figure_dict = figure.to_dict()
    
    existing_shapes = figure_dict.get("layout", {}).get("shapes", [])   

    new_vline =[{
        'type': 'line',
        'x0': x_coordinate,
        'x1': x_coordinate,
        'xref': 'x',
        'y0': 0,
        'y1': 1,
        'yref': f'y{y_axis_indicator} domain'
    }]
    
    updated_shapes = existing_shapes + new_vline
    figure_dict["layout"]["shapes"] =  updated_shapes
    return go.Figure(figure_dict)

usage:

import plotly.graph_objects as go
import math
def makefig():
    xs = list(range(200))
    fig=go.Figure()
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x/10) for x in xs],name="d1", yaxis="y"))
    fig.update_layout(yaxis=dict(domain=[0,0.3], showline=True, side="left", autorange=True))
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x/5) for x in xs],name="d2", yaxis="y2"))
    fig.update_layout(yaxis2=dict(domain=[0.3,0.6], showline=True, side="right", autorange=True))
    fig.add_trace(go.Scatter(mode="lines+markers",x=xs,y=[math.sin(x) for x in xs],name="d3", yaxis="y3"))
    fig.update_layout(yaxis3=dict(domain=[0.6,0.9], showline=True, side="left", autorange=True))
    fig.update_traces(showlegend=False)
    fig.update_layout(height=800)
    return fig

fig = makefig()

fig = add_vlines(fig, 22)
fig = add_vlines(fig, 44, "2")
fig = add_vlines(fig, 10, "3")
fig.show()

You might improve the quick&dirty function I came up with. One thing to consider is, that this approach will not work for figures with a single y axis. So it’s pretty much tailored to the example you gave above.

Also keep in mind that the y0 axis does not exist in plotly- I’ve never really understood why :slight_smile:
mrep v_line

1 Like