我想在不同的模型之间进行插值。为方便起见,我的数据如下所示:
我有10种不同的模拟(我将称之为z
)。对于每个z
,我有array x
和array y
(给定z
,len(x)=len(y)
)。
例如:
代表z=1
:x.shape=(1200,)
和y.shape=(1200,)
代表z=2
:x.shape=(1250,)
和y.shape=(1250,)
代表z=3
:x.shape=(1236,)
和y.shape=(1236,)
依旧......
我想进行插值,以便对于给定的z
和x
,我得到y
。例如,对于z=2.5
和x=10**9
,代码输出y
。我假设:
y = a*x + b*z + c
我当然不知道a
,b
和c
。
我的问题是如何将数据存储在网格中?我很困惑,因为z
和x
的大小不同y
。如何构建网格?
我能够部分解决我的问题。我首先做的是使用x
在y
和interp1d
之间进行插值。它工作得非常好。然后,我创建了 x
和y
值的新网格。简而言之,该方法是:
f = interp1d(x, y, kind='linear')
new_x = np.linspace(10**7, 4*10**9, 10000)
new_y = f(new_x)
然后我插入了x
,y
和z
:
ff = LinearNDInterpolator( (x, z), y)
为了测试该方法是否有效,这是一个带z=3
的图。
情节看起来很好,直到x=10**8
。实际上,该线偏离了原始模型。这是我进一步放大时的情节:
x > 10**8
时插值显然不好。我该如何解决?
答案 0 :(得分:0)
在您的问题中,似乎曲线y(x)表现良好,因此您可能只是先为z的给定值插入y(x),然后在获得的y值之间进行插值。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import random
#####
# Generate some data
#####
generate = lambda x, z: 1./(x+1.)+(z*x/75.+z/25.)
def f(z):
#create an array of values between zero and 100 of random length
x = np.linspace(0,10., num=random.randint(42,145))
#generate corresponding y values
y = generate(x, z)
return np.array([x,y])
Z = [1, 2, 3, 3.6476, 4, 5.1]
A = [f(z) for z in Z]
#now A contains the dataset of [x,y] pairs for each z value
#####
# Interpolation
#####
def do_interpolation(x,z):
#assume Z being sorted in ascending order
#look for indizes of z values closest to given z
ig = np.searchsorted(Z, z)
il = ig-1
#interpolate y(x) for those z values
yg = np.interp(x, A[ig][0,:], A[ig][1,:])
yl = np.interp(x, A[il][0,:], A[il][1,:])
#linearly interpolate between yg and yl
return yl + (yg-yl)*float(z-Z[il])/(Z[ig] - Z[il])
# do_interpolation(x,z) will now provide the interpolated data
print do_interpolation( np.linspace(0, 10), 2.5)
#####
# Plotting, use Slider to change the value of z.
#####
fig=plt.figure()
fig.subplots_adjust(bottom=0.2)
ax=fig.add_subplot(111)
for i in range(len(Z)):
ax.plot(A[i][0,:] , A[i][1,:], label="{z}".format(z=Z[i]) )
l, = ax.plot(np.linspace(0, 10) , do_interpolation( np.linspace(0, 10), 2.5), label="{z}".format(z="interpol"), linewidth=2., color="k" )
axn1 = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg='#e4e4e4')
sn1 = Slider(axn1, 'z', Z[0], Z[-1], valinit=2.5)
def update(val):
l.set_data(np.linspace(0, 10), do_interpolation( np.linspace(0, 10), val))
plt.draw()
sn1.on_changed(update)
ax.legend()
plt.show()
答案 1 :(得分:0)
你所做的对我来说似乎有点奇怪,至少你似乎使用一组y
值进行插值。我建议不是一个接一个地执行两个插值,而是将y(z,x)
函数视为纯二维插值问题的结果。
正如我在评论中指出的那样,我建议使用scipy.interpolate.LinearNDInterpolator
,即griddata
在双线性插值下使用的同一个对象。正如我们在评论中所讨论的那样,你需要一个可以在之后多次查询的插补器,因此我们必须使用较低级别的插值器对象,因为它是可调用的。
以下是我的意思的完整示例,包括虚拟数据和绘图:
import numpy as np
import scipy.interpolate as interp
import matplotlib.pyplot as plt
# create dummy data
zlist = range(4) # z values
# one pair of arrays for each z value in a list:
xlist = [np.linspace(-1,1,41),
np.linspace(-1,1,61),
np.linspace(-1,1,55),
np.linspace(-1,1,51)]
funlist = [lambda x:0.1*np.ones_like(x),
lambda x:0.2*np.cos(np.pi*x)+0.4,
lambda x:np.exp(-2*x**2)+0.5,
lambda x:-0.7*np.abs(x)+1.7]
ylist = [f(x) for f,x in zip(funlist,xlist)]
# create contiguous 1d arrays for interpolation
all_x = np.concatenate(xlist)
all_y = np.concatenate(ylist)
all_z = np.concatenate([np.ones_like(x)*z for x,z in zip(xlist,zlist)])
# create a single linear interpolator object
yfun = interp.LinearNDInterpolator((all_z,all_x),all_y)
# generate three interpolated sets: one with z=2 to reproduce existing data,
# two with z=1.5 and z=2.5 respectively to see what happens
xplot = np.linspace(-1,1,30)
z = 2
y_repro = yfun(z,xplot)
z = 1.5
y_interp1 = yfun(z,xplot)
z = 2.5
y_interp2 = yfun(z,xplot)
# plot the raw data (markers) and the two interpolators (lines)
fig,ax = plt.subplots()
for x,y,z,mark in zip(xlist,ylist,zlist,['s','o','v','<','^','*']):
ax.plot(x,y,'--',marker=mark,label='z={}'.format(z))
ax.plot(xplot,y_repro,'-',label='z=2 interp')
ax.plot(xplot,y_interp1,'-',label='z=1.5 interp')
ax.plot(xplot,y_interp2,'-',label='z=2.5 interp')
ax.set_xlabel('x')
ax.set_ylabel('y')
# reduce plot size and put legend outside for prettiness, see also http://stackoverflow.com/a/4701285/5067311
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show()
您没有指定如何存储一系列(x,y)
数组对,我使用了一个numpy ndarray
列表。如您所见,我将1d数组列表压缩为一组1d数组:all_x
,all_y
,all_z
。这些可以用作分散的y(z,x)
数据,您可以从中构造插值器对象。正如您在结果中看到的那样,对于z=2
,它会再现输入点,对于非整数z
,它会在相关的y(x)
曲线之间进行插值。
此方法应适用于您的数据集。但需要注意的是:x
轴上的对数刻度上有大量数字。仅这一点就可能导致数字不稳定。我建议您也尝试使用log(x)
执行插值,它可能表现得更好(这只是一个模糊的猜测)。