Search code examples
chartsplotlyregressionvisualizationpolynomials

How can I add the slope of a specific point in a polynomial line in plotly


Let's say I have a polynomial regression in plotly that looks like that

enter image description here

Something along a code like this:

fig = px.scatter(
    x=final_df.index,
    y=final_df.nr_deaths, 
    trendline="lowess", #ols
    trendline_color_override="red",
    trendline_options=dict(frac=0.1),
    opacity=.5,
    title='Deaths per year'
)
fig.show()

How would I calculate the slope (= tangent) line on a specific point of the polynomial regression line?


Solution

  • Currently, this cannot be done within plotly alone. But you can achieve this by using other libraries for calculation and applying the results in the chart.

    The difficulty in this question lies in

      1. calculating the slope of the polynomial at a certain point
      1. calculating the x and y values for plotting them as lines

    For calculating the slopes at a certain point you can use numpy functionality. Afterwards you can just calculate the x and y values with python and plot them with plotly.

    poly_degree = 3
    
    
    y = df.col.values
    x = np.arange(0, len(y))
    x = x.reshape(-1, 1)
    
    fitted_params = np.polyfit(np.arange(0, len(y)), y, poly_degree )
    
    polynomials = np.poly1d(fitted_params)
    
    derivatives = np.polyder(polynomials)
    
    y_value_at_point = polynomials(x).flatten()
    
    slope_at_point = np.polyval(derivatives, np.arange(0, len(y)))
    
    

    For calculating the corresponding slope values (the necessary x values and y values) at a point, and plotting it in plotly you can do something like this:

    def draw_slope_line_at_point(fig, ind, x, y, slope_at_point, verbose=False):
        """Plot a line from an index at a specific point for x values, y values and their slopes"""
        
        
        y_low = (x[0] - x[ind]) * slope_at_point[ind] + y[ind]
        y_high = (x[-1] - x[ind]) * slope_at_point[ind] + y[ind]
        
        x_vals = [x[0], x[-1]]
        y_vals = [y_low, y_high]
    
        if verbose:
            print((x[0] - x[ind]))
            print(x[ind], x_vals, y_vals, y[ind],slope_at_point[ind])
        
        fig.add_trace(
                go.Scatter(
                    x=x_vals, 
                    y=y_vals, 
                    name="Tangent at point", 
                    line = dict(color='orange', width=2, dash='dash'),
                )
            )
        
        return x_vals, y_vals
    

    Calling it and adding annotation would look like this:

    for pt in [31]:
        draw_slope_line_at_point(
            fig, 
            x= np.arange(0, len(y)),
            y = y_value_at_point,
            slope_at_point=slope_at_point,
            ind = pt)
        
        fig.add_annotation(x=pt, y=y_value_at_point[pt],
                text=f'''Slope: {slope_at_point[pt]:.2f}\t {df.date.strftime('%Y-%m-%d')[pt]}''',
                showarrow=True,
                arrowhead=1)
    
    

    and then looking like that in the result:

    enter image description here

    enter image description here