确定哪个点子集最接近多项式

Determine which subset of points follows a polynomial most closely

我目前正在尝试根据它们的行为对一堆河流进行分类。许多河流的行为与二次多项式非常相似。

但是,有些河流在某些区域偏离了这种模式。

我想通过计算所有点与简单多项式的距离来对其进行分类。所以它基本上看起来像这样:

但是为了能够做到这一点,我必须只计算那些 "normal behavior" 的点的多项式。否则我的多项式将移动到发散行为的方向,我无法正确计算距离。

这是一些示例数据。

x_test = [-150,-140,-130,-120,-110,-100,-90,-80,-70,-60,-50,-40,-30,-20,-10,0,10,20,30,40,50,60,70,70,80,80,90,90,100,100]
y_test = [0.1,0.11,0.2,0.25,0.25,0.4,0.5,0.4,0.45,0.6,0.5,0.5,0.6,0.6,0.7, 0.7,0.65,0.8,0.85,0.8,1,1,1.2,0.8,1.4,0.75,1.4,0.7,2,0.5]

我可以用 numpy 从中创建一个多项式。

fit = np.polyfit(x_test, y_test, deg=2, full=True)
polynom = np.poly1d(fit[0]) 
simulated_data = polynom(x)

绘制时,我得到以下信息:

ax = plt.gca()
ax.scatter(x_test,y_test)
ax.plot(x, simulated_data)

如您所见,多项式略微向下移动,这是由此处标记为黑色的点引起的:

有没有一种直接的方法可以找到那些不遵循主要趋势的点并排除它们以创建多项式?

这看起来更像是一个 AI 问题,而不是一个简单的拟合问题:您个人如何决定什么不适合 - 特别是在您的第二个发散图中,如果您忽略较大的曲线,则第一条较短的向上曲线看起来是多项式的?

您只需要 3 个点来计算一个 2 多项式:如何计算 3 个水平间隔良好的点的 all/many 采样的曲线(不一定相信第一个或最后一个点)并查看哪个产生最少的异常值 - 比其他 90% 的点更远?

然后您可以根据剩余的非离群点计算曲线,并检查它是否适合您的普通计算曲线。

编辑:'well spaced' 的意思是每个水平三分之一的点各有一个点 - 使用三个挤在一起的 ooint 来尝试推断其他点是没有意义的。此外,从您提供的数据的外观来看,您想要一条围绕原点开始向上的曲线,因此无论如何您都可以过滤一些随机生成的曲线。

编辑:离群值建议草率 - 如果你的数据在最后变得更宽,就像喇叭一样,你有许多合理的拟合,所以它只有在它有明显的马刺的地方你才能有一个明确的标记异常值。如果您计算点的直方图与每条随​​机曲线的距离,您可以扫描直方图切线中的肩部和不对称性,使其远离钟形曲线,并在该点切分异常值。

从根本上说,我认为数据可能过于复杂,无法进行计算机辅助分析,除非你突破计算机视觉技术:让计算机尽其所能,然后目视检查带注释的图表,看看是否你同意。

它也可能有助于绘制垂直轴的日志,所以你处理的是直线。

一种可能有效的方法是将点聚集到 "main" 和 "offshoot" 分支中,假设有两个分支并且一个包含的点多于另一个。之后,每个簇都可用于拟合在河流分支合并点交叉的多项式。这甚至可以通过使用多项式来迭代几次,通过使用点到多项式的距离作为距离度量而不是聚类算法使用的距离来更好地分类到聚类中。

常见的 k-means algorithm is probably not a good fit as the clusters are not clustered around points but curves. Algorithms like DBSCAN 可能效果更好,因为它们计算了给定点附近点的密度,这更类似于我们人类在看到上面示例数据集中的模式时所做的事情.

这可能看起来像这样(无效代码):

points = (x_test, y_test)
labels = dbscan(points, k=2, labels=("main", "offshoot"))
polynomial_main = fit_polynomial([points[x.index] for x in labels if x.label = "main"])
polynomial_off = fit_polynomial([points[x.index] for x in labels if x.label = "offshoot"])

# optionally, purely distance based clustering
# might also use different clustering algorithm using distance as measure
points_main = [p for p in points if distance(p, polynomial_main) < distance(p, polynomial_off)]
points_off = [p for p in points if distance(p, polynomial_off) < distance(p, polynomial_main)]
polynomial_main = fit_polynomial(points_main)
polynomial_off = fit_polynomial(points_off)