Я хотел бы создать функцию python для линейной интерполяции в частично пустой сетке и получения ближайшей экстраполяции за пределы.
Скажем, у меня есть следующие данные, хранящиеся в pandas DataFrame:
In [1]: import numpy as np
In [2]: import pandas as pd
In [3]: x = [0,1,2,3,4]
In [4]: y = [0.5,1.5,2.5,3.5,4.5,5.5]
In [5]: z = np.array([[np.nan,np.nan,1.5,2.0,5.5,3.5],[np.nan,1.0,4.0,2.5,4.5,3.0],[2.0,0.5,6.0,1.5,3.5,np.nan],[np.nan,1.5,4.0,2.0,np.nan,np.nan],[np.nan,np.nan,2.0,np.nan,np.nan,np.nan]])
In [6]: df = pd.DataFrame(z,index=x,columns=y)
In [7]: df
Out[7]:
0.5 1.5 2.5 3.5 4.5 5.5
0 NaN NaN 1.5 2.0 5.5 3.5
1 NaN 1.0 4.0 2.5 4.5 3.0
2 2.0 0.5 6.0 1.5 3.5 NaN
3 NaN 1.5 4.0 2.0 NaN NaN
4 NaN NaN 2.0 NaN NaN NaN
Я хотел бы получить функцию myInterp
, которая возвращает линейную интерполяцию в границах данных (т.е. не значения NaN) и получает ближайшую экстраполяцию за пределами границ (т.е. NaN или без значений), например:
In [1]: myInterp([1.5,2.5]) #linear interpolation
Out[1]: 5.0
In [2]: myInterp([1.5,4.0]) #bi-linear interpolation
Out[2]: 3.0
In [3]: myInterp([0.0,2.0]) #nearest extrapolation (inside grid)
Out[3]: 1.5
In [4]: myInterp([5.0,2.5]) #nearest extrapolation (outside grid)
Out[4]: 2.0
Я пробовал много комбинаций пакетов scipy.interpolate
безуспешно, есть ли у кого-нибудь предложения, как это сделать?
Да, к сожалению, scipy не работает с nans
Из документов:
Note that calling interp2d with NaNs present in input values results in undefined behaviour.
Даже маскировка нанов в np.masked_array
не увенчалась успехом.
Поэтому я бы посоветовал удалить все записи nan из z
, воспользовавшись возможностью предоставить sp.interp2d
полный список x- и y-координат только для действительных данных и оставив z также 1D:
X=[];Y=[];Z=[] # initialize new 1-D-lists for interp2
for i, xi in enumerate(x): # iterate through x
for k, yk in enumerate(y): # iterate through y
if not np.isnan(z[i, k]): # check if z-value is valid...
X.append(xi) # ...and if so, append coordinates and value to prepared lists
Y.append(yk)
Z.append(z[i, k])
Так хоть sp.interp2d работает и дает результат:
ip = sp.interpolate.interp2d(X,Y,Z)
Однако значения в результате вас не порадуют:
In: ip(x,y)
Out:
array([[ 18.03583061, -0.44933642, 0.83333333, -1. , -1.46105542],
[ 9.76791531, 1.3014037 , 2.83333333, 1.5 , 0.26947229],
[ 1.5 , 3.05214381, 4.83333333, 4. , 2. ],
[ 2. , 3.78378051, 1.5 , 2. , 0.8364618 ],
[ 5.5 , 3.57039277, 3.5 , -0.83019815, -0.7967441 ],
[ 3.5 , 3.29227922, 17.29607177, 0. , 0. ]])
по сравнению с исходными данными:
In:z
Out:
array([[ nan, nan, 1.5, 2. , 5.5, 3.5],
[ nan, 1. , 4. , 2.5, 4.5, 3. ],
[ 2. , 0.5, 6. , 1.5, 3.5, nan],
[ nan, 1.5, 4. , 2. , nan, nan],
[ nan, nan, 2. , nan, nan, nan]])
Но IMHO это потому, что градиентные изменения в ваших данных слишком высоки. Даже больше в отношении небольшого количества выборок данных.
Я надеюсь, что это всего лишь набор тестовых данных, и в вашем реальном приложении есть более плавные градиенты и еще несколько примеров. Тогда я был бы рад услышать, сработает ли это ...
Однако тривиальный тест с массивом нулевого градиента - только немного разрушенный nans - может дать намек на то, что интерполяция должен работать, в то время как экстраполяция верна лишь отчасти:
In:ip(x,y)
Out:
array([[ 3. , 3. , 3. , 3. , 0. ],
[ 3. , 3. , 3. , 3. , 1.94701008],
[ 3. , 3. , 3. , 3. , 3. ],
[ 3. , 3. , 3. , 3. , 1.54973345],
[ 3. , 3. , 3. , 3. , 0.37706713],
[ 3. , 3. , 2.32108317, 0.75435203, 0. ]])
в результате тривиального тестового ввода
In:z
Out:
array([[ nan, nan, 3., 3., 3., 3.],
[ nan, 3., 3., nan, 3., 3.],
[ 3., 3., 3., 3., 3., nan],
[ nan, 3., 3., 3., nan, nan],
[ nan, nan, 3., nan, nan, nan]])
PS: Если присмотреться к правой стороне: есть даже действительные записи, полностью измененные, т. Е. Сделанные неправильно, что приводит к ошибкам в последующем анализе.
Но сюрприз: кубическая версия здесь работает намного лучше:
In:ip = sp.interpolate.interp2d(X,Y,Z, kind='cubic')
In:ip(x,y)
Out:
array([[ 3. , 3. , 3. , 3.02397028, 3.0958811 ],
[ 3. , 3. , 3. , 3. , 3. ],
[ 3. , 3. , 3. , 3. , 3. ],
[ 3. , 3. , 3. , 3. , 3. ],
[ 3. , 3. , 3. , 2.97602972, 2.9041189 ],
[ 3. , 3. , 3. , 2.9041189 , 2.61647559]])
In:z
Out:
array([[ nan, nan, 3., 3., 3., 3.],
[ nan, 3., 3., nan, 3., 3.],
[ 3., 3., 3., 3., 3., nan],
[ nan, 3., 3., 3., nan, nan],
[ nan, nan, 3., nan, nan, nan]])
Поскольку scipy.interp2d не работает с Nans, решение состоит в том, чтобы заполнить NaN в DataFrame перед использованием interp2d. Это можно сделать с помощью функции pandas.interpolate.
В предыдущем примере желаемый результат был получен следующим образом:
In [1]: from scipy.interpolate import interp2d
In [2]: df = df.interpolate(limit_direction='both',axis=1,inplace=True)
In [3]: myInterp = interp2d(df.index,df.columns,df.values.T)
In [4]: myInterp(1.5,2.5)
Out[4]: array([5.])
In [5]: myInterp(1.5,4.0)
Out[5]: array([3.])
In [6]: myInterp(0.0,2.0)
Out[6]: array([1.5])
In [7]: myInterp(5.0,2.5)
Out[7]: array([2.])
спасибо за предложение, я постараюсь вернуться к вам