Search code examples
pythonmatplotlibtrendline

How to draw a bottoming out curved trendline


I want to draw a trendline that follows a few points nicely. It is supposed to be a kind of 'average' so it doesn't have to hit the points exactly, but the shape has to be a smooth arc. I am having trouble getting Python or Excel to get exactly what I want. Here is an example code that I'm using,

x = np.array([6,8,10,12,16,24,32])
y = np.array([934,792,744,710,699,686,681])

#create scatterplot
plt.scatter(x, y)

#calculate equation for quadratic trendline
z = np.polyfit(x, y, 2)
p = np.poly1d(z)

#add trendline to plot
plt.plot(x, p(x))

which gives a output like this

whereas what I'm looking for is something like this Here's another example, where the line is sharper and doesn't bottom out like the previous enter image description here

Does anyone know how this can be done? When I try it on Excel it seems like an 'equation' is needed but I'm not sure how to get to this equation.

Edit: the curve I am looking for has to be monotonic decreasing and concave


Solution

  • To fit a curve with a desired shape, use scipy.optimize.curve_fit. Since you want a generic 1/x curve, you can use a/(x+b) + c, where a adjusts the curvature, b adjusts the left-right position, and c adjusts the up-down position. You can see how these parameters change the shape using this Desmos plot.

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    plt.close("all")
    
    def func(x, a, b, c):
        return a/(x+b) + c
    
    x = np.array([6,8,10,12,16,24,32])
    y = np.array([934,792,744,710,699,686,681])
    
    popt, _ = curve_fit(func, x, y)
    
    xfine = np.linspace(x.min(), x.max(), 50)
    yfit = func(xfine, *popt)
    
    fig, ax = plt.subplots()
    ax.plot(x, y, ".")
    ax.plot(xfine, yfit)
    fig.show()
    

    enter image description here