Ответ 1
Если я правильно понимаю вас, у вас есть набор данных с большим количеством небольших пробелов (один NaN
s), который вы хотите заполнить и увеличить пробелы, которые у вас нет.
Использование pandas
для "переполнения" пробелов
Один из вариантов заключается в использовании pandas
fillna
с ограниченным количеством значений заполнения.
Как быстрый пример того, как это работает:
In [1]: import pandas as pd; import numpy as np
In [2]: x = pd.Series([1, np.nan, 2, np.nan, np.nan, 3, np.nan, np.nan, np.nan, 4])
In [3]: x.fillna(method='ffill', limit=1)
Out[3]:
0 1
1 1
2 2
3 2
4 NaN
5 3
6 3
7 NaN
8 NaN
9 4
dtype: float64
In [4]: x.fillna(method='ffill', limit=2)
Out[4]:
0 1
1 1
2 2
3 2
4 2
5 3
6 3
7 3
8 NaN
9 4
dtype: float64
В качестве примера использования этого для чего-то похожего на ваш случай:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
x = np.random.normal(0, 1, 1000).cumsum()
# Set every third value to NaN
x[::3] = np.nan
# Set a few bigger gaps...
x[20:100], x[200:300], x[400:450] = np.nan, np.nan, np.nan
# Use pandas with a limited forward fill
# You may want to adjust the `limit` here. This will fill 2 nan gaps.
filled = pd.Series(x).fillna(limit=2, method='ffill')
# Let plot the results
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot(x, color='lightblue')
axes[1].plot(filled, color='lightblue')
axes[0].set(ylabel='Original Data')
axes[1].set(ylabel='Filled Data')
plt.show()
Использование numpy
для интерполяции пробелов
В качестве альтернативы мы можем сделать это, используя только numpy
. Возможно (и более эффективно) выполнить "форвардную заливку", идентичную методу pandas выше, но я покажу еще один способ, чтобы дать вам больше опций, чем просто повторять значения.
Вместо повторения последнего значения через "пробел" мы можем выполнить линейную интерполяцию значений в пробеле. Это менее эффективно вычислительно (и я сделаю его еще менее эффективным, интерполируя везде), но для большинства наборов данных вы не заметите существенной разницы.
В качестве примера определим функцию interpolate_gaps
:
def interpolate_gaps(values, limit=None):
"""
Fill gaps using linear interpolation, optionally only fill gaps up to a
size of `limit`.
"""
values = np.asarray(values)
i = np.arange(values.size)
valid = np.isfinite(values)
filled = np.interp(i, i[valid], values[valid])
if limit is not None:
invalid = ~valid
for n in range(1, limit+1):
invalid[:-n] &= invalid[n:]
filled[invalid] = np.nan
return filled
Обратите внимание, что мы получим интерполированное значение, в отличие от предыдущей версии pandas
:
In [11]: values = [1, np.nan, 2, np.nan, np.nan, 3, np.nan, np.nan, np.nan, 4]
In [12]: interpolate_gaps(values, limit=1)
Out[12]:
array([ 1. , 1.5 , 2. , nan, 2.66666667,
3. , nan, nan, 3.75 , 4. ])
В примере построения графика, если мы заменим строку:
filled = pd.Series(x).fillna(limit=2, method='ffill')
С
filled = interpolate_gaps(x, limit=2)
Мы получим визуально идентичный сюжет:
Как полный, автономный пример:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)
def interpolate_gaps(values, limit=None):
"""
Fill gaps using linear interpolation, optionally only fill gaps up to a
size of `limit`.
"""
values = np.asarray(values)
i = np.arange(values.size)
valid = np.isfinite(values)
filled = np.interp(i, i[valid], values[valid])
if limit is not None:
invalid = ~valid
for n in range(1, limit+1):
invalid[:-n] &= invalid[n:]
filled[invalid] = np.nan
return filled
x = np.random.normal(0, 1, 1000).cumsum()
# Set every third value to NaN
x[::3] = np.nan
# Set a few bigger gaps...
x[20:100], x[200:300], x[400:450] = np.nan, np.nan, np.nan
# Interpolate small gaps using numpy
filled = interpolate_gaps(x, limit=2)
# Let plot the results
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot(x, color='lightblue')
axes[1].plot(filled, color='lightblue')
axes[0].set(ylabel='Original Data')
axes[1].set(ylabel='Filled Data')
plt.show()
Примечание. Я изначально полностью неправильно прочитал вопрос. См. Историю версий для моего первоначального ответа.