【问题标题】:Curve fitting with cubic spline三次样条曲线拟合
【发布时间】:2021-02-23 19:58:34
【问题描述】:

我正在尝试插入一个累积分布,例如i) 人数到 ii) 自有汽车的数量,表明例如前 20% 的人拥有超过 20% 的汽车——当然,100% 的人拥有 100% 的汽车。我也知道有例如1亿人,2亿辆汽车。

现在来看我的代码:

#import libraries (more than required here)
import pandas as pd
from scipy import interpolate
from scipy.interpolate import interp1d
from sympy import symbols, solve, Eq
import matplotlib.pyplot as plt
from matplotlib import pyplot as plt
%matplotlib inline
import plotly.express as px
from scipy import interpolate

curve=pd.read_excel('inputs.xlsx',sheet_name='inputdata')

输入数据:Curveplot(左侧累计人数 (x) // 右侧累计汽车 (y))

#Input data in list form (I am not sure how to interpolate from a list for the moment)
cumulatedpeople = [0, 0.453086, 0.772334, 0.950475, 0.978981, 0.999876, 0.999990, 1]
cumulatedcars= [0, 0.016356, 0.126713, 0.410482, 0.554976, 0.950073, 0.984913, 1]

x, y = points[:,0], points[:,1]
interpolation = interp1d(x, y, kind = 'cubic')

number_of_people_mn= 100000000

oneperson = 1 / number_of_people_mn
dataset = pd.DataFrame(range(number_of_people_mn + 1))
dataset.columns = ["nr_of_one_person"]
dataset.drop(dataset.index[:1], inplace=True)

#calculating the position of every single person on the cumulated x-axis (between 0 and 1)
dataset["cumulatedpeople"] = dataset["nr_of_one_person"] / number_of_people_mn

#finding the "cumulatedcars" to the "cumulatedpeople" via interpolation (between 0 and 1)
dataset["cumulatedcars"] = interpolation(dataset["cumulatedpeople"])

plt.plot(dataset["cumulatedpeople"], dataset["cumulatedcars"])
plt.legend(['Cubic interpolation'], loc = 'best')
plt.xlabel('Cumulated people')
plt.ylabel('Cumulated cars')
plt.title("People-to-car cumulated curve")
plt.show()

但是在查看实际情节时,我得到以下错误的结果:Cubic interpolation

事实上,曲线应该看起来几乎像来自具有完全相同输入数据的线性插值的曲线 - 但是这对于我的目的来说不够准确:Linear interpolation

我是否遗漏了任何相关步骤,或者从几乎看起来像线性插值的输入中获得准确插值的最佳方法是什么?

【问题讨论】:

  • 我想你在这里混合了一些概念。为什么你认为插值器不准确?它会遍历您的每个数据点,因此该模型实际上是完美的,对吧?问题在于您用于模型拟合的数据点之外发生了什么。这就是说,您的模型不可生成,主要问题是选择 interpolation 您会强制模型完全通过您的数据点。如果您放宽该要求(并且只适合二阶多项式,或使用 平滑 样条),结果会明显更好。

标签: python scipy interpolation curve-fitting


【解决方案1】:

简短回答:您的代码做对了,但数据不适合三次插值。

让我解释一下。这是我为清楚起见简化的代码

from scipy.interpolate import interp1d
from matplotlib import pyplot as plt

cumulatedpeople = [0, 0.453086, 0.772334, 0.950475, 0.978981, 0.999876, 0.999990, 1]
cumulatedcars= [0, 0.016356, 0.126713, 0.410482, 0.554976, 0.950073, 0.984913, 1]
interpolation = interp1d(cumulatedpeople, cumulatedcars, kind = 'cubic')

number_of_people_mn= 100#000000
cumppl = np.arange(number_of_people_mn + 1)/number_of_people_mn
cumcars = interpolation(cumppl)
plt.plot(cumppl, cumcars)
plt.plot(cumulatedpeople, cumulatedcars,'o')
plt.show()

注意最后几行——我在同一张图上绘制了插值结果和输入日期。这是结果

橙色点是原始数据,蓝色线是三次插值。插值器通过所有点,因此在技术上做正确的事情

显然它没有做你想做的事情

这种奇怪行为的原因主要在于右端有几个非常靠近的 x 点 - 插值器会产生大量摆动,试图拟合非常接近的点。

如果我从插值器中删除 两个 最右边的点:

interpolation = interp1d(cumulatedpeople[:-2], cumulatedcars[:-2], kind = 'cubic')

看起来更合理一点:

但仍然有人认为线性插值更好。现在左端的摆动是因为初始 x 点之间的间隙太大

这里的寓意是,只有当 x 点之间的间隙大致相同时,才应该真正使用三次插值

我认为你最好的选择是使用curve_fit之类的东西

相关讨论可以找到here

here 所解释的,特别是单调插值可以对您的数据产生良好的效果。在此处复制相关位,您可以将插值器替换为

from scipy.interpolate import pchip
interpolation = pchip(cumulatedpeople, cumulatedcars)

并获得一个体面的合身:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    相关资源
    最近更新 更多