pandasで条件に応じて値を置換(where, mask)
pandasで条件に応じて値を置換する方法を説明する。if文を使うわけではないが、DataFrame
やSeries
に対してif then ...
やif then ... else ...
のような条件分岐の処理が可能。
条件がFalse
の要素を置換するにはwhere()
メソッド、True
の要素を置換するにはmask()
メソッド、True
とFalse
の要素をどちらも置換するにはNumPyのnp.where()
関数を使う。
loc
やiloc
のブーリアンインデックスによって条件に応じて値を代入することもできる。
特定の値の置換、欠損値NaN
の置換や削除については以下の記事を参照。
- 関連記事: pandas.DataFrame, Seriesの要素の値を置換するreplace
- 関連記事: pandasで欠損値NaNを置換(穴埋め)するfillna
- 関連記事: pandasで欠損値NaNを削除(除外)するdropna
本記事のサンプルコードのpandasとNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import pandas as pd
import numpy as np
print(pd.__version__)
# 2.1.4
print(np.__version__)
# 1.26.2
where()メソッド: Trueはそのまま、Falseを置換
DataFrame
, Series
のメソッドとしてwhere()
が提供されている。
- pandas.DataFrame.where — pandas 2.1.4 documentation
- pandas.Series.where — pandas 2.1.4 documentation
Seriesのwhere()メソッド
以下のSeries
を例とする。
s = pd.Series([-2, -1, 0, 1, 2])
print(s)
# 0 -2
# 1 -1
# 2 0
# 3 1
# 4 2
# dtype: int64
第一引数cond
where()
の第一引数cond
にbool
型のSeries
を指定すると、True
の要素の値は呼び出し元のオブジェクトのままで、False
の要素の値はNaN
となる。
例えばSeries
に対する比較演算はbool
型のSeries
を返すので、それを第一引数に指定できる。
print(s < 0)
# 0 True
# 1 True
# 2 False
# 3 False
# 4 False
# dtype: bool
print(s.where(s < 0))
# 0 -2.0
# 1 -1.0
# 2 NaN
# 3 NaN
# 4 NaN
# dtype: float64
比較演算だけでなく、文字列に対する条件なども指定可能。次節のmask()
の例を参照。
bool
値を要素とするリストやNumPy配列ndarray
などのarray-likeオブジェクトも指定できる。
print(s.where([False, True, False, True, False]))
# 0 NaN
# 1 -1.0
# 2 NaN
# 3 1.0
# 4 NaN
# dtype: float64
第二引数other
第二引数other
にスカラー値を指定すると、False
の要素がその値に置換される。
print(s.where(s < 0, 10))
# 0 -2
# 1 -1
# 2 10
# 3 10
# 4 10
# dtype: int64
リストやNumPy配列ndarray
などのarray-likeオブジェクトも指定可能。False
の要素が同じ位置の要素に置換される。
print(s.where(s < 0, [0, 10, 20, 30, 40]))
# 0 -2
# 1 -1
# 2 20
# 3 30
# 4 40
# dtype: int64
Series
も指定できる。位置ではなくインデックス(ラベル)が一致する要素に置換される。
s2 = pd.Series([0, 10, 20, 30, 40], index=[4, 3, 2, 1, 0])
print(s2)
# 4 0
# 3 10
# 2 20
# 1 30
# 0 40
# dtype: int64
print(s.where(s < 0, s2))
# 0 -2
# 1 -1
# 2 20
# 3 10
# 4 0
# dtype: int64
元のSeries
に処理を加えて置換することも可能。
print(s * 100 + 10)
# 0 -190
# 1 -90
# 2 10
# 3 110
# 4 210
# dtype: int64
print(s.where(s < 0, s * 100 + 10))
# 0 -2
# 1 -1
# 2 10
# 3 110
# 4 210
# dtype: int64
引数inplace
デフォルトでは元のオブジェクトは変更されず新たなオブジェクトが返される。引数inplace
をTrue
とすると元のオブジェクトが更新される。
s.where(s < 0, 10, inplace=True)
print(s)
# 0 -2
# 1 -1
# 2 10
# 3 10
# 4 10
# dtype: int64
DataFrameのwhere()メソッド
以下のDataFrame
を例とする。
df = pd.DataFrame({'A': [-2, -1, 0, 1, 2], 'B': [0, 10, 20, 30, 40]})
print(df)
# A B
# 0 -2 0
# 1 -1 10
# 2 0 20
# 3 1 30
# 4 2 40
基本的な使い方
DataFrame
のwhere()
も基本的な使い方はSeries
のwhere()
と同じ。
where()
の第一引数cond
にbool
型のDataFrame
を指定すると、True
の要素の値は呼び出し元のオブジェクトのままで、False
の要素の値がNaN
に置換される。
print((df < 0) | (df > 20))
# A B
# 0 True False
# 1 True False
# 2 False False
# 3 False True
# 4 False True
print(df.where((df < 0) | (df > 20)))
# A B
# 0 -2.0 NaN
# 1 -1.0 NaN
# 2 NaN NaN
# 3 NaN 30.0
# 4 NaN 40.0
上の例では|
(または)で複数条件を組み合わせている。&
, |
ではなくand
, or
を使ったり、括弧を省略したりするとエラーになるので注意。
第二引数other
にスカラー値やDataFrame
を指定するとFalse
の要素を置換できる。
print(df.where((df < 0) | (df > 20), 100))
# A B
# 0 -2 100
# 1 -1 100
# 2 100 100
# 3 100 30
# 4 100 40
print(df * 100 + 10)
# A B
# 0 -190 10
# 1 -90 1010
# 2 10 2010
# 3 110 3010
# 4 210 4010
print(df.where((df < 0) | (df > 20), df * 100 + 10))
# A B
# 0 -2 10
# 1 -1 1010
# 2 10 2010
# 3 110 30
# 4 210 40
例は省略するが、Series
のwhere()
のように第一引数・第二引数に二次元配列やリストのリストを指定することも可能。引数inplace
も指定できる。
DataFrameの列を個別に処理
例えば、数値と文字列の列が混在しているDataFrame
に対して数値との比較演算を行うと、文字列と数値が比較されてしまいエラーとなる。
df['C'] = ['A', 'B', 'C', 'D', 'E']
print(df)
# A B C
# 0 -2 0 A
# 1 -1 10 B
# 2 0 20 C
# 3 1 30 D
# 4 2 40 E
# print(df < 0)
# TypeError: '<' not supported between instances of 'str' and 'int'
DataFrame
の列はSeries
なので、列を選択して個別に処理できる。新たな列として追加することも可能。
print(df['C'].where(df['A'] < 0, 'X'))
# 0 A
# 1 B
# 2 X
# 3 X
# 4 X
# Name: C, dtype: object
df['D'] = df['C'].where(df['A'] < 0, 'X')
print(df)
# A B C D
# 0 -2 0 A A
# 1 -1 10 B B
# 2 0 20 C X
# 3 1 30 D X
# 4 2 40 E X
数値列のみを抽出、処理してから数値以外の列と連結することもできる。select_dtypes()
メソッドとpd.concat()
関数を使う。
- 関連記事: pandas.DataFrameから特定の型の列を抽出・除外するselect_dtypes
- 関連記事: pandas.DataFrame, Seriesをソートするsort_values, sort_index
df_num = df.select_dtypes('number')
print(df_num.where(df_num > 0, -10))
# A B
# 0 -10 -10
# 1 -10 10
# 2 -10 20
# 3 1 30
# 4 2 40
print(df.select_dtypes(exclude='number'))
# C D
# 0 A A
# 1 B B
# 2 C X
# 3 D X
# 4 E X
print(pd.concat([df_num.where(df_num > 0, -10), df.select_dtypes(exclude='number')],
axis=1))
# A B C D
# 0 -10 -10 A A
# 1 -10 10 B B
# 2 -10 20 C X
# 3 1 30 D X
# 4 2 40 E X
mask()メソッド: Trueを置換、Falseはそのまま
DataFrame
, Series
のメソッドとしてmask()
が提供されている。
mask()
メソッドはwhere()
メソッドとは反対に、第一引数に指定した条件がFalse
の要素が呼び出し元のオブジェクトのままで、True
の要素がNaN
または第二引数で指定した値に置換される。
引数などの使い方はwhere()
と同じ。詳しくは前節のwhere()
の説明を参照。
以下、いくつかの例を示す。
s = pd.Series(['Alice', 'Bob', 'Charlie', 'Dave', 'Ellen'])
print(s)
# 0 Alice
# 1 Bob
# 2 Charlie
# 3 Dave
# 4 Ellen
# dtype: object
print(s.mask(s.str.endswith('e')))
# 0 NaN
# 1 Bob
# 2 NaN
# 3 NaN
# 4 Ellen
# dtype: object
print(s.mask(s.str.endswith('e'), 'X'))
# 0 X
# 1 Bob
# 2 X
# 3 X
# 4 Ellen
# dtype: object
print(s.mask(s.str.endswith('e'), s.str.upper()))
# 0 ALICE
# 1 Bob
# 2 CHARLIE
# 3 DAVE
# 4 Ellen
# dtype: object
df = pd.DataFrame({'A': [-2, -1, 0, 1, 2], 'B': [0, 10, 20, 30, 40]})
print(df)
# A B
# 0 -2 0
# 1 -1 10
# 2 0 20
# 3 1 30
# 4 2 40
print(df.mask((df < 0) | (df > 20)))
# A B
# 0 NaN 0.0
# 1 NaN 10.0
# 2 0.0 20.0
# 3 1.0 NaN
# 4 2.0 NaN
print(df.mask((df < 0) | (df > 20), 100))
# A B
# 0 100 0
# 1 100 10
# 2 0 20
# 3 1 100
# 4 2 100
print(df.mask((df < 0) | (df > 20), df * 100 + 10))
# A B
# 0 -190 0
# 1 -90 10
# 2 0 20
# 3 1 3010
# 4 2 4010
Series
に対する例では文字列に対する後方一致を条件としている。完全一致や前方一致、正規表現による判定も可能。以下の記事を参照。
np.where()関数: True, Falseをそれぞれ置換
NumPyのnp.where()
関数を利用して、DataFrame
やSeries
に対して条件に応じた値の置換を行うことができる。
pandasのwhere()
またはmask()
メソッドでは、条件がTrue
かFalse
いずれかのみが置換され、他方は元のオブジェクトの値がそのまま使われる。True
とFalse
を同時に置換することはできない。
NumPyのnp.where()
関数では、第一引数に条件、第二引数にTrue
を置換する値、第三引数にFalse
を置換する値を指定する。第二・第三引数にはスカラー値や配列、Series
やDataFrame
を指定可能。
s = pd.Series([-2, -1, 0, 1, 2])
print(s)
# 0 -2
# 1 -1
# 2 0
# 3 1
# 4 2
# dtype: int64
print(np.where(s < 0, -100, 1))
# [-100 -100 1 1 1]
print(np.where(s < 0, s * 10, s * 100 + 10))
# [-20 -10 10 110 210]
print(type(np.where(s < 0, -100, 1)))
# <class 'numpy.ndarray'>
df = pd.DataFrame({'A': [-2, -1, 0, 1, 2], 'B': [0, 10, 20, 30, 40]})
print(df)
# A B
# 0 -2 0
# 1 -1 10
# 2 0 20
# 3 1 30
# 4 2 40
print(np.where((df < 0) | (df > 20), -100, 1))
# [[-100 1]
# [-100 1]
# [ 1 1]
# [ 1 -100]
# [ 1 -100]]
print(np.where((df < 0) | (df > 20), df * 10, df * 100 + 10))
# [[ -20 10]
# [ -10 1010]
# [ 10 2010]
# [ 110 300]
# [ 210 400]]
print(type(np.where((df < 0) | (df > 20), -100, 1)))
# <class 'numpy.ndarray'>
np.where()
の返り値はNumPy配列ndarray
。元のDataFrame
やSeries
のindex
やcolumns
属性を使って、DataFrame
やSeries
を生成できる。
print(pd.Series(np.where(s < 0, -100, 1), index=s.index))
# 0 -100
# 1 -100
# 2 1
# 3 1
# 4 1
# dtype: int64
print(pd.DataFrame(np.where((df < 0) | (df > 20), -100, 1),
index=df.index, columns=df.columns))
# A B
# 0 -100 1
# 1 -100 1
# 2 1 1
# 3 1 -100
# 4 1 -100
新たな列として追加する場合は、右辺にndarray
をそのまま指定できる。
df['C'] = np.where(df['A'] < 0, -100, 1)
print(df)
# A B C
# 0 -2 0 -100
# 1 -1 10 -100
# 2 0 20 1
# 3 1 30 1
# 4 2 40 1
loc, ilocのブーリアンインデックスによる値の代入
loc
やiloc
にbool
型のSeries
や配列を指定すると、True
の位置の要素を抽出できる(ブーリアンインデックス)。
df = pd.DataFrame({'A': [-2, -1, 0, 1, 2], 'B': [0, 10, 20, 30, 40]})
print(df)
# A B
# 0 -2 0
# 1 -1 10
# 2 0 20
# 3 1 30
# 4 2 40
print(df.loc[df['A'] < 0, 'A'])
# 0 -2
# 1 -1
# Name: A, dtype: int64
loc
, iloc
での参照は値の取得だけでなく代入にも使える。右辺にスカラー値やSeries
、配列などを指定可能。
df.loc[df['A'] < 0, 'A'] = -10
print(df)
# A B
# 0 -10 0
# 1 -10 10
# 2 0 20
# 3 1 30
# 4 2 40
df.loc[df['A'] >= 0, 'A'] = df['B'] * 10
print(df)
# A B
# 0 -10 0
# 1 -10 10
# 2 200 20
# 3 300 30
# 4 400 40
新しい列名を指定すると新しい列が追加される。条件を満たさない要素は欠損値NaN
となる。NaN
を含む列の型dtype
はfloat
になるので注意。
df.loc[df['A'] < 0, 'C'] = -100
print(df)
# A B C
# 0 -10 0 -100.0
# 1 -10 10 -100.0
# 2 200 20 NaN
# 3 300 30 NaN
# 4 400 40 NaN