pandasで欠損値NaNを置換(穴埋め)するfillna

Modified: | Tags: Python, pandas

pandasでDataFrameSeriesの欠損値NaNを任意の値に置換(穴埋め、代入)するにはfillna()メソッドを使う。

単純な置換ではなく前後の値から補間するにはinterpolate()を使う。

欠損値NaNの抽出・削除・カウントについては以下の記事を参照。

なお、pandasではNaN(Not a Number: 非数)のほか、Noneも欠損値として扱われる。

本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。例として、空白を含むCSVファイルを読み込んで使用する。

import pandas as pd

print(pd.__version__)
# 2.1.4

df = pd.read_csv('data/src/sample_pandas_normal_nan.csv')
print(df)
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1      NaN   NaN   NaN    NaN    NaN
# 2  Charlie   NaN    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen   NaN    CA   88.0    NaN
# 5    Frank  30.0   NaN    NaN    NaN

欠損値NaNを共通の値で一律に置換

fillna()の第一引数valueに置き換えたい値を指定すると、すべての欠損値NaNがその値で置き換わる。

print(df.fillna(0))
#       name   age state  point  other
# 0    Alice  24.0    NY    0.0    0.0
# 1        0   0.0     0    0.0    0.0
# 2  Charlie   0.0    CA    0.0    0.0
# 3     Dave  68.0    TX   70.0    0.0
# 4    Ellen   0.0    CA   88.0    0.0
# 5    Frank  30.0     0    0.0    0.0

NaNを含む数値の列はデータ型が浮動小数点数floatとなるため、NaNを整数intの値に置換してもデータ型はfloatのまま。intに変換したい場合はastype()を使う。

欠損値NaNを列ごとに異なる値で置換

fillna()の第一引数valueに辞書dictを指定すると、列ごとに異なる値を代入できる。

{key: value}{列名: 置き換えたい値}とする。指定されていない列は欠損値NaNのまま。列名と一致しないkeyは無視される。

print(df.fillna({'name': 'XXX', 'age': 20, 'ZZZ': 100}))
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1      XXX  20.0   NaN    NaN    NaN
# 2  Charlie  20.0    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  20.0    CA   88.0    NaN
# 5    Frank  30.0   NaN    NaN    NaN

Seriesも指定可能。

Seriesのラベルと一致する列名の列の欠損値がSeriesの値で置換される。Seriesのラベルと対応しない列は欠損値のまま。列名と一致しないSeriesのラベルは無視される。

s_for_fill = pd.Series(['XXX', 20, 100], index=['name', 'age', 'ZZZ'])
print(s_for_fill)
# name    XXX
# age      20
# ZZZ     100
# dtype: object

print(df.fillna(s_for_fill))
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1      XXX  20.0   NaN    NaN    NaN
# 2  Charlie  20.0    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  20.0    CA   88.0    NaN
# 5    Frank  30.0   NaN    NaN    NaN

欠損値NaNを列ごとに平均値・中央値・最頻値などで置換

列ごとの平均値はmean()メソッドで算出できる。欠損値NaNは除外して算出されるが、すべての要素がNaNの列はNaN。引数numeric_onlyTrueとすると対象を数値列に限定できる。返り値はSeries

print(df.mean(numeric_only=True))
# age      40.666667
# point    79.000000
# other          NaN
# dtype: float64

このSeriesfillna()の第一引数valueに指定すると、上述のように、対応する列の欠損値が平均値で置換される。

print(df.fillna(df.mean(numeric_only=True)))
#       name        age state  point  other
# 0    Alice  24.000000    NY   79.0    NaN
# 1      NaN  40.666667   NaN   79.0    NaN
# 2  Charlie  40.666667    CA   79.0    NaN
# 3     Dave  68.000000    TX   70.0    NaN
# 4    Ellen  40.666667    CA   88.0    NaN
# 5    Frank  30.000000   NaN   79.0    NaN

同様に、中央値で置き換えたい場合はmedian()メソッドを使う。偶数個の場合は中央二つの値の平均値が中央値となる。

print(df.fillna(df.median(numeric_only=True)))
#       name   age state  point  other
# 0    Alice  24.0    NY   79.0    NaN
# 1      NaN  30.0   NaN   79.0    NaN
# 2  Charlie  30.0    CA   79.0    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  30.0    CA   88.0    NaN
# 5    Frank  30.0   NaN   79.0    NaN

最頻値はmode()メソッド。mode()DataFrameを返すのでiloc[0]で先頭行をSeriesとして取得している。最頻値は文字列に対しても有効。

print(df.fillna(df.mode().iloc[0]))
#       name   age state  point  other
# 0    Alice  24.0    NY   70.0    NaN
# 1    Alice  24.0    CA   70.0    NaN
# 2  Charlie  24.0    CA   70.0    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  24.0    CA   88.0    NaN
# 5    Frank  30.0    CA   70.0    NaN

欠損値NaNを前後の値で置換: ffill(), bfill()

指定した値ではなく欠損値NaNの前後(上下)の要素の値で置換するにはffill()およびbfill()メソッドを使う。

ffill()は欠損値を前(上)の値で置き換え、bfill()は後ろ(下)の値で置き換える。

print(df.ffill())
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1    Alice  24.0    NY    NaN    NaN
# 2  Charlie  24.0    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  68.0    CA   88.0    NaN
# 5    Frank  30.0    CA   88.0    NaN

print(df.bfill())
#       name   age state  point  other
# 0    Alice  24.0    NY   70.0    NaN
# 1  Charlie  68.0    CA   70.0    NaN
# 2  Charlie  68.0    CA   70.0    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  30.0    CA   88.0    NaN
# 5    Frank  30.0   NaN    NaN    NaN

デフォルトでは連続する欠損値をすべて置換する。引数limitで最大何回まで連続して置換するかを指定できる。

print(df.ffill(limit=1))
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1    Alice  24.0    NY    NaN    NaN
# 2  Charlie   NaN    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  68.0    CA   88.0    NaN
# 5    Frank  30.0    CA   88.0    NaN

print(df.bfill(limit=1))
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1  Charlie   NaN    CA    NaN    NaN
# 2  Charlie  68.0    CA   70.0    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  30.0    CA   88.0    NaN
# 5    Frank  30.0   NaN    NaN    NaN

引数axis1または'columns'とすると左右の値で置換される。ffill()は左の値、bfill()は右の値で置き換える。

print(df.ffill(axis=1))
#       name      age state point other
# 0    Alice     24.0    NY    NY    NY
# 1      NaN      NaN   NaN   NaN   NaN
# 2  Charlie  Charlie    CA    CA    CA
# 3     Dave     68.0    TX  70.0  70.0
# 4    Ellen    Ellen    CA  88.0  88.0
# 5    Frank     30.0  30.0  30.0  30.0

print(df.bfill(axis=1))
#       name   age state point other
# 0    Alice  24.0    NY   NaN   NaN
# 1      NaN   NaN   NaN   NaN   NaN
# 2  Charlie    CA    CA   NaN   NaN
# 3     Dave  68.0    TX  70.0   NaN
# 4    Ellen    CA    CA  88.0   NaN
# 5    Frank  30.0   NaN   NaN   NaN

なお、ffill(), bfill()と同じ処理を行うpad(), backfill()は、バージョン2.0.0からDeprecated(非推奨)になっている。

fillna()の引数methodはバージョン2.1.0でDeprecated(非推奨)

バージョン2.1.0でDeprecated(非推奨)になったが、fillna()の引数method, limitを指定することでffill(), bfill()と同じ処理が可能。

引数method'ffill'または'pad'とするとffill()と同じ処理、'bfill'または'backfill'とするとbfill()と同じ処理になる。

バージョン2.1.4ではまだ使用可能だが、FutureWarningが出る。

print(df.fillna(method='ffill', limit=1))
#       name   age state  point  other
# 0    Alice  24.0    NY    NaN    NaN
# 1    Alice  24.0    NY    NaN    NaN
# 2  Charlie   NaN    CA    NaN    NaN
# 3     Dave  68.0    TX   70.0    NaN
# 4    Ellen  68.0    CA   88.0    NaN
# 5    Frank  30.0    CA   88.0    NaN
# 
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_50534/2498159999.py:1: FutureWarning: DataFrame.fillna with 'method' is deprecated and will raise in a future version. Use obj.ffill() or obj.bfill() instead.

元のオブジェクトを変更: 引数inplace

fillna()fill(), bfill()はデフォルトでは新しいオブジェクトを返し、元のオブジェクトは変更されない。引数inplaceTrueとすると元のオブジェクト自体が変更される。

例はfillna()だが、fill()bfill()でも同様。

df.fillna(0, inplace=True)
print(df)
#       name   age state  point  other
# 0    Alice  24.0    NY    0.0    0.0
# 1        0   0.0     0    0.0    0.0
# 2  Charlie   0.0    CA    0.0    0.0
# 3     Dave  68.0    TX   70.0    0.0
# 4    Ellen   0.0    CA   88.0    0.0
# 5    Frank  30.0     0    0.0    0.0

pandas.Seriesのfillna(), ffill(), bfill()

Seriesの場合もこれまでのDataFrameの例と同様にfillna()が使える。

s = pd.read_csv('data/src/sample_pandas_normal_nan.csv')['age']
print(s)
# 0    24.0
# 1     NaN
# 2     NaN
# 3    68.0
# 4     NaN
# 5    30.0
# Name: age, dtype: float64

print(s.fillna(100))
# 0     24.0
# 1    100.0
# 2    100.0
# 3     68.0
# 4    100.0
# 5     30.0
# Name: age, dtype: float64

print(s.fillna({1: 100, 4: -100}))
# 0     24.0
# 1    100.0
# 2      NaN
# 3     68.0
# 4   -100.0
# 5     30.0
# Name: age, dtype: float64

ffill()bfill()も提供されている。

print(s.ffill(limit=1))
# 0    24.0
# 1    24.0
# 2     NaN
# 3    68.0
# 4    68.0
# 5    30.0
# Name: age, dtype: float64

print(s.bfill(limit=1))
# 0    24.0
# 1     NaN
# 2    68.0
# 3    68.0
# 4    30.0
# 5    30.0
# Name: age, dtype: float64

pad(), backfill()もあるが、バージョン2.0.0からDeprecated(非推奨)になっている。

fillna()の引数methodはバージョン2.1.0でDeprecated(非推奨)になった。バージョン2.1.4ではまだ使用可能だが、FutureWarningが出る。

print(s.fillna(method='ffill', limit=1))
# 0    24.0
# 1    24.0
# 2     NaN
# 3    68.0
# 4    68.0
# 5    30.0
# Name: age, dtype: float64
# 
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_50534/2241812369.py:1: FutureWarning: Series.fillna with 'method' is deprecated and will raise in a future version. Use obj.ffill() or obj.bfill() instead.

関連カテゴリー

関連記事