Pythonのfilter()でリストから条件を満たす要素を抽出・削除
Pythonのfilter()
を使うと、イテラブル(リストやタプルなど)から条件を満たす要素を抽出したり削除したりできる。
なお、最後に述べるように、filter()
の処理はリスト内包表記やジェネレータ式で代用可能であり、多くの場合、リスト内包表記・ジェネレータ式を使用するほうが好ましいとされている。
- 関連記事: Pythonリスト内包表記の使い方
リスト内包表記を使ったリストの要素の抽出などについての詳細は以下の記事を参照。
filter()の基本的な使い方
filter()
の第一引数に関数(呼び出し可能オブジェクト)、第二引数にリストなどのイテラブルオブジェクトを指定する。イテラブルの要素に関数を適用し、結果がTrue
と判定されたものを抽出する。
Python3のfilter()はイテレータを返す
値が偶数(2で割ったときのあまりが0)のときTrue
を返すlambda
(ラムダ式、無名関数)を例とする。
Python3のfilter()
はfilter
型のオブジェクト(イテレータ)を返し、そのままprint()
しても中身の値は出力されない。
l = [-2, -1, 0, 1, 2]
print(filter(lambda x: x % 2 == 0, l))
# <filter object at 0x10bb38580>
print(type(filter(lambda x: x % 2 == 0, l)))
# <class 'filter'>
イテレータの値はfor
文などで取り出せる。
for i in filter(lambda x: x % 2 == 0, l):
print(i)
# -2
# 0
# 2
なお、Python2のfilter()
はリストを返す。Python2のコードをPython3で実行する場合は注意。
リストに変換
結果をリストに変換したい場合はlist()
を使う。
l = [-2, -1, 0, 1, 2]
print(list(filter(lambda x: x % 2 == 0, l)))
# [-2, 0, 2]
以降のサンプルコードでは、便宜上、リストに変換した結果を出力する。
条件による要素の抽出・削除
filter()
は第一引数の処理の結果がTrue
と判定される要素を抽出する。所望の要素を削除したい場合は、条件が反対となるように第一引数を指定すればよい。
例えば、「偶数の要素を削除する」という処理は「奇数の要素を抽出する」という処理と同等。
l = [-2, -1, 0, 1, 2]
print(list(filter(lambda x: x % 2 == 0, l)))
# [-2, 0, 2]
print(list(filter(lambda x: x % 2 != 0, l)))
# [-1, 1]
上の例のように、反対の結果となる比較演算子(==
と!=
、>
と<=
など)に置き換えてもよいし、否定を表すnot
を使ってもよい。
'e'
で終わる文字列を抽出する例と削除する例は以下の通り。
l_s = ['apple', 'orange', 'strawberry']
print(list(filter(lambda x: x.endswith('e'), l_s)))
# ['apple', 'orange']
print(list(filter(lambda x: not x.endswith('e'), l_s)))
# ['strawberry']
filter()
とは反対にFalse
となる要素を残すitertools.filterfalse()
という関数もある。条件がややこしい場合はこちらのほうが簡単。後述する。
lambda(ラムダ式、無名関数)、defで定義した関数を適用
filter()
の第一引数には呼び出し可能オブジェクトを指定する。これまでの例のように、lambda
(ラムダ式、無名関数)を使うことが多いが、def
で定義した関数を指定することももちろん可能。
def is_even(x):
return x % 2 == 0
l = [-2, -1, 0, 1, 2]
print(list(filter(is_even, l)))
# [-2, 0, 2]
複数条件を適用
複数条件を適用したい場合は、そのようなラムダ式や関数を指定すればよい。
and
(かつ)やor
(または)で複数の条件を繋げられる。
l = [-2, -1, 0, 1, 2]
print(list(filter(lambda x: x % 2 == 0 and x > 0, l)))
# [2]
print(list(filter(lambda x: x % 2 == 0 or x > 0, l)))
# [-2, 0, 1, 2]
第一引数にNoneを指定した場合
filter()
の第一引数にNone
を指定した場合、True
と判定される要素が抽出される。
l_b = [True, False]
print(list(filter(None, l_b)))
# [True]
True
, False
そのものだけでなく、数値やリスト、文字列なども判定される。
例えば、数値の場合は0
がFalse
でそれ以外はTrue
、リストや文字列の場合は空だとFalse
でそれ以外はTrue
と判定される。詳細は以下の記事を参照。
したがって、filter()
の第一引数にNone
を指定すると、0
や空のリスト、文字列を除去する処理となる。
l = [-2, -1, 0, 1, 2]
print(list(filter(None, l)))
# [-2, -1, 1, 2]
l_2d = [[0, 1, 2], [], [3, 4, 5]]
print(list(filter(None, l_2d)))
# [[0, 1, 2], [3, 4, 5]]
l_s = ['apple', '', 'orange', 'strawberry']
print(list(filter(None, l_s)))
# ['apple', 'orange', 'strawberry']
Falseとなる要素を抽出: itertools.filterfalse()
filter()
の反対で、False
となる要素を抽出するitertools.filterfalse()
も提供されている。
使い方はfilter()
と同じ。itertools
をインポートする必要がある。
import itertools
l = [-2, -1, 0, 1, 2]
print(list(itertools.filterfalse(lambda x: x % 2 == 0, l)))
# [-1, 1]
print(list(itertools.filterfalse(lambda x: x % 2 != 0, l)))
# [-2, 0, 2]
l_s = ['apple', 'orange', 'strawberry']
print(list(itertools.filterfalse(lambda x: x.endswith('e'), l_s)))
# ['strawberry']
上述の通り、filter()
でも第一引数の処理を変えれば同じ結果を得られるが、場合によってはfilter()
とnot
を使うよりもitertools.filterfalse()
のほうが意図が明確なコードが書ける。
itertools.filterfalse()
の第一引数をNone
とした場合、False
と判定される要素が抽出される。あまり使い所はないかもしれない。
l = [-2, -1, 0, 1, 2]
print(list(itertools.filterfalse(None, l)))
# [0]
リスト内包表記・ジェネレータ式で代用
filter()
と同等の処理はif
を使ったリスト内包表記やジェネレータ式でも実現できる。
filter(function, iterable)
は、関数がNone
でなければジェネレータ式(item for item in iterable if function(item))
と同等で、関数がNone
なら(item for item in iterable if item)
と同等です。 組み込み関数 - filter() — Python 3.11.3 ドキュメント
l = [-2, -1, 0, 1, 2]
print([x for x in l if x % 2 == 0])
# [-2, 0, 2]
print([x for x in l if x % 2 != 0])
# [-1, 1]
l_s = ['apple', 'orange', 'strawberry']
print([x for x in l_s if x.endswith('e')])
# ['apple', 'orange']
print([x for x in l_s if not x.endswith('e')])
# ['strawberry']
l = [-2, -1, 0, 1, 2]
print([x for x in l if x])
# [-2, -1, 1, 2]
l_2d = [[0, 1, 2], [], [3, 4, 5]]
print([x for x in l_2d if x])
# [[0, 1, 2], [3, 4, 5]]
list(filter())
のようにリストを取得したい場合はリスト内包表記、filter()
のようにイテレータを取得したい場合はジェネレータ式を使えばよい。
- 関連記事: Pythonリスト内包表記の使い方
以下のStack Overflowの質問にもあるように、ほとんどの場合、filter()
よりもリスト内包表記・ジェネレータ式を使うほうがコードが簡潔・明解で好ましいとされている。
書籍『Effective Python』でもfilter()
よりもリスト内包表記を使うべきと書かれている。
処理速度が重要な状況でどちらを使うか迷っている場合、処理速度は様々な要因で変動する可能性もあるので、なるべく想定に近い環境および処理で実際に計測してみることをおすすめする。