【Python入門】filter()関数の使い方と実践例|リストから条件でデータを抽出しよう!

python

「リストの中から特定の条件に合うデータだけを取り出したい」

Pythonを使っていると、そんな場面に必ず出会います。

よくある処理例:

  • 偶数だけを抽出したい
  • 空文字列を除去したい
  • 特定の条件に合うユーザーだけを取得したい
  • ログから特定のレベルだけを表示したい

従来の方法との比較:

# for文での書き方(冗長)
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = []
for n in numbers:
    if n % 2 == 0:
        even_numbers.append(n)

# filter()なら1行でスッキリ
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))

そんなときに便利なのが、組み込み関数filter()です。

for文やリスト内包表記よりもスッキリ書けて、読みやすく効率的なコードにすることができます。

この記事では、Pythonのfilter()関数の基本構文・使い方・ラムダ式との組み合わせ方・注意点・実践例までを、初心者向けにやさしく解説します。

スポンサーリンク

第1章:filter()関数とは?基本構文と使い方

filter()の基本構文

filter(関数, イテラブル)

パラメータの説明:

  • 関数:各要素に対してTrue/Falseを返す関数
  • イテラブル:リストやタプル、文字列など反復可能なオブジェクト

戻り値: フィルタされた要素(引数の関数でTrueを返した要素)を含むイテレータ(filterオブジェクト)

基本的な使用例

例1:偶数だけを取り出す

def is_even(n):
    """偶数かどうかを判定する関数"""
    return n % 2 == 0

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = filter(is_even, numbers)

# 結果をリストに変換して表示
print(list(result))  # [2, 4, 6, 8, 10]

例2:正の数だけを取り出す

def is_positive(n):
    """正の数かどうかを判定する関数"""
    return n > 0

numbers = [-3, -1, 0, 1, 2, 5]
positive_numbers = list(filter(is_positive, numbers))
print(positive_numbers)  # [1, 2, 5]

例3:文字列の長さでフィルタ

def is_long_word(word):
    """4文字以上の単語かどうかを判定"""
    return len(word) >= 4

words = ["cat", "dog", "elephant", "bird", "butterfly"]
long_words = list(filter(is_long_word, words))
print(long_words)  # ['elephant', 'bird', 'butterfly']

filter()の動作イメージ

# filter()が内部で行っている処理のイメージ
def my_filter(func, iterable):
    """filter()の簡単な実装例"""
    result = []
    for item in iterable:
        if func(item):  # 関数がTrueを返す場合のみ
            result.append(item)
    return result

# 使用例
numbers = [1, 2, 3, 4, 5]
result = my_filter(lambda x: x % 2 == 0, numbers)
print(result)  # [2, 4]

第2章:filter() × lambda の書き方と活用例

ラムダ式とは?

ラムダ式は、関数をその場で定義するための匿名関数です。

# 通常の関数定義
def double(x):
    return x * 2

# ラムダ式での定義
double_lambda = lambda x: x * 2

print(double(5))        # 10
print(double_lambda(5)) # 10

filter()とlambdaの組み合わせ

例1:偶数の抽出をlambdaで

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 通常の関数を使った場合
def is_even(n):
    return n % 2 == 0
result1 = list(filter(is_even, numbers))

# lambdaを使った場合(1行で完結)
result2 = list(filter(lambda x: x % 2 == 0, numbers))

print(result1)  # [2, 4, 6, 8, 10]
print(result2)  # [2, 4, 6, 8, 10]

例2:文字列のフィルタリング

# 特定の文字を含む単語を抽出
words = ["apple", "banana", "avocado", "berry", "cherry"]

# 'a'を含む単語を抽出
words_with_a = list(filter(lambda word: "a" in word, words))
print(words_with_a)  # ['apple', 'banana', 'avocado']

# 5文字以上の単語を抽出
long_words = list(filter(lambda word: len(word) >= 5, words))
print(long_words)  # ['apple', 'banana', 'avocado', 'berry', 'cherry']

# 'b'で始まる単語を抽出
b_words = list(filter(lambda word: word.startswith('b'), words))
print(b_words)  # ['banana', 'berry']

例3:数値の範囲指定

temperatures = [15, 22, 28, 35, 18, 42, 12, 30]

# 20度以上30度以下の快適な温度を抽出
comfortable_temps = list(filter(lambda temp: 20 <= temp <= 30, temperatures))
print(comfortable_temps)  # [22, 28, 30]

# 極端な温度(15度以下または35度以上)を抽出
extreme_temps = list(filter(lambda temp: temp <= 15 or temp >= 35, temperatures))
print(extreme_temps)  # [15, 35, 42, 12]

複雑な条件での活用

# 辞書のリストから特定の条件でフィルタ
students = [
    {"name": "Alice", "age": 20, "grade": 85},
    {"name": "Bob", "age": 22, "grade": 75},
    {"name": "Charlie", "age": 19, "grade": 95},
    {"name": "Diana", "age": 21, "grade": 65}
]

# 80点以上の学生を抽出
high_achievers = list(filter(lambda student: student["grade"] >= 80, students))
print(high_achievers)
# [{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Charlie', 'age': 19, 'grade': 95}]

# 20歳以上で70点以上の学生を抽出
qualified_students = list(filter(
    lambda student: student["age"] >= 20 and student["grade"] >= 70, 
    students
))
print(qualified_students)
# [{'name': 'Alice', 'age': 20, 'grade': 85}, {'name': 'Bob', 'age': 22, 'grade': 75}]

第3章:filterとリスト内包表記の違いと使い分け

同じ処理の異なる書き方

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 方法1: filter() + lambda
even_numbers_filter = list(filter(lambda x: x % 2 == 0, numbers))

# 方法2: リスト内包表記
even_numbers_comprehension = [x for x in numbers if x % 2 == 0]

# 方法3: 従来のfor文
even_numbers_loop = []
for x in numbers:
    if x % 2 == 0:
        even_numbers_loop.append(x)

print(even_numbers_filter)        # [2, 4, 6, 8, 10]
print(even_numbers_comprehension) # [2, 4, 6, 8, 10]
print(even_numbers_loop)          # [2, 4, 6, 8, 10]

filter()の利点

1. 可読性が高い

# filter(): 「フィルタする処理」だと一目で分かる
valid_emails = list(filter(is_valid_email, email_list))

# リスト内包表記: 慣れていないと読みにくい場合がある
valid_emails = [email for email in email_list if is_valid_email(email)]

2. 関数の再利用性

def is_adult(person):
    return person["age"] >= 18

# 複数の場所で同じ条件を使い回せる
adults_list1 = list(filter(is_adult, customers))
adults_list2 = list(filter(is_adult, employees))
adults_list3 = list(filter(is_adult, visitors))

3. 関数型プログラミングのスタイル

# map(), filter(), reduce() の組み合わせ
from functools import reduce

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

result = reduce(
    lambda x, y: x + y,  # 合計を計算
    map(
        lambda x: x ** 2,  # 二乗する
        filter(lambda x: x % 2 == 0, numbers)  # 偶数だけ抽出
    )
)
print(result)  # 220 (2^2 + 4^2 + 6^2 + 8^2 + 10^2)

リスト内包表記の利点

1. 結果がすぐリストになる

# filter(): list()で変換が必要
result = list(filter(lambda x: x > 0, numbers))

# リスト内包表記: 直接リストが得られる
result = [x for x in numbers if x > 0]

2. 変換とフィルタが同時にできる

numbers = [1, 2, 3, 4, 5]

# リスト内包表記: フィルタ + 変換が1行
squares_of_evens = [x**2 for x in numbers if x % 2 == 0]

# filter() + map(): 2段階の処理
squares_of_evens = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers)))

3. Pythonらしい記述

# Pythonコミュニティでよく見かける書き方
positive_numbers = [x for x in numbers if x > 0]

使い分けの指針

条件おすすめ理由
シンプルな条件リスト内包表記簡潔で読みやすい
複雑な条件関数filter()関数の再利用性
フィルタのみfilter()意図が明確
フィルタ + 変換リスト内包表記1行で完結
関数型スタイルfilter()map(), reduce()との組み合わせ
大量データfilter()イテレータによるメモリ効率

第4章:filter()を使うときの注意点とよくあるミス

注意①:filterの戻り値はリストではなく「イテレータ」

numbers = [-1, 0, 1, 2, 3]
result = filter(lambda x: x > 0, numbers)

print(result)        # <filter object at 0x...>
print(type(result))  # <class 'filter'>

# リストとして使いたい場合は変換が必要
result_list = list(result)
print(result_list)   # [1, 2, 3]
print(type(result_list))  # <class 'list'>

注意②:イテレータは一度使うと使い切り(再利用不可)

numbers = [-1, 0, 1, 2, 3]
filtered = filter(lambda x: x > 0, numbers)

print(list(filtered))  # [1, 2, 3]
print(list(filtered))  # [] ← 空になる!

# 再利用したい場合はリストに変換
filtered_list = list(filter(lambda x: x > 0, numbers))
print(filtered_list)  # [1, 2, 3]
print(filtered_list)  # [1, 2, 3] ← 何度でも使える

注意③:関数がNoneの場合の特殊な動作

# Noneを渡すと「真偽値がTrue」の要素を残す
data = ["", "hello", 0, 42, None, "world", False, True]
result = list(filter(None, data))
print(result)  # ['hello', 42, 'world', True]

# これは以下と同じ意味
result = list(filter(lambda x: bool(x), data))
print(result)  # ['hello', 42, 'world', True]

真偽値の判定基準:

# False として判定される値
falsy_values = [False, 0, 0.0, "", [], {}, None]
for value in falsy_values:
    print(f"{repr(value)}: {bool(value)}")

# 出力:
# False: False
# 0: False
# 0.0: False
# '': False
# []: False
# {}: False
# None: False

注意④:変更可能なオブジェクトの扱い

# リストの要素が変更可能オブジェクトの場合
people = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 17},
    {"name": "Charlie", "age": 30}
]

adults = list(filter(lambda person: person["age"] >= 18, people))
print(adults)
# [{'name': 'Alice', 'age': 25}, {'name': 'Charlie', 'age': 30}]

# 元のリストと同じオブジェクトを参照している
adults[0]["age"] = 26
print(people[0])  # {'name': 'Alice', 'age': 26} ← 元のリストも変更される

# 新しいオブジェクトを作りたい場合
import copy
adults_copy = [copy.deepcopy(person) for person in people if person["age"] >= 18]

パフォーマンスに関する注意

import time

# 大量データでのパフォーマンス比較
large_numbers = list(range(1000000))

# filter() + list() の場合
start = time.time()
result1 = list(filter(lambda x: x % 2 == 0, large_numbers))
time1 = time.time() - start

# リスト内包表記の場合
start = time.time()
result2 = [x for x in large_numbers if x % 2 == 0]
time2 = time.time() - start

print(f"filter(): {time1:.4f}秒")
print(f"リスト内包表記: {time2:.4f}秒")
# 一般的にリスト内包表記の方が高速

第5章:実践的なfilter()の活用例

データクリーニング

# CSVデータの前処理例
raw_data = [
    {"name": "Alice", "email": "alice@example.com", "age": 25},
    {"name": "", "email": "invalid-email", "age": 30},
    {"name": "Bob", "email": "bob@example.com", "age": -5},
    {"name": "Charlie", "email": "charlie@example.com", "age": 35},
    {"name": None, "email": "", "age": 40}
]

def is_valid_record(record):
    """有効なレコードかどうかを判定"""
    return (
        record.get("name") and  # 名前が存在し、空でない
        "@" in record.get("email", "") and  # 有効なメール形式
        record.get("age", 0) > 0  # 年齢が正の数
    )

clean_data = list(filter(is_valid_record, raw_data))
print(clean_data)
# [{'name': 'Alice', 'email': 'alice@example.com', 'age': 25},
#  {'name': 'Charlie', 'email': 'charlie@example.com', 'age': 35}]

ログファイルの解析

# ログエントリの処理例
log_entries = [
    {"timestamp": "2024-01-01 10:00:00", "level": "INFO", "message": "Application started"},
    {"timestamp": "2024-01-01 10:01:00", "level": "DEBUG", "message": "Processing request"},
    {"timestamp": "2024-01-01 10:02:00", "level": "ERROR", "message": "Database connection failed"},
    {"timestamp": "2024-01-01 10:03:00", "level": "WARNING", "message": "High memory usage"},
    {"timestamp": "2024-01-01 10:04:00", "level": "ERROR", "message": "Timeout occurred"}
]

# エラーレベルのログのみ抽出
error_logs = list(filter(lambda log: log["level"] == "ERROR", log_entries))
print("エラーログ:")
for log in error_logs:
    print(f"  {log['timestamp']}: {log['message']}")

# WARNING以上のレベル(WARNING, ERROR)を抽出
important_levels = ["WARNING", "ERROR"]
important_logs = list(filter(lambda log: log["level"] in important_levels, log_entries))
print("\n重要なログ:")
for log in important_logs:
    print(f"  [{log['level']}] {log['timestamp']}: {log['message']}")

Webアプリケーションでの使用例

# ユーザー管理システムの例
users = [
    {"id": 1, "username": "alice", "email": "alice@example.com", "is_active": True, "role": "admin"},
    {"id": 2, "username": "bob", "email": "bob@example.com", "is_active": False, "role": "user"},
    {"id": 3, "username": "charlie", "email": "charlie@example.com", "is_active": True, "role": "user"},
    {"id": 4, "username": "diana", "email": "diana@example.com", "is_active": True, "role": "moderator"}
]

def get_active_users(users):
    """アクティブなユーザーのみを取得"""
    return list(filter(lambda user: user["is_active"], users))

def get_admins(users):
    """管理者のみを取得"""
    return list(filter(lambda user: user["role"] == "admin", users))

def search_users_by_email_domain(users, domain):
    """指定ドメインのメールアドレスを持つユーザーを検索"""
    return list(filter(lambda user: user["email"].endswith(f"@{domain}"), users))

# 使用例
active_users = get_active_users(users)
admins = get_admins(users)
example_users = search_users_by_email_domain(users, "example.com")

print(f"アクティブユーザー: {len(active_users)}人")
print(f"管理者: {len(admins)}人")
print(f"example.comドメインのユーザー: {len(example_users)}人")

ファイル処理での活用

import os
from pathlib import Path

def filter_files_by_extension(directory, extension):
    """指定した拡張子のファイルのみを取得"""
    all_files = os.listdir(directory)
    return list(filter(lambda filename: filename.endswith(extension), all_files))

def filter_large_files(file_paths, min_size_mb=1):
    """指定サイズ以上のファイルのみを取得"""
    min_size_bytes = min_size_mb * 1024 * 1024
    
    def is_large_file(filepath):
        try:
            return os.path.getsize(filepath) >= min_size_bytes
        except OSError:
            return False
    
    return list(filter(is_large_file, file_paths))

# 使用例(実際のディレクトリがある場合)
# python_files = filter_files_by_extension(".", ".py")
# large_files = filter_large_files(python_files, 0.1)  # 0.1MB以上

まとめ:filter()を使いこなして効率的な条件抽出をしよう!

filter()は、特定の条件に一致するデータだけを取り出すのに最適なPythonの組み込み関数です。

特に、lambdaと組み合わせることで、シンプルかつ読みやすいコードを実現できます。

本記事の重要ポイント

基本的な使い方:

  • filter(関数, イテラブル) で条件に合う要素だけを抽出
  • 戻り値はイテレータなので、リストが必要なら list() で変換
  • lambdaとの組み合わせで簡潔な記述が可能

特殊な使い方:

  • filter(None, iterable) で真偽値がTrueの要素のみを抽出
  • 複雑な条件も関数として定義すれば再利用可能

注意すべきポイント:

  • イテレータは使い切り:再利用したい場合はリストに変換
  • パフォーマンス:大量データではリスト内包表記の方が高速な場合も
  • オブジェクトの参照:フィルタ結果も元のオブジェクトを参照

コメント

タイトルとURLをコピーしました