pandasで欠損値NaNを前後の値から補間するinterpolate
pandas.DataFrame
, pandas.Series
の欠損値NaN
を前後の値から補間するにはinterpolate()
メソッドを使う。
- pandas.DataFrame.interpolate — pandas 0.23.3 documentation
- pandas.Series.interpolate — pandas 0.23.3 documentation
以下の内容について説明する。
interpolate()
の基本的な使い方- 行 or 列を指定: 引数
axis
- 補間する連続欠損値の最大数を指定: 引数
limit
- 補間方向を指定: 引数
limit_direction
- 内挿のみ or 外挿のみ or 両方を指定: 引数
limit_area
- オブジェクト自体を更新するかを指定: 引数
inplace
- 行 or 列を指定: 引数
- 補間方法: 引数
method
- 線形補間:
linear
,index
,values
- スプライン補間:
spline
- その他
- 線形補間:
- 時系列データの補間
欠損値NaN
を削除したり特定の値で穴埋めする場合はdropna()
, fillna()
を使う。以下の記事を参照。
interpolate()の基本的な使い方
以下のpandas.DataFrame
を例とする。
import pandas as pd
df = pd.DataFrame({'col1': [0, pd.np.nan, pd.np.nan, 3, 4],
'col2': [pd.np.nan, 1, 2, pd.np.nan, pd.np.nan],
'col3': [4, pd.np.nan, pd.np.nan, 7, 10]})
print(df)
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
デフォルトでは各列に対して線形補間を行う。下端の欠損値には同じ値が繰り返される。上端の欠損値はそのまま。
print(df.interpolate())
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
以下、引数の設定について説明する。基本的にはpandas.Series
でも同じ。
第一引数method
で指定する補間方法については後述。
行 or 列を指定: 引数axis
引数axis=1
とすると各行に対して補間される。右端の欠損値には同じ値が繰り返される。左端の欠損値はそのまま。
print(df.interpolate(axis=1))
# col1 col2 col3
# 0 0.0 2.0 4.0
# 1 NaN 1.0 1.0
# 2 NaN 2.0 2.0
# 3 3.0 5.0 7.0
# 4 4.0 7.0 10.0
補間する連続欠損値の最大数を指定: 引数limit
欠損値が連続している場合、最大でいくつの欠損値を補間するかを引数limit
で指定する。デフォルトはNone
で連続する欠損値すべてが補間される。
print(df.interpolate(limit=1))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
補間方向を指定: 引数limit_direction
補間方向は引数limit_direction
で'forward'
, 'backward'
, 'both'
のいずれかを指定する。デフォルトは'forward'
。
print(df.interpolate(limit=1, limit_direction='forward'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit=1, limit_direction='backward'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 NaN 1.0 NaN
# 2 2.0 2.0 6.0
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit=1, limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
上述のように、デフォルトでは上端(または左端)の欠損値はそのままとなるが、limit_direction='both'
とすると両方とも補間される。
print(df.interpolate(limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
内挿のみ or 外挿のみ or 両方を指定: 引数limit_area
補間対象領域は引数limit_area
で指定する。
'inside'
だと内挿のみ、'outside'
だと外挿のみ、None
(デフォルト)だと両方が対象となる。外挿については上述のlimit_direction
で前方(上側・左側)、後方(下側・右側)、両方を指定できる。
print(df.interpolate(limit_area='inside'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit_area='outside'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
print(df.interpolate(limit_area='outside', limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
なお、便宜上「外挿」という言葉を使っているが、上の結果からも分かるように、線形補間(デフォルト)では外側の値は端の値の繰り返しとなり線形外挿ではない。後述のスプライン補間では外側の値も繰り返しではなく外装された値となる。
オブジェクト自体を更新するかを指定: 引数inplace
ほかの多くのメソッドと同様、引数inplace
でオブジェクト自体を更新するかどうかを指定できる。
df_copy = df.copy()
df_copy.interpolate(inplace=True)
print(df_copy)
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
補間方法: 引数method
補間方法は第一引数method
に指定する。デフォルトはmethod='linear'
で線形補間。
線形補間: linear, index, values
method='linear'
(デフォルト)ではインデックス列が数値でも特に考慮されないが、method='index'
またはmethod='values'
とするとインデックス列を考慮して補間される。インデックス列をY軸、対象の列をX軸として線形補間するイメージ。
以下の例ではインデックス5
の値がmethod='linear'
(デフォルト)では2.0
、method='index'
またはmethod='values'
では2.5
となる。
s = pd.Series([0, pd.np.nan, pd.np.nan, pd.np.nan, 4, pd.np.nan, pd.np.nan],
index=[0, 2, 5, 6, 8, 10, 14])
print(s)
# 0 0.0
# 2 NaN
# 5 NaN
# 6 NaN
# 8 4.0
# 10 NaN
# 14 NaN
# dtype: float64
print(s.interpolate())
# 0 0.0
# 2 1.0
# 5 2.0
# 6 3.0
# 8 4.0
# 10 4.0
# 14 4.0
# dtype: float64
print(s.interpolate('index'))
# 0 0.0
# 2 1.0
# 5 2.5
# 6 3.0
# 8 4.0
# 10 4.0
# 14 4.0
# dtype: float64
print(s.interpolate('values'))
# 0 0.0
# 2 1.0
# 5 2.5
# 6 3.0
# 8 4.0
# 10 4.0
# 14 4.0
# dtype: float64
デフォルトのmethod='linear'
はインデックス列が文字列でもOKだが、method='index'
またはmethod='values'
だとエラーとなる。
s.index = list('abcdefg')
print(s)
# a 0.0
# b NaN
# c NaN
# d NaN
# e 4.0
# f NaN
# g NaN
# dtype: float64
print(s.interpolate())
# a 0.0
# b 1.0
# c 2.0
# d 3.0
# e 4.0
# f 4.0
# g 4.0
# dtype: float64
# print(s.interpolate('values'))
# TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'
スプライン補間: spline
method='spline'
とするとスプライン補間。同時に引数order
に次数を指定する必要がある。
s = pd.Series([0, 10, pd.np.nan, pd.np.nan, 4, pd.np.nan, pd.np.nan],
index=[0, 2, 5, 6, 8, 10, 14])
print(s.interpolate('spline', order=2))
# 0 0.00
# 2 10.00
# 5 13.75
# 6 12.00
# 8 4.00
# 10 -10.00
# 14 -56.00
# dtype: float64
スプライン補間は常にインデックス列を考慮して補間される。
s.index = range(7)
print(s.interpolate('spline', order=2))
# 0 0.0
# 1 10.0
# 2 14.0
# 3 12.0
# 4 4.0
# 5 -10.0
# 6 -30.0
# dtype: float64
したがって、スプライン補間する場合はインデックス列が数値である必要がある。文字列だとエラー。
s.index = list('abcdefg')
# print(s.interpolate('spline', order=2))
# TypeError: unsupported operand type(s) for -: 'str' and 'str'
その他
引数method
に指定できる補間方法としては、そのほか、'nearest'
, 'zero'
, 'slinear'
, 'quadratic'
, 'cubic'
, 'barycentric'
, 'krogh'
, 'polynomial'
, 'piecewise_polynomial'
, 'from_derivatives'
, 'pchip'
, 'akima'
がある。
公式ドキュメントにもあるように、上述のスプライン補間('spline'
)も含めて、これらはSciPyの関数のラッパー。
いずれの場合も上述のスプライン補間と同様にインデックスが数値である必要がある。
時系列データの補間
時系列データについては専用の補間方法としてmethod='time'
が用意されている。method='time'
の場合、インデックス列の日時に合わせて線形補間される。
df_nan = pd.DataFrame({'value': [1, pd.np.nan, pd.np.nan, pd.np.nan, 31]},
index=pd.to_datetime(['2018-01-01', '2018-01-02', '2018-01-15', '2018-01-20', '2018-01-31']))
print(df_nan)
# value
# 2018-01-01 1.0
# 2018-01-02 NaN
# 2018-01-15 NaN
# 2018-01-20 NaN
# 2018-01-31 31.0
print(df_nan.interpolate())
# value
# 2018-01-01 1.0
# 2018-01-02 8.5
# 2018-01-15 16.0
# 2018-01-20 23.5
# 2018-01-31 31.0
print(df_nan.interpolate('time'))
# value
# 2018-01-01 1.0
# 2018-01-02 2.0
# 2018-01-15 15.0
# 2018-01-20 20.0
# 2018-01-31 31.0
時系列データのリサンプリングについては以下の記事を参照。