Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Is there a way to add a horizontal line or vertical line similar to matplotlib? #143

Closed
helloitsjulian opened this issue Sep 20, 2019 · 27 comments

Comments

@helloitsjulian
Copy link

Something similar to this https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.axhline.html

Is there any intention for this to be added or should we go through accessing layout?

@indiana-nikel
Copy link

indiana-nikel commented Sep 25, 2019

Also wondering if this is in the works! Maybe something along the lines of:

.update_layout(hline=5)
.update_layout(vline=-1)

@nicolaskruchten
Copy link
Contributor

You can indeed add horizontal and vertical lines to plots in general, but we don't have a "facet-aware" way of doing this yet unfortunately, so this only really works well/easily with a single plot:

import plotly.express as px

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip")
fig.update_layout(shapes=[
    dict(
      type= 'line',
      yref= 'paper', y0= 0, y1= 1,
      xref= 'x', x0= 5, x1= 5
    )
])

This adds a vertical line at x=5

image

@indiana-nikel
Copy link

indiana-nikel commented Sep 26, 2019

I'm finding that if you add fig.show(config={"showLink":False}), you receive duplicate charts:

import plotly.express as px

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip")
fig.update_layout(shapes=[
    dict(
      type= 'line',
      yref= 'paper', y0= 0, y1= 1,
      xref= 'x', x0= 5, x1= 5
    )
])
fig.show(config={"showLink":False})

Is this behavior intended/built in to .update_layout(...)?

@nicolaskruchten
Copy link
Contributor

I'm not sure what you mean by "duplicate charts" here...? Also note that the "showLink: False" is redundant in Plotly.py v4, as this is the default :)

@nicolaskruchten
Copy link
Contributor

Ah, if you mean that fig.update_layout() returns fig then yes, this is intentional, so if you have a notebook cell that ends with this call, then fig.show() is optional. But in a single notebook cell, your code above should output only a single chart.

@vcmorini
Copy link

vcmorini commented Apr 15, 2020

Nice @nicolaskruchten ! Is there a way to add more than one xref or yref? Because my express px.scatter have multiple facets.

Answer: just add another dict in the shape list:

        fig.update_layout(shapes=[         # Line Diagonal
            dict(
                type="line",
                yref='y1',
                y0=200,
                y1=200,
                xref='x1',
                x0=min_x*0.8,
                x1=max_x*1.2,
                line=dict(
                    color="Red",
                    width=4,
                    dash="dashdot",
                )
        ),
            dict(
                type="line",
                yref='y2',
                y0=200,
                y1=200,
                xref='x2',
                x0=min_x*0.8,
                x1=max_x*1.2,
                line=dict(
                    color="Red",
                    width=4,
                    dash="dashdot",
                )
        )
        ])

@jvschoen
Copy link

jvschoen commented Apr 17, 2020

we don't have a "facet-aware" way of doing this yet unfortunately

@nicolaskruchten Is there any plans of implementing this? It's very useful when trying to display thresholds values or summary statistics (mean, median) across facets (Either constant value across all facets, or generated upon grouping for facets). yintercept or xintercept values could be used and just utilize the trend line functionality to just display a constant trend.

@nicolaskruchten
Copy link
Contributor

Yes, we will implement a nice .add_hlines() and .add_vlines() in an upcoming version, as the current way is pretty clunky.

@nicolaskruchten
Copy link
Contributor

You can follow the development here: plotly/plotly.py#2141

@jvschoen
Copy link

        fig.update_layout(shapes=[         # Line Diagonal
            dict(
                type="line",
                yref='y1',
                y0=200,
                y1=200,
                xref='x1',
                x0=min_x*0.8,
                x1=max_x*1.2,
                line=dict(
                    color="Red",
                    width=4,
                    dash="dashdot",
                )
        ),
            dict(
                type="line",
                yref='y2',
                y0=200,
                y1=200,
                xref='x2',
                x0=min_x*0.8,
                x1=max_x*1.2,
                line=dict(
                    color="Red",
                    width=4,
                    dash="dashdot",
                )
        )
        ])

@vcmorini Thanks for the idea. A more generalized solution based off this:

fig.layout.update(
    shapes=[{'type': 'line',
                    'y0':y_intercept,' y1': y_intercept,
                    'x0':str(df.x_value.min()), 'x1':str(df.x_value.max()),
                    'xref':'x' + str(i + 1), 'yref':'y' + str(i + 1),
                    'line': {'color': 'black', 'width': 1, 'dash': 'dot'}} 
               for i, member in enumerate(pd.unique(df.x_value))])

@liweipace
Copy link

I was using go.Scatter to generate a plot. One solution, which is also clunky, for go is adding another plot with

trace2 = go.Scatter(
       x = [df.Date.min(),df.Date.max()],
       y = [df.Close.mean(),df.Close.mean()]
       mode = "lines",
       name = "Close price",
       marker = dict(color = 'rgba(80, 26, 80, 0.8)')
)

.add_hlines() is just finding the x_min and x_max with two fixed y
.add_vlines() vice versa.
BTW, looking forward to upcoming versions!

@vcmorini
Copy link

vcmorini commented May 5, 2020

It somehow bugs with Plotly Express with multiple facets:

Untitled

                    {
                        "type": "line",
                        "yref": "paper",
                        "y0": 0,
                        "y1": 50,
                        "xref": "x{}".format(i + 1),
                        "x0": -200,
                        "x1": -200,
                        "line": {
                            "color":"Red",
                            "width":1,
                        }
                    }

Workaround:

                counts, _ = np.histogram(df["data"], bins=bins)
                max_bin_count = counts.max()
....
                    {
                        "type": "line",
                        "yref": "y{}".format(i + 1),
                        "y0": 0,
                        "y1": max_bin_count,
                        "xref": "x{}".format(i + 1),
                        "x0": -200,
                        "x1": -200,
                        "line": {
                            "color":"Red",
                            "width":1,
                        }
                    }

Ref: https://plotly.com/python/histograms/#accessing-the-counts-yaxis-values

@awheels-ds
Copy link

Is there a way to add a label on the x-axis for each vertical line?

@mariliarosa4
Copy link

I would like to know too.

@SpyderRivera
Copy link

In R, there is an easy way that could be adapted to python.

vline <- function(x = 0, color = "red") {
  list(
    type = "line", 
    y0 = 0, 
    y1 = 1, 
    yref = "paper",
    x0 = x, 
    x1 = x, 
    line = list(color = color)
  )
}

hline <- function(y = 0, color = "blue") {
  list(
    type = "line", 
    x0 = 0, 
    x1 = 1, 
    xref = "paper",
    y0 = y, 
    y1 = y, 
    line = list(color = color)
  )
}

plot_ly() %>%
  layout(shapes = list(vline(4), hline(5)))

image

@nicolaskruchten
Copy link
Contributor

@SpyderRivera thanks!

We've got a PR almost ready to merge that introduces a fig.add_hline(y=...) API which will make this much easier... should be out in Plotly.py 4.11 by the end of September. Stay tuned! :)

@nickmuchi87
Copy link

added a horizontal line using fig.add_shape(), however, the line was disabled once i specified the 'seaborn' template, is there a way around this? @nicolaskruchten, thanks in advance

@victorsalles
Copy link

@SpyderRivera thanks!

We've got a PR almost ready to merge that introduces a fig.add_hline(y=...) API which will make this much easier... should be out in Plotly.py 4.11 by the end of September. Stay tuned! :)

great to hear that! I'm looking forward for 4.11 release! are there any dates already?

@nicolaskruchten
Copy link
Contributor

This feature has been moved to 4.12, but I estimate that within 2 weeks we should have it out :)

@nicolaskruchten
Copy link
Contributor

OK took longer than two weeks, sorry folks :) But it's out now in 4.12! https://community.plotly.com/t/announcing-plotly-py-4-12-horizontal-and-vertical-lines-and-rectangles/46783

@HugoGit39
Copy link

HugoGit39 commented Dec 29, 2020

@nicolaskruchten @SpyderRivera so the hline method works in a single line plot. However I have 2 lines with 2 differnt scales..of which I want to add an hline according to the 2nd rrighter scale...though this doesnt work:

plot %>% layout(yaxis2 = list(shapes = list(hline(10))))

Any help?

plot%>% layout(shapes = list(hline(10))) does work but only for the left 1st scale

@Okroshiashvili
Copy link

OK took longer than two weeks, sorry folks :) But it's out now in 4.12! https://community.plotly.com/t/announcing-plotly-py-4-12-horizontal-and-vertical-lines-and-rectangles/46783

Guys, you all rock. Thanks for your dedication and commitment 🎉

@martppa
Copy link

martppa commented May 11, 2021

Hi! I have found add_hline doesn't work when ploting it with dash :(

@nicolaskruchten
Copy link
Contributor

This will work with recent versions of Dash: I recommend upgrading to the latest, which is 1.20 :)

@martppa
Copy link

martppa commented May 11, 2021

Hi @nicolaskruchten , thanks for the support. Well, I do actually have the latest version of dash, I also noted I am not using plotly express, I am using graphic objects. But still can't get the add_hline or add_vline working.

@self.app.callback(
    Output(_id, 'figure'),
    [Input(_id, 'id'),
     Input('update_interval', 'n_intervals')]
)
def callback(component_id, _):
    graph = next(component for component in components if component.id == component_id)
    figure = go.Figure()
    lines = graph.get_lines()
    for line in lines:
        if isinstance(line, VectorLine):
            figure.add_trace(go.Scatter(
                x=[point[0] for point in line.vector],
                y=[point[1] for point in line.vector],
                name=line.title,
                mode='lines',
            ))
        if isinstance(line, HLine):
            figure.add_hline(line.y,
                             annotation_text=line.title,
                             annotation_position="bottom right")
        if isinstance(line, VLine):
            figure.add_vline(line.x)

    figure.layout = go.Layout(title=graph.title, height=700)
    return figure

The thing is, if I do a figure.show() I get the horizontal line.

@martppa
Copy link

martppa commented May 11, 2021

Solved, The layout must be set before hlines and vlines are added.

@nicolaskruchten
Copy link
Contributor

To be precise: setting the layout to some value will overwrite what add_*line added to the pre-existing layout, yes :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests