Рассмотрим следующий пример, в котором мы устанавливаем образец набора данных, создаем MultiIndex, стягиваем фрейм данных и затем выполняем линейную интерполяцию, где мы заполняем строку за строкой:
import pandas as pd # version 0.14.1
import numpy as np # version 1.8.1
df = pd.DataFrame({'location': ['a', 'b'] * 5,
'trees': ['oaks', 'maples'] * 5,
'year': range(2000, 2005) * 2,
'value': [np.NaN, 1, np.NaN, 3, 2, np.NaN, 5, np.NaN, np.NaN, np.NaN]})
df.set_index(['trees', 'location', 'year'], inplace=True)
df = df.unstack()
df = df.interpolate(method='linear', axis=1)
Если распакованный набор данных выглядит следующим образом:
value
year 2000 2001 2002 2003 2004
trees location
maples b NaN 1 NaN 3 NaN
oaks a NaN 5 NaN NaN 2
Как метод интерполяции, я ожидаю результат:
value
year 2000 2001 2002 2003 2004
trees location
maples b NaN 1 2 3 NaN
oaks a NaN 5 4 3 2
но вместо этого метод дает (обратите внимание на экстраполированное значение):
value
year 2000 2001 2002 2003 2004
trees location
maples b NaN 1 2 3 3
oaks a NaN 5 4 3 2
Есть ли способ проинструктировать pandas, чтобы не экстраполировать прошлое последнее не пропущенное значение в серии?
EDIT:
Мне все еще хотелось бы видеть эту функциональность в pandas, но на данный момент я реализовал ее как функцию в numpy, а затем я использую df.apply()
для изменения df
. Это была функциональность параметров left
и right
в np.interp()
, которые я отсутствовал в pandas.
def interpolate(a, dec=None):
"""
:param a: a 1d array to be interpolated
:param dec: the number of decimal places with which each
value should be returned
:return: returns an array of integers or floats
"""
# default value is the largest number of decimal places in the input array
if dec is None:
dec = max_decimal(a)
# detect array format convert to numpy as necessary
if type(a) == list:
t = 'list'
b = np.asarray(a, dtype='float')
if type(a) in [pd.Series, np.ndarray]:
b = a
# return the row if it all nan's
if np.all(np.isnan(b)):
return a
# interpolate
x = np.arange(b.size)
xp = np.where(~np.isnan(b))[0]
fp = b[xp]
interp = np.around(np.interp(x, xp, fp, np.nan, np.nan), decimals=dec)
# return with proper numerical type formatting
# check to make sure there aren't nan before converting to int
if dec == 0 and np.isnan(np.sum(interp)) == False:
interp = interp.astype(int)
if t == 'list':
return interp.tolist()
else:
return interp
# two little helper functions
def count_decimal(i):
try:
return int(decimal.Decimal(str(i)).as_tuple().exponent) * -1
except ValueError:
return 0
def max_decimal(a):
m = 0
for i in a:
n = count_decimal(i)
if n > m:
m = n
return m
Работает как шарм в наборе данных примера:
In[1]: df.apply(interpolate, axis=1)
Out[1]:
value
year 2000 2001 2002 2003 2004
trees location
maples b NaN 1 2 3 NaN
oaks a NaN 5 4 3 2