首页 > 解决方案 > 三次样条曲线拟合

问题描述

我试图插入一个累积分布,例如 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')

输入数据:曲线图(左侧的累积人数(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()

但是,在查看实际图时,我得到以下错误的结果:三次插值

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

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

标签: pythonscipyinterpolationcurve-fitting

解决方案


简短的回答:您的代码正在做正确的事情,但数据不适合三次插值。

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

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()

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

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

显然它没有做你想做的事

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

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

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

它看起来更合理一点: 样条2

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

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

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

相关讨论可以在这里找到

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

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

并得到一个体面的合身: 单调


推荐阅读