plotly.py icon indicating copy to clipboard operation
plotly.py copied to clipboard

Error on using add_vline with text annotation for data with date-time x axis

Open MR0205 opened this issue 4 years ago • 22 comments

Hi, I am trying to use the library for a simple visualisation for the first time, and I stumbled upon supposedly a bug trying to draw a vertical line with text annotation on a graph with a date-time x axis.

import plotly.express as px
df = px.data.stocks(indexed=True)
fig = px.line(df)
fig.add_vline(x="2018-09-24", annotation_text="test" )
fig.show()

I get the following error message:

TypeError: unsupported operand type(s) for +: 'int' and 'str'

Could anyone please help me confirm that it is indeed a bug, my version is '4.14.1' ? Thank you.

MR0205 avatar Feb 05 '21 17:02 MR0205

Yep, definitely a bug! Thanks for the clear and reproducible report :)

I'm not immediately clear why we need to compute the midpoint of anything here https://github.com/plotly/plotly.py/blob/03979d105c65dda3df3a155322eaff18f203b03f/packages/python/plotly/plotly/shapeannotation.py#L58 but this is the problem. Ideally we would need to rework this to use the (x|y)anchor attribute instead of trying to do math on the coordinates in the Python layer.

nicolaskruchten avatar Feb 05 '21 17:02 nicolaskruchten

Hi @MR0205,

If you're looking for a temporary workaround, you can get the annotation to work by converting your x value into milliseconds since epoch as follows:

import plotly.express as px
import datetime
df = px.data.stocks(indexed=True)
fig = px.line(df)
fig.add_vline(x=datetime.datetime.strptime("2018-09-24", "%Y-%m-%d").timestamp() * 1000, annotation_text="test" )
fig.show()

awrobel1 avatar Feb 13 '21 17:02 awrobel1

@awrobel1 Thanks for your idea, I thought on using a rectangle as a replacement, where you would make both edges start at the limit of a single day. Not sure though that will completely imitate the vertical line, due to possible problem when zooming.

MR0205 avatar Feb 18 '21 07:02 MR0205

Following this too!

YukunYangNPF avatar Mar 11 '21 02:03 YukunYangNPF

The above suggestion from @awrobel1 was great for me. In my case I simplified a tad with x=datetime.datetime(2021,5,27).timestamp() * 1000. No need to convert from a string first in this case.

tomshaffner avatar Jun 02 '21 22:06 tomshaffner

Yes, after looking into this a bit more, the implementation assumes the data are types that can have arithmetic performed on them. It needs to do this in order to compute where to put the annotation. This bug probably got through because to compute where an annotation is placed, it only needs to find the minimum, maximum or mean of the extreme coordinate values. min and max are defined for strings, so dates don't break it for positions like 'left', 'right', etc., but I think the fact that it works is just luck: the strings often sort the same as the dates. This points to 2 things that could be fixed at the higher level:

  • when doing arithmetic on data, operators should be chosen corresponding to the type of the axis: e.g., if the axis type is 'date' then the values should be converted to milliseconds before arithmetic is performed
  • rather than having anchored annotations only available when using add_hline and the like, maybe have them available when using add_shape? Then add_hline, etc. are just special cases of add_shape.

nicholas-esterer avatar Aug 02 '21 18:08 nicholas-esterer

Hi all,

I have a rather weird problem that might be connected to this issue.

I deployed an app using streamlit (V'1.2.0')

I use Plotly (V'5.3.1') to create a plot in the app.

The plot is a timeline, and there is a vertical line in it. Based on this bug, i caluclate the postion of the of the line the following: end_time_prelim = datetime.strptime(df['end_time'].max(), "%Y-%m-%d %H:%M:%S").timestamp() * 1000

And then use this in fig.add_vline(x=end_time_prelim) This works fine when I run the code locally. However, in the online version, the line is at a shifted different position and slightly shifted.

Is this known? Thanks a lot :-)

Not sure if helpful, but this would be the app: https://share.streamlit.io/claudiabehnke86/tournamentcalculator/tourcalc/theapp.py

ClaudiaBehnke86 avatar Jan 05 '22 14:01 ClaudiaBehnke86

Hi @ClaudiaBehnke86,

Not sure, but you might be having trouble with time zones? When you run time timestamp calculation I believe that its looking for the timestamp based on your current timezone. So locally, everything works intuitively, but when you host your code in the cloud you start running on a UTC server and the times all seem to jump.

If this is the problem you just need to make sure to adjust your line based on the difference between your current time and UTC, by adding/subtracting milliseconds or using timezone aware times.

Also, make sure your code can handle time shifts for daylight savings time if relevant!

awrobel1 avatar Jan 05 '22 17:01 awrobel1

Yes, this makes a lot of sense :-D. Thanks for the hint!

ClaudiaBehnke86 avatar Jan 05 '22 18:01 ClaudiaBehnke86

Hi @MR0205,

If you're looking for a temporary workaround, you can get the annotation to work by converting your x value into milliseconds since epoch as follows:

import plotly.express as px
import datetime
df = px.data.stocks(indexed=True)
fig = px.line(df)
fig.add_vline(x=datetime.datetime.strptime("2018-09-24", "%Y-%m-%d").timestamp() * 1000, annotation_text="test" )
fig.show()

Thanks for this! I had the same issue and it worked for me!

alexrblohm avatar Mar 24 '22 14:03 alexrblohm

Hi, how can I do similar things when x are strings (labels) instead of dates or numbers?

fzyzcjy avatar Jun 15 '22 12:06 fzyzcjy

This is still a problem for all categorial data on the x axis. Is there any solution for when x are strings?

mateomontero01 avatar Jul 21 '22 03:07 mateomontero01

Yup this is still a very real and weird problem because if you try to do the annotation within the add_vline arguments you'll get the exception:

fig = px.line(df, x="string_category", y="some_values")
fig.add_vline(
    x="string_value",
    line_dash="dot",
    annotation_text="some_annotation"
)

but if you do it with an add_annotation function it works fine

fig = px.line(df, x="string_category", y="some_values")
fig.add_vline(
    x="string_value",
    line_dash="dot"
)
fig.add_annotation(x="string_value",text="some_annotation")

jasonsross avatar Oct 26 '22 19:10 jasonsross

Just got this exact error today, removing the annotation_text parameter works

BremondThomas avatar Jul 09 '23 13:07 BremondThomas

Same error! For both, annotation_text="..." and label=dict(text="...") inside add_vline(...)

NikOcaml avatar Jul 10 '23 15:07 NikOcaml

Another solution is to add a box with a specific width:

from datetime import timedelta

fig.add_vrect(
        x0=epoch,
        x1=epoch + timedelta(minutes=1),
        annotation_text="Text laebel",
        fillcolor=color,
        line_width=2,
        line_color=color,
    )

ChristopherRabotin avatar Sep 18 '23 20:09 ChristopherRabotin

Got the same problem today as the one before using add_vline with the annotation_text parameter and I used this workaround.

Yup this is still a very real and weird problem because if you try to do the annotation within the add_vline arguments you'll get the exception:

fig = px.line(df, x="string_category", y="some_values")
fig.add_vline(
    x="string_value",
    line_dash="dot",
    annotation_text="some_annotation"
)

but if you do it with an add_annotation function it works fine

fig = px.line(df, x="string_category", y="some_values")
fig.add_vline(
    x="string_value",
    line_dash="dot"
)
fig.add_annotation(x="string_value",text="some_annotation")

Dario-Mantegazza avatar Feb 15 '24 10:02 Dario-Mantegazza

Can confirm this problem. Is it planned to be fixed in any upcoming release? It's been around for a while ;)

jtwild avatar Feb 23 '24 14:02 jtwild

What happens if you convert the dates to UNIX timestamps?

archmoj avatar Feb 23 '24 17:02 archmoj

Oh gosh, I don’t think we want to recommend Unix timestamps… we’d need them to be in milliseconds, and quite likely this will give time zone problems due to our weird backward-compatibility hacks on the plotlyjs side

alexcjohnson avatar Feb 23 '24 18:02 alexcjohnson

What happens if you convert the dates to UNIX timestamps?

I will try to test it on the week of the 11th of March as before I don't have time. I hope I will remember XD

Dario-Mantegazza avatar Feb 23 '24 21:02 Dario-Mantegazza