note.nkmk.me

pandasで欠損値NaNを前後の値から補間するinterpolate

Date: 2018-08-04 / tags: Python, pandas
このエントリーをはてなブックマークに追加

pandas.DataFrame, pandas.Seriesの欠損値NaNを前後の値から補間するにはinterpolate()メソッドを使う。

以下の内容について説明する。

  • interpolate()の基本的な使い方
    • 行 or 列を指定: 引数axis
    • 補間する連続欠損値の最大数を指定: 引数limit
    • 補間方向を指定: 引数limit_direction
    • 内挿のみ or 外挿のみ or 両方を指定: 引数limit_area
    • オブジェクト自体を更新するかを指定: 引数inplace
  • 補間方法: 引数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.0method='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'

その他

補間方法としては、そのほか、'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

時系列データのリサンプリングについては以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事