使用 python 3.x 的一维插值

1-D interpolation using python 3.x

我有一个看起来像 S 形图但相对于垂直线翻转的数据。

但该图是绘制一维数据而不是某种函数的结果。

我的目标是找到 y 值为 50% 时的 x 值。如您所见,当 y 刚好在 50% 时没有数据点。 我想到了插值。但我不确定当 y 值为 50% 时插值是否能让我找到 x 值。所以我的问题是 1) 当 y 为 50% 时,您可以使用插值法找到 x 吗?或 2) 您是否需要将数据拟合到某种函数中?

下面是我目前的代码

import numpy as np
import matplotlib.pyplot as plt


my_x = [4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66]

my_y_raw=np.array([0.99470977497817203, 0.99434995886145172, 0.98974611323163653, 0.961630837657524, 0.99327633558441175, 0.99338952769251909, 0.99428263292577534, 0.98690514212711611, 0.99111667721533181, 0.99149418924880861, 0.99133773062680464, 0.99143506380003499, 0.99151080464011454, 0.99268261743308517, 0.99289757252812316, 0.99100207861144063, 0.99157171773324027, 0.99112571824824358, 0.99031608691035722, 0.98978104266076905, 0.989782674787969, 0.98897835092187614, 0.98517540405423909, 0.98308943666187076, 0.96081810781994603, 0.85563541881892147, 0.61570811548079107, 0.33076276040577052, 0.14655134838124245, 0.076853147122142126, 0.035831324928136087, 0.021344669212790181])
my_y=my_y_raw/np.max(my_y_raw)

plt.plot(my_x, my_y,color='k', markersize=40)
plt.scatter(my_x,my_y,marker='*',label="myplot", color='k', edgecolor='k', linewidth=1,facecolors='none',s=50)
plt.legend(loc="lower left")
plt.xlim([4,102])
plt.show()

如您所说,您的数据看起来像翻转的 S 形曲线。我们可以假设您的函数是严格递减函数吗?如果是这样,我们可以尝试以下方法:

  1. 删除所有数据不严格的点 decreasing.For 例如,对于您的数据,该点将接近 0。
  2. 使用二分查找找到y=0.5应该放在的位置。
  3. 现在你知道你想要的 y=0.5 应该位于的两个 (x, y) 对。
  4. 如果 (x, y) 对非常接近,您可以使用简单的线性插值。
  5. 否则,您可以看到那些对附近的 sigmoid 的近似值是多少。

您可能不需要为数据拟合任何函数。只需找到以下两个元素:

  1. y<50% 的最大 x
  2. y>50% 的最小 x

然后使用插值法找到 x*。下面是代码

my_x = np.array([4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66])
my_y=np.array([0.99470977497817203, 0.99434995886145172, 0.98974611323163653, 0.961630837657524, 0.99327633558441175, 0.99338952769251909, 0.99428263292577534, 0.98690514212711611, 0.99111667721533181, 0.99149418924880861, 0.99133773062680464, 0.99143506380003499, 0.99151080464011454, 0.99268261743308517, 0.99289757252812316, 0.99100207861144063, 0.99157171773324027, 0.99112571824824358, 0.99031608691035722, 0.98978104266076905, 0.989782674787969, 0.98897835092187614, 0.98517540405423909, 0.98308943666187076, 0.96081810781994603, 0.85563541881892147, 0.61570811548079107, 0.33076276040577052, 0.14655134838124245, 0.076853147122142126, 0.035831324928136087, 0.021344669212790181])

tempInd1 = my_y<.5 # This will only work if the values are monotonic

x1 = my_x[tempInd1][0]
y1 = my_y[tempInd1][0]

x2 = my_x[~tempInd1][-1]
y2 = my_y[~tempInd1][-1]

scipy.interp(0.5, [y1, y2], [x1, x2])

使用SciPy

最直接的插值方法是使用 SciPy interpolate.interp1d 函数。 SciPy 与 NumPy 密切相关,您可能已经安装了它。 interp1d 的优点是它可以为您对数据进行排序。这是以有点时髦的语法为代价的。在许多插值函数中,假设您正在尝试从 x 值插值 y 值。这些函数通常需要 "x" 值单调递增。在你的例子中,我们交换了 x 和 y 的正常意义。正如@Abhishek Mishra 指出的那样,y 值有一个异常值。就您的数据而言,您很幸运,您可以避开异常值。

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

my_x = [4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,
48,50,52,54,56,58,60,62,64,66]

my_y_raw=np.array([0.99470977497817203, 0.99434995886145172, 
0.98974611323163653, 0.961630837657524, 0.99327633558441175, 
0.99338952769251909, 0.99428263292577534, 0.98690514212711611, 
0.99111667721533181, 0.99149418924880861, 0.99133773062680464, 
0.99143506380003499, 0.99151080464011454, 0.99268261743308517, 
0.99289757252812316, 0.99100207861144063, 0.99157171773324027, 
0.99112571824824358, 0.99031608691035722, 0.98978104266076905, 
0.989782674787969, 0.98897835092187614, 0.98517540405423909, 
0.98308943666187076, 0.96081810781994603, 0.85563541881892147, 
0.61570811548079107, 0.33076276040577052, 0.14655134838124245, 
0.076853147122142126, 0.035831324928136087, 0.021344669212790181])

# set assume_sorted to have scipy automatically sort for you
f = interp1d(my_y_raw, my_x, assume_sorted = False)
xnew = f(0.5)

print('interpolated value is ', xnew)

plt.plot(my_x, my_y_raw,'x-', markersize=10)
plt.plot(xnew, 0.5, 'x', color = 'r', markersize=20)
plt.plot((0, xnew), (0.5,0.5), ':')
plt.grid(True)
plt.show()

这给出了

interpolated value is  56.81214249272691

使用 NumPy

Numpy 也有一个 interp 函数,但它不会为您进行排序。如果你不排序,你会后悔的:

Does not check that the x-coordinate sequence xp is increasing. If xp is not increasing, the results are nonsense.

让我 np.interp 工作的唯一方法是将数据推入结构化数组。

import numpy as np
import matplotlib.pyplot as plt

my_x = np.array([4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,
48,50,52,54,56,58,60,62,64,66], dtype = np.float)


my_y_raw=np.array([0.99470977497817203, 0.99434995886145172, 
0.98974611323163653, 0.961630837657524, 0.99327633558441175, 
0.99338952769251909, 0.99428263292577534, 0.98690514212711611, 
0.99111667721533181, 0.99149418924880861, 0.99133773062680464, 
0.99143506380003499, 0.99151080464011454, 0.99268261743308517, 
0.99289757252812316, 0.99100207861144063, 0.99157171773324027, 
0.99112571824824358, 0.99031608691035722, 0.98978104266076905, 
0.989782674787969, 0.98897835092187614, 0.98517540405423909, 
0.98308943666187076, 0.96081810781994603, 0.85563541881892147, 
0.61570811548079107, 0.33076276040577052, 0.14655134838124245, 
0.076853147122142126, 0.035831324928136087, 0.021344669212790181], 
dtype = np.float)

dt = np.dtype([('x', np.float), ('y', np.float)])
data = np.zeros( (len(my_x)), dtype = dt)
data['x'] = my_x
data['y'] = my_y_raw

data.sort(order = 'y') # sort data in place by y values

print('numpy interp gives ', np.interp(0.5, data['y'], data['x']))

这给出了

numpy interp gives  56.81214249272691