pandasで欠損値NaNを置換(穴埋め)するfillna
pandasでDataFrame
やSeries
の欠損値NaN
を任意の値に置換(穴埋め、代入)するにはfillna()
メソッドを使う。
- pandas.DataFrame.fillna — pandas 2.1.4 documentation
- pandas.Series.fillna — pandas 2.1.4 documentation
単純な置換ではなく前後の値から補間するにはinterpolate()
を使う。
欠損値NaN
の抽出・削除・カウントについては以下の記事を参照。
- 関連記事: pandasで欠損値NaNを含む行・列を抽出
- 関連記事: pandasで欠損値NaNを削除(除外)するdropna
- 関連記事: pandasで欠損値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_only
をTrue
とすると対象を数値列に限定できる。返り値はSeries
。
print(df.mean(numeric_only=True))
# age 40.666667
# point 79.000000
# other NaN
# dtype: float64
このSeries
をfillna()
の第一引数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()
メソッドを使う。偶数個の場合は中央二つの値の平均値が中央値となる。
- 関連記事: pandasで中央値を取得する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()
メソッドを使う。
- pandas.DataFrame.ffill — pandas 2.1.4 documentation
- pandas.DataFrame.bfill — pandas 2.1.4 documentation
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
引数axis
を1
または'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(非推奨)になっている。
- pandas.DataFrame.pad — pandas 2.1.4 documentation
- pandas.DataFrame.backfill — pandas 2.1.4 documentation
fillna()の引数methodはバージョン2.1.0でDeprecated(非推奨)
バージョン2.1.0でDeprecated(非推奨)になったが、fillna()
の引数method
, limit
を指定することでffill()
, bfill()
と同じ処理が可能。
- What’s new in 2.1.0 (Aug 30, 2023) — pandas 2.1.4 documentation
- DEPR: fillna 'method' · Issue #53394 · pandas-dev/pandas
引数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()
はデフォルトでは新しいオブジェクトを返し、元のオブジェクトは変更されない。引数inplace
をTrue
とすると元のオブジェクト自体が変更される。
例は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.