在Matplotlib中仿真Excel的“光滑曲线散点图”样条函数3点

我试图效仿Excel的

插入>散点图>具有平滑线条和标记的散点图

命令在Matplotlib

scipy函数interpolate创build了一个类似的效果,以及如何在这里简单地实现一些很好的例子: 如何在matplotlib中绘制三次样条曲线

然而,Excel的样条algorithm也可以通过三个点(例如x = [0,1,2] y = [4,2,1])生成一条平滑的曲线。 而且不可能用三次样条来做到这一点。

我见过的讨论表明,Excelalgorithm使用Catmull-Rom样条曲线; 但是不太了解这些,或者他们如何适应Matplotlib: http ://answers.microsoft.com/en-us/office/forum/office_2007-excel/how-does-excel-plot-smooth-curves / c751e8ff-9f99-4ac7-a74a-fba41ac80300

有没有一种简单的方法来修改上面的例子,通过三个或更多点使用插值库实现平滑的曲线?

非常感谢

现在,您可能已经find了Centripetal Catmull-Rom样条的维基百科页面,但是如果您还没有find,则会包含以下示例代码:

import numpy import matplotlib.pyplot as plt def CatmullRomSpline(P0, P1, P2, P3, nPoints=100): """ P0, P1, P2, and P3 should be (x,y) point pairs that define the Catmull-Rom spline. nPoints is the number of points to include in this curve segment. """ # Convert the points to numpy so that we can do array multiplication P0, P1, P2, P3 = map(numpy.array, [P0, P1, P2, P3]) # Calculate t0 to t4 alpha = 0.5 def tj(ti, Pi, Pj): xi, yi = Pi xj, yj = Pj return ( ( (xj-xi)**2 + (yj-yi)**2 )**0.5 )**alpha + ti t0 = 0 t1 = tj(t0, P0, P1) t2 = tj(t1, P1, P2) t3 = tj(t2, P2, P3) # Only calculate points between P1 and P2 t = numpy.linspace(t1,t2,nPoints) # Reshape so that we can multiply by the points P0 to P3 # and get a point for each value of t. t = t.reshape(len(t),1) A1 = (t1-t)/(t1-t0)*P0 + (t-t0)/(t1-t0)*P1 A2 = (t2-t)/(t2-t1)*P1 + (t-t1)/(t2-t1)*P2 A3 = (t3-t)/(t3-t2)*P2 + (t-t2)/(t3-t2)*P3 B1 = (t2-t)/(t2-t0)*A1 + (t-t0)/(t2-t0)*A2 B2 = (t3-t)/(t3-t1)*A2 + (t-t1)/(t3-t1)*A3 C = (t2-t)/(t2-t1)*B1 + (t-t1)/(t2-t1)*B2 return C def CatmullRomChain(P): """ Calculate Catmull Rom for a chain of points and return the combined curve. """ sz = len(P) # The curve C will contain an array of (x,y) points. C = [] for i in range(sz-3): c = CatmullRomSpline(P[i], P[i+1], P[i+2], P[i+3]) C.extend(c) return C 

这很好地计算n >= 4点的插值,如下所示:

 points = [[0,1.5],[2,2],[3,1],[4,0.5],[5,1],[6,2],[7,3]] c = CatmullRomChain(points) px, py = zip(*points) x, y = zip(*c) plt.plot(x, y) plt.plot(px, py, 'or') 

导致这个matplotlib图像:

在这里输入图像说明

更新:

另外,有一个BarycentricInterpolator scipy.interpolate函数BarycentricInterpolator似乎做您正在寻找的内容。 对于只有3个数据点的情况,使用和运行起来相当简单。

 from scipy.interpolate import BarycentricInterpolator # create some data points points1 = [[0, 2], [1, 4], [2, -2], [3, 6], [4, 2]] points2 = [[1, 1], [2, 5], [3, -1]] # put data into x, y tuples x1, y1 =zip(*points1) x2, y2 = zip(*points2) # create the interpolator bci1 = BarycentricInterpolator(x1, y1) bci2 = BarycentricInterpolator(x2, y2) # define dense x-axis for interpolating over x1_new = np.linspace(min(x1), max(x1), 1000) x2_new = np.linspace(min(x2), max(x2), 1000) # plot it all plt.plot(x1, y1, 'o') plt.plot(x2, y2, 'o') plt.plot(x1_new, bci1(x1_new)) plt.plot(x2_new, bci2(x2_new)) plt.xlim(-1, 5) 

在这里输入图像说明

更新2

scipy另一个select是通过Akima1DInterpolator akima插值。 它与重心一样易于实现,但具有避免数据集边缘的大振荡的优点。 以下是一些testing用例,展示了迄今为止所要求的所有标准。

 from scipy.interpolate import Akima1DInterpolator x1, y1 = np.arange(13), np.random.randint(-10, 10, 13) x2, y2 = [0,2,3,6,12], [100,50,30,18,14] x3, y3 = [4, 6, 8], [60, 80, 40] akima1 = Akima1DInterpolator(x1, y1) akima2 = Akima1DInterpolator(x2, y2) akima3 = Akima1DInterpolator(x3, y3) x1_new = np.linspace(min(x1), max(x1), 1000) x2_new = np.linspace(min(x2), max(x2), 1000) x3_new = np.linspace(min(x3), max(x3), 1000) plt.plot(x1, y1, 'bo') plt.plot(x2, y2, 'go') plt.plot(x3, y3, 'ro') plt.plot(x1_new, akima1(x1_new), 'b', label='random points') plt.plot(x2_new, akima2(x2_new), 'g', label='exponential') plt.plot(x3_new, akima3(x3_new), 'r', label='3 points') plt.xlim(-1, 15) plt.ylim(-10, 110) plt.legend(loc='best') 

在这里输入图像说明

@Lanery:回复:更新2:最好的只是变得更好!

必须将列表x2,y2,x3,y3重新定义为numpy数组,以让您的示例在我的系统(Spyder / Python 2.7)上工作:

 x2 = np.array([0,2,3,6,12]) y2 = np.array([100,50,30,18,14]) x3 = np.array([4, 6, 8]) y3 = np.array([60, 80, 40]) 

但现在像梦一样工作! 非常感谢您的专业知识和明确的解释。