Как «растянуть» мой фрейм данных и интерполировать между существующими значениями

#python #pandas #interpolation #reindex

#python #панды #интерполяция #переиндексация

Вопрос:

Я столкнулся с простой DataFrame.reindex().interpolate() проблемой, потому что используемые мной фреймы данных не имеют индекса даты и времени.

У меня есть DataFrame1: t это выглядит так:

 In[1]: import pandas as pd
t = pd.DataFrame({'D18O': [-0.47, -0.12,  0.55,  0.72,  1.8 ,  1.1 ,  0.43, -0.29, -0.55,
       -0.6 , -0.32,  0.28,  0.72,  1.1 ,  1.34,  1.32,  1.11,  0.46,
        0.09,  0.02]})

Out[2]: 
1    -0.47
2    -0.12
3     0.55
4     0.72
5     1.80
6     1.10
7     0.43
8    -0.29
9    -0.55
10   -0.60
11   -0.32
12    0.28
13    0.72
14    1.10
15    1.34
16    1.32
17    1.11
18    0.46
19    0.09
20    0.02
Name: D18O, dtype: float64
 

Я хочу «растянуть» его до 430 строк, равномерно распределяя каждую строку и линейно интерполируя значения между ними. Это потому, что мой фрейм данных 2: env содержит 430 строк, и я хочу провести более поздний анализ, для которого оба фрейма должны иметь одинаковое измерение.

 In[2]: env.index
Out[49]: RangeIndex(start=0, stop=430, step=1)
 

Я пробовал переиндексировать и интерполировать во многих комбинациях, но просто не могу найти правильный метод. Я думаю, проблема в том, что 430 неравномерно делится на 19/20.

 new_idx = np.linspace(t.index[0], t.index[-1], env.shape[0])
t.reindex(new_idx).interpolate()
 

Я думал, что это может сработать, но поскольку индексы не равны, он пропускает большинство значений t и оставляет меня с почти пустым новым фреймом данных.

Для шага переиндексации я ожидаю чего-то вроде:

 In[3]: t['D18O']
Out[3]: 
0          0.47
2.13157     NaN
2.26315     NaN
...         ...
21.5      -0.12
22.63157    NaN
23.76315    NaN
...         ...
...         ...
430        0.02
Name: D18O, dtype: float64
 

Индексы на самом деле не имеют значения, если значения распределены равномерно, а количество строк соответствует количеству строк в env .

Ответ №1:

Вы можете использовать параметр ffill с limit DataFrame.reindex помощью in , но есть проблема с дублированием первого значения, поэтому возможным решением является добавление первого вспомогательного значения, близкого 0 к индексу, reindex , удаление его по iloc и последнее interpolate :

 r = pd.RangeIndex(0, 430, 1)

t.loc[-0.001] = 0
t = t.sort_index()
new_idx = np.linspace(t.index[0], t.index[-1], len(r))
print (t.reindex(new_idx, method='ffill', limit=1).iloc[1:].interpolate())

               D18O
0.043291  -0.470000
0.087583  -0.454091
0.131874  -0.438182
0.176166  -0.422273
0.220457  -0.406364
0.264748  -0.390455
0.309040  -0.374545
0.353331  -0.358636
0.397622  -0.342727
0.441914  -0.326818
0.486205  -0.310909
0.530497  -0.295000
0.574788  -0.279091
0.619079  -0.263182
0.663371  -0.247273
0.707662  -0.231364
0.751953  -0.215455
...
...
 

Комментарии:

1. Это работает отлично. Если я построю оригинал и интерполирую t , они покажут точно такой же график. Тем не менее, я не совсем понимаю взаимосвязь между вставкой индекса, прямым заполнением и исключением первого значения. Я думал method='ffill' , что заполнит каждое значение между двумя записями?

2. @cripcate — Точно, если удалить limit=1 его, замените все NaN значения s между, ограничение, которое я использовал только для первой замены одного NaN.

3. Ах, хорошо, значит, он заполняет только одно значение. Я все еще не совсем понимаю, зачем нужно вспомогательное значение, хотя, извините. И разве я не должен делать t.loc[-0.0001] = t[0] это вместо t.loc[-0.0001] = t[0] того, чтобы? Извините, здесь немного запутался.

4. @cripcate — я думаю, что это работает так же, если t[0] или 0 , потому что это значение не используется для интерполяции. Проверьте это, удалив t.loc[-0.001] = 0 и t.reindex(new_idx, method='ffill', limit=1).interpolate()

5. для меня это работает отлично, но только тогда, когда я отклоняю вставку вспомогательного значения

Ответ №2:

Теперь я использовал более общий способ интерполяции данных по определенному индексу. Я просто хочу перечислить свой подход для будущих ссылок:

 import numpy as np
import pandas as pd 
from scipy.interpolate import interp1d

# Example data 5 numeric columns
i = pd.RangeIndex(0, 430, 1)
df1 = pd.DataFrame([-0.47, -0.12, 0.55, 0.72, 1.8, 1.1, 0.43, -0.29, 
                    -0.55, -0.6, -0.32, 0.28, 0.72, 1.1 , 1.34, 1.32,
                    1.11, 0.46, 0.09, 0.02], [-0.47, -0.12, 0.55, 0.72, 1.8, 1.1, 0.43, -0.29, 
                    -0.55, -0.6, -0.32, 0.28, 0.72, 1.1 , 1.34, 1.32,
                    1.11, 0.46, 0.09, 0.02], [-0.47, -0.12, 0.55, 0.72, 1.8, 1.1, 0.43, -0.29, 
                    -0.55, -0.6, -0.32, 0.28, 0.72, 1.1 , 1.34, 1.32,
                    1.11, 0.46, 0.09, 0.02])

# Select numeric columns
nums = df1.select_dtypes([np.number])
old_idx = df.index
# Calculate new index
len_idx = env.shape[0]
mi, ma = old_idx.min(), old_idx.max()
new_idx = np.linspace(mi, ma, len_idx)

# Plot to compare interpolation to original values
fig, ax = plt.subplots(1, 1)
ax.plot(old_idx, df1.iloc[:, 0], 'k--')

def interpol(column):
    ```Interpolation function```    
    interpolant = interp1d(old_idx, column)
    interpolated = interpolant(new_idx)
    return interpolated

# Interpolate data to match index length of enviromental data
inter_nums = pd.DataFrame(index=new_idx)
for col in nums:
    inter = interpol(nums[col])
    inter_nums[col] = inter

# Plot after interpolation. Same curve? good!      
ax.plot(inter_nums_iloc[:; 0], c='r')