4

I am trying to find and annotate the intersection point of two-line using Plotly. I know we can use (plt.plot(*intersection.xy,'ko')) to get the intersection point in Mathplotlib, but how can do it in plotly or if it can be done.

import numpy as np
import pandas as pd
import plotly.express as px

test = pd.merge(ps,uts,on = 'Time')
print(test)

time = test['Time']
omfr = test['Orifice Mass Flow Rate']
qab = test['Qab']

fig = px.line(test, x=time, y=[omfr,qab])
fig.update_layout( title='Pipe stress and UTS (MPa)',xaxis_title="Time (s)",yaxis_title="Pipe stress and UTS (MPa)",
                  hovermode='x')

Output of the code: Output of the code

Intersection point: Intersection point

vestland
  • 55,229
  • 37
  • 187
  • 305
  • 1
    @Rajaa Monnapillai My initial suggestion had some errors in the code snippet (bad copy&paste..?) Now it works as intended. Let me know how it works for you. – vestland Dec 07 '20 at 20:43
  • 1
    How did my suggestion work out for you? – vestland Dec 09 '20 at 18:52
  • Hi @vestland, it worked for me and what I was looking for. Btw, how would you annotate the x and the y value instead of stating "intersection". would you just change the value of the name in fig.add_trace to the intersection values? – Rajaa Monnapillai Dec 11 '20 at 18:36
  • 1
    I can add that in an edit. Would you consider marking my suggestion as the accepted answer? By now you've also gained enough reputation points to upvote questions and answers. If you find them useful. – vestland Dec 11 '20 at 18:49
  • 1
    Will do. 5 minutes. – vestland Dec 15 '20 at 22:15
  • 1
    That should do it! – vestland Dec 15 '20 at 22:24
  • @vestland, I have done that and thanks, this is what I wanted. Just wondering how to change the coordinates of the text? – Rajaa Monnapillai Dec 15 '20 at 22:34
  • Ah, I see. You don't specify the coordinates of the text directly, but rather the offset of the text relative to the intersect using other values for `ax=0` and `ay=-100`. – vestland Dec 15 '20 at 22:41

1 Answers1

4

Annotating and showing the intersection is easy. Finding it is the hard part, and my suggestion in that regard builds directly on the contributions in the post How do I compute the intersection point of two lines. I'll include a few lines on the details in my suggestion when I find the time. For now, the complete code snippet at the end of my answer will produce the following figure using this dataset:

Data

   x  y1  y2
0  1   1  11.00
1  2   8  14.59
2  3  27  21.21
3  4  64  31.11

Plot

enter image description here

Edit - Annotation

If you'd like to change the text annotation, just change

text="intersect"

... to something like:

text = 'lines intersect at x = ' + str(round(x[0], 2)) + ' and y = ' + str(round(y[0], 2))

Result:

enter image description here

Complete code

import pandas as pd
import plotly.graph_objects as go
import numpy as np
# import dash


# sample dataframe
df = pd.DataFrame()
df['x'] = np.arange(4) +1
df['y1'] = df['x']**3
df['y2'] = [10+val**2.2 for val in df['x']]

# intersection stuff
def _rect_inter_inner(x1,x2):
    n1=x1.shape[0]-1
    n2=x2.shape[0]-1
    X1=np.c_[x1[:-1],x1[1:]]
    X2=np.c_[x2[:-1],x2[1:]]
    S1=np.tile(X1.min(axis=1),(n2,1)).T
    S2=np.tile(X2.max(axis=1),(n1,1))
    S3=np.tile(X1.max(axis=1),(n2,1)).T
    S4=np.tile(X2.min(axis=1),(n1,1))
    return S1,S2,S3,S4

def _rectangle_intersection_(x1,y1,x2,y2):
    S1,S2,S3,S4=_rect_inter_inner(x1,x2)
    S5,S6,S7,S8=_rect_inter_inner(y1,y2)

    C1=np.less_equal(S1,S2)
    C2=np.greater_equal(S3,S4)
    C3=np.less_equal(S5,S6)
    C4=np.greater_equal(S7,S8)

    ii,jj=np.nonzero(C1 & C2 & C3 & C4)
    return ii,jj

def intersection(x1,y1,x2,y2):

    ii,jj=_rectangle_intersection_(x1,y1,x2,y2)
    n=len(ii)

    dxy1=np.diff(np.c_[x1,y1],axis=0)
    dxy2=np.diff(np.c_[x2,y2],axis=0)

    T=np.zeros((4,n))
    AA=np.zeros((4,4,n))
    AA[0:2,2,:]=-1
    AA[2:4,3,:]=-1
    AA[0::2,0,:]=dxy1[ii,:].T
    AA[1::2,1,:]=dxy2[jj,:].T

    BB=np.zeros((4,n))
    BB[0,:]=-x1[ii].ravel()
    BB[1,:]=-x2[jj].ravel()
    BB[2,:]=-y1[ii].ravel()
    BB[3,:]=-y2[jj].ravel()

    for i in range(n):
        try:
            T[:,i]=np.linalg.solve(AA[:,:,i],BB[:,i])
        except:
            T[:,i]=np.NaN


    in_range= (T[0,:] >=0) & (T[1,:] >=0) & (T[0,:] <=1) & (T[1,:] <=1)

    xy0=T[2:,in_range]
    xy0=xy0.T
    return xy0[:,0],xy0[:,1]

# plotly figure
x,y=intersection(np.array(df['x'].values.astype('float')),np.array(df['y1'].values.astype('float')),
                 np.array(df['x'].values.astype('float')),np.array(df['y2'].values.astype('float')))

fig = go.Figure(data=go.Scatter(x=df['x'], y=df['y1'], mode = 'lines'))
fig.add_traces(go.Scatter(x=df['x'], y=df['y2'], mode = 'lines'))
fig.add_traces(go.Scatter(x=x, y=y,
                          mode = 'markers',
                          marker=dict(line=dict(color='black', width = 2),
                                      symbol = 'diamond',
                                      size = 14,
                                      color = 'rgba(255, 255, 0, 0.6)'),
                         name = 'intersect'),
              )
fig.add_annotation(x=x[0], y=y[0],
                    # text="intersect",
                    text = 'lines intersect at x = ' + str(round(x[0], 2)) + ' and y = ' + str(round(y[0], 2)),
                    font=dict(family="sans serif",
                                  size=18,
                                  color="black"),
                    ax=0,
                    ay=-100,
                    showarrow=True,
                    arrowhead=1)

fig.show()
vestland
  • 55,229
  • 37
  • 187
  • 305