Python filter関数入門|条件に合う要素だけを抽出
Python filter関数の基本的な使い方を初心者向けに解説。条件関数の作成、lambda式との組み合わせ、リスト内包表記との比較、実践的な活用方法まで詳しく紹介します。
みなさん、Pythonでリストから特定の条件に合う要素だけを取り出したいと思ったことはありませんか?
「偶数だけを抽出したい」 「特定の文字を含む文字列だけを選びたい」 「条件に合うデータだけをフィルタリングしたい」
そんな時に役立つのがfilter関数です。 でも大丈夫です!
この記事では、filter関数の基本から実践的な使い方まで、初心者向けに分かりやすく解説します。 コード例をたくさん使って、一緒に理解していきましょう!
filter関数って何?
filter関数は、条件に合う要素だけを抽出してくれる便利な関数です。 簡単に言うと、「フィルター」の役割をしてくれます。
基本的な仕組み
まず、シンプルな例から見てみましょう:
# 正の数だけを抽出する関数def is_positive(number): return number > 0
# リストから正の数だけを取り出すnumbers = [-3, -1, 0, 2, 5, 8, -2]positive_numbers = list(filter(is_positive, numbers))
print(f"元のリスト: {numbers}")print(f"正の数のみ: {positive_numbers}")
実行結果:
元のリスト: [-3, -1, 0, 2, 5, 8, -2]
正の数のみ: [2, 5, 8]
**filter(関数, リスト)**という形で使います。 関数がTrueを返す要素だけが残ります。
filter関数の書き方
filter関数の基本的な構文はこんな感じです:
filter(条件を判定する関数, フィルタリングしたいリスト)
もう少し詳しい例を見てみましょう:
# 偶数を判定する関数def is_even(number): return number % 2 == 0
# 1から10までの数値all_numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 偶数だけを抽出even_numbers = list(filter(is_even, all_numbers))
print(f"全ての数値: {all_numbers}")print(f"偶数のみ: {even_numbers}")
実行結果:
全ての数値: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
偶数のみ: [2, 4, 6, 8, 10]
is_even関数が各数値をチェックして、偶数(2で割り切れる数)だけを抽出しています。
他の方法との比較
filter関数を使わない場合と比較してみましょう:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 方法1: for文を使った従来の方法even_traditional = []for num in numbers: if num % 2 == 0: even_traditional.append(num)
print("for文の場合:", even_traditional)
# 方法2: filter関数を使った方法even_filter = list(filter(lambda x: x % 2 == 0, numbers))print("filter関数の場合:", even_filter)
# 方法3: リスト内包表記even_comprehension = [x for x in numbers if x % 2 == 0]print("リスト内包表記:", even_comprehension)
実行結果:
for文の場合: [2, 4, 6, 8, 10]
filter関数の場合: [2, 4, 6, 8, 10]
リスト内包表記: [2, 4, 6, 8, 10]
結果は同じですが、filter関数は条件が明確で読みやすいのが特徴です。
基本的な使い方
filter関数の基本的な使い方を、具体例とともに学んでいきましょう。
数値のフィルタリング
まず、数値を扱う基本的な例から始めます:
data = [-10, -5, -1, 0, 1, 3, 5, 8, 10, 15, 20]print("元のデータ:", data)
# 正の数だけpositive = list(filter(lambda x: x > 0, data))print(f"正の数: {positive}")
# 負の数だけnegative = list(filter(lambda x: x < 0, data))print(f"負の数: {negative}")
# 5以上10以下の数range_5_to_10 = list(filter(lambda x: 5 <= x <= 10, data))print(f"5以上10以下: {range_5_to_10}")
実行結果:
元のデータ: [-10, -5, -1, 0, 1, 3, 5, 8, 10, 15, 20]
正の数: [1, 3, 5, 8, 10, 15, 20]
負の数: [-10, -5, -1]
5以上10以下: [5, 8, 10]
lambda x: 条件という書き方で、簡単な条件を指定できます。
文字列のフィルタリング
文字列でもfilter関数を活用できます:
words = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape"]print("単語リスト:", words)
# 5文字以上の単語long_words = list(filter(lambda w: len(w) >= 5, words))print(f"5文字以上: {long_words}")
# 'a'を含む単語contains_a = list(filter(lambda w: 'a' in w, words))print(f"'a'を含む: {contains_a}")
# 'e'で終わる単語ends_with_e = list(filter(lambda w: w.endswith('e'), words))print(f"'e'で終わる: {ends_with_e}")
実行結果:
単語リスト: ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig', 'grape']
5文字以上: ['apple', 'banana', 'cherry', 'elderberry', 'grape']
'a'を含む: ['apple', 'banana', 'date', 'grape']
'e'で終わる: ['apple', 'date', 'elderberry', 'grape']
文字列の長さや内容で条件を指定できます。
リストの中の辞書をフィルタリング
より実践的な例として、辞書のリストをフィルタリングしてみましょう:
students = [ {"name": "田中", "age": 20, "grade": "A"}, {"name": "佐藤", "age": 22, "grade": "B"}, {"name": "鈴木", "age": 19, "grade": "A"}, {"name": "高橋", "age": 21, "grade": "C"}, {"name": "伊藤", "age": 23, "grade": "A"}]
print("学生データ:")for student in students: print(f" {student['name']}: {student['age']}歳, {student['grade']}評価")
# 20歳以上の学生adults = list(filter(lambda s: s["age"] >= 20, students))print(f"20歳以上の学生:")for student in adults: print(f" {student['name']}: {student['age']}歳")
# A評価の学生a_grade = list(filter(lambda s: s["grade"] == "A", students))print(f"A評価の学生:")for student in a_grade: print(f" {student['name']}: {student['grade']}評価")
実行結果:
学生データ:
田中: 20歳, A評価
佐藤: 22歳, B評価
鈴木: 19歳, A評価
高橋: 21歳, C評価
伊藤: 23歳, A評価
20歳以上の学生:
田中: 20歳
佐藤: 22歳
高橋: 21歳
伊藤: 23歳
A評価の学生:
田中: A評価
鈴木: A評価
伊藤: A評価
辞書の中の特定のキーの値で条件を指定できます。
空の値やNoneを除去する
実際のデータ処理でよく使われる、空の値を除去する方法です:
mixed_data = [1, None, 3, "", 5, 0, None, 7, False, 9]print("元のデータ:", mixed_data)
# None以外の値non_none = list(filter(lambda x: x is not None, mixed_data))print(f"None以外: {non_none}")
# 「真」の値のみ(より簡潔な書き方)truthy_values = list(filter(None, mixed_data))print(f"真の値のみ: {truthy_values}")
# 空でない文字列texts = ["hello", "", "world", None, "python", "", "programming"]non_empty_texts = list(filter(lambda x: x and x.strip(), texts))print(f"空でない文字列: {non_empty_texts}")
実行結果:
元のデータ: [1, None, 3, '', 5, 0, None, 7, False, 9]
None以外: [1, 3, '', 5, 0, 7, False, 9]
真の値のみ: [1, 3, 5, 7, 9]
空でない文字列: ['hello', 'world', 'python', 'programming']
**filter(None, リスト)**は、「偽」の値(None、0、空文字列など)を除去する便利な方法です。
lambda式との組み合わせ
filter関数では、lambda式を使って条件を簡潔に書けます。 lambda式について詳しく見てみましょう。
lambdaの基本的な書き方
lambda式は、簡単な関数を一行で書く方法です:
# 普通の関数def is_even(x): return x % 2 == 0
# lambda式で同じことを書くis_even_lambda = lambda x: x % 2 == 0
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# どちらも同じ結果result1 = list(filter(is_even, numbers))result2 = list(filter(is_even_lambda, numbers))result3 = list(filter(lambda x: x % 2 == 0, numbers))
print("普通の関数:", result1)print("lambda変数:", result2)print("直接lambda:", result3)
実行結果:
普通の関数: [2, 4, 6, 8, 10]
lambda変数: [2, 4, 6, 8, 10]
直接lambda: [2, 4, 6, 8, 10]
lambda x: 条件という形で、短く書けるのが便利です。
複雑な条件をlambdaで書く
より複雑な条件も、lambdaで表現できます:
products = [ {"name": "ノートPC", "price": 80000, "category": "electronics", "stock": 5}, {"name": "マウス", "price": 2500, "category": "electronics", "stock": 20}, {"name": "本", "price": 1500, "category": "books", "stock": 0}, {"name": "コーヒー", "price": 500, "category": "food", "stock": 30}, {"name": "スマートフォン", "price": 60000, "category": "electronics", "stock": 8}]
print("商品データのフィルタリング:")
# 5000円以下の商品affordable = list(filter(lambda p: p["price"] <= 5000, products))print(f"5000円以下の商品:")for product in affordable: print(f" {product['name']}: {product['price']}円")
# 在庫ありの電子機器electronics_in_stock = list(filter( lambda p: p["category"] == "electronics" and p["stock"] > 0, products))print(f"在庫ありの電子機器:")for product in electronics_in_stock: print(f" {product['name']}: {product['stock']}個")
実行結果:
商品データのフィルタリング:
5000円以下の商品:
マウス: 2500円
本: 1500円
コーヒー: 500円
在庫ありの電子機器:
ノートPC: 5個
マウス: 20個
スマートフォン: 8個
andやorを使って、複数の条件を組み合わせできます。
より高度なlambda式
さらに複雑な条件の例も見てみましょう:
employees = [ {"name": "田中", "age": 28, "department": "開発", "salary": 5000000, "experience": 3}, {"name": "佐藤", "age": 35, "department": "営業", "salary": 4500000, "experience": 8}, {"name": "鈴木", "age": 42, "department": "開発", "salary": 7000000, "experience": 15}, {"name": "高橋", "age": 29, "department": "マーケティング", "salary": 4800000, "experience": 5}, {"name": "伊藤", "age": 38, "department": "開発", "salary": 6200000, "experience": 12}]
print("従業員データの高度なフィルタリング:")
# シニアエンジニア(開発部、30歳以上、経験10年以上)senior_engineers = list(filter( lambda emp: (emp["department"] == "開発" and emp["age"] >= 30 and emp["experience"] >= 10), employees))
print(f"シニアエンジニア:")for emp in senior_engineers: print(f" {emp['name']}: {emp['age']}歳, 経験{emp['experience']}年")
# 高収入の若手(30歳未満、年収500万以上)high_earning_young = list(filter( lambda emp: emp["age"] < 30 and emp["salary"] >= 5000000, employees))
print(f"高収入の若手:")for emp in high_earning_young: print(f" {emp['name']}: {emp['age']}歳, {emp['salary']:,}円")
実行結果:
従業員データの高度なフィルタリング:
シニアエンジニア:
鈴木: 42歳, 経験15年
伊藤: 38歳, 経験12年
高収入の若手:
田中: 28歳, 5,000,000円
複数の条件を組み合わせることで、より精密なフィルタリングができます。
lambdaの制限と対処法
lambda式では表現しにくい複雑な条件もあります。 そんな時は、普通の関数を使いましょう:
def is_valid_email(email): """メールアドレスの妥当性をチェック""" if not email or not isinstance(email, str): return False # 基本的なチェック if "@" not in email or "." not in email: return False # @の個数チェック if email.count("@") != 1: return False # 空白文字のチェック if " " in email: return False return True
# テストデータemail_candidates = [ "user@example.com", "invalid-email", "test@test.org", "user@@double.com", # 無効 "space @email.com", # 無効 "admin@site.net"]
valid_emails = list(filter(is_valid_email, email_candidates))print("有効なメールアドレス:")for email in valid_emails: print(f" {email}")
実行結果:
有効なメールアドレス:
user@example.com
test@test.org
admin@site.net
複雑な判定は、普通の関数を作った方が読みやすくなります。
実践的な活用例
filter関数の実践的な使い方を、具体例とともに見てみましょう。
データ分析での活用
売上データを分析する例です:
sales_data = [ {"date": "2024-01-15", "product": "ノートPC", "amount": 120000, "region": "東京"}, {"date": "2024-01-16", "product": "マウス", "amount": 2500, "region": "大阪"}, {"date": "2024-01-17", "product": "キーボード", "amount": 8000, "region": "東京"}, {"date": "2024-01-18", "product": "モニター", "amount": 45000, "region": "名古屋"}, {"date": "2024-01-19", "product": "ノートPC", "amount": 135000, "region": "東京"}, {"date": "2024-01-20", "product": "タブレット", "amount": 60000, "region": "大阪"}]
print("=== 売上データ分析 ===")print(f"総売上件数: {len(sales_data)}")
# 高額商品(50000円以上)high_value_sales = list(filter(lambda sale: sale["amount"] >= 50000, sales_data))print(f"高額商品の売上: {len(high_value_sales)}件")for sale in high_value_sales: print(f" {sale['date']}: {sale['product']} {sale['amount']:,}円")
# 東京エリアの売上tokyo_sales = list(filter(lambda sale: sale["region"] == "東京", sales_data))tokyo_total = sum(sale["amount"] for sale in tokyo_sales)print(f"東京エリアの売上: {len(tokyo_sales)}件, 合計 {tokyo_total:,}円")
# PC関連商品pc_products = list(filter( lambda sale: sale["product"] in ["ノートPC", "タブレット"], sales_data))print(f"PC関連商品: {len(pc_products)}件")for sale in pc_products: print(f" {sale['product']}: {sale['amount']:,}円")
実行結果:
=== 売上データ分析 ===
総売上件数: 6
高額商品の売上: 4件
2024-01-15: ノートPC 120,000円
2024-01-18: モニター 45,000円
2024-01-19: ノートPC 135,000円
2024-01-20: タブレット 60,000円
東京エリアの売上: 3件, 合計 263,000円
PC関連商品: 3件
ノートPC: 120,000円
ノートPC: 135,000円
タブレット: 60,000円
ビジネスデータの分析でも、filter関数は大活躍します。
ログ分析での活用
システムのログを分析する例です:
log_entries = [ {"timestamp": "2024-01-15 10:30:00", "level": "INFO", "message": "アプリケーション開始"}, {"timestamp": "2024-01-15 10:31:00", "level": "INFO", "message": "ユーザーログイン"}, {"timestamp": "2024-01-15 10:32:00", "level": "WARNING", "message": "メモリ使用量が高い"}, {"timestamp": "2024-01-15 10:33:00", "level": "ERROR", "message": "データベース接続失敗"}, {"timestamp": "2024-01-15 10:34:00", "level": "INFO", "message": "データベース再接続成功"}, {"timestamp": "2024-01-15 10:35:00", "level": "ERROR", "message": "ファイル読み込みエラー"}, {"timestamp": "2024-01-15 10:36:00", "level": "CRITICAL", "message": "システム停止"}]
print("=== ログ分析 ===")print(f"総ログエントリー数: {len(log_entries)}")
# エラーレベルのログのみerror_logs = list(filter(lambda log: log["level"] in ["ERROR", "CRITICAL"], log_entries))print(f"エラーログ: {len(error_logs)}件")for log in error_logs: print(f" {log['timestamp']} [{log['level']}] {log['message']}")
# 問題のあるログdef is_problematic_log(log): """問題のあるログかどうかを判定""" problem_levels = ["WARNING", "ERROR", "CRITICAL"] problem_keywords = ["失敗", "エラー", "停止", "高い"] if log["level"] in problem_levels: return True return any(keyword in log["message"] for keyword in problem_keywords)
problematic_logs = list(filter(is_problematic_log, log_entries))print(f"問題のあるログ: {len(problematic_logs)}件")for log in problematic_logs: print(f" {log['timestamp']} [{log['level']}] {log['message']}")
実行結果:
=== ログ分析 ===
総ログエントリー数: 7
エラーログ: 3件
2024-01-15 10:33:00 [ERROR] データベース接続失敗
2024-01-15 10:35:00 [ERROR] ファイル読み込みエラー
2024-01-15 10:36:00 [CRITICAL] システム停止
問題のあるログ: 4件
2024-01-15 10:32:00 [WARNING] メモリ使用量が高い
2024-01-15 10:33:00 [ERROR] データベース接続失敗
2024-01-15 10:35:00 [ERROR] ファイル読み込みエラー
2024-01-15 10:36:00 [CRITICAL] システム停止
ログの監視や問題の特定にも、filter関数が使えます。
ファイル処理での活用
ファイル一覧から特定の条件のファイルを抽出する例です:
files = [ {"name": "document.pdf", "size": 1024000, "modified": "2024-01-15"}, {"name": "image.jpg", "size": 2048000, "modified": "2024-01-10"}, {"name": "script.py", "size": 5120, "modified": "2024-01-20"}, {"name": "data.csv", "size": 512000, "modified": "2024-01-18"}, {"name": "backup.zip", "size": 10240000, "modified": "2024-01-12"}, {"name": "config.json", "size": 2048, "modified": "2024-01-19"}]
print("=== ファイル分析 ===")print(f"総ファイル数: {len(files)}")
# 大きなファイル(1MB以上)large_files = list(filter(lambda f: f["size"] >= 1024000, files))print(f"大きなファイル(1MB以上): {len(large_files)}件")for file in large_files: size_mb = file["size"] / 1024000 print(f" {file['name']}: {size_mb:.2f}MB")
# Pythonファイルpython_files = list(filter(lambda f: f["name"].endswith('.py'), files))print(f"Pythonファイル: {len(python_files)}件")for file in python_files: print(f" {file['name']}: {file['size']}バイト")
# 最近更新されたファイル(2024-01-18以降)recent_files = list(filter(lambda f: f["modified"] >= "2024-01-18", files))print(f"最近更新されたファイル: {len(recent_files)}件")for file in recent_files: print(f" {file['name']}: {file['modified']}")
実行結果:
=== ファイル分析 ===
総ファイル数: 6
大きなファイル(1MB以上): 3件
document.pdf: 1.00MB
image.jpg: 2.00MB
backup.zip: 10.00MB
Pythonファイル: 1件
script.py: 5120バイト
最近更新されたファイル: 3件
script.py: 2024-01-20
data.csv: 2024-01-18
config.json: 2024-01-19
ファイル管理やバックアップの自動化でも活躍します。
リスト内包表記との比較
filter関数と似た機能を持つリスト内包表記と比較してみましょう。
基本的な比較
同じ結果を得る2つの方法を比較します:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# filter関数を使った方法even_filter = list(filter(lambda x: x % 2 == 0, numbers))print("filter関数:", even_filter)
# リスト内包表記を使った方法even_comprehension = [x for x in numbers if x % 2 == 0]print("リスト内包表記:", even_comprehension)
# 結果は同じprint("結果が同じ:", even_filter == even_comprehension)
実行結果:
filter関数: [2, 4, 6, 8, 10]
リスト内包表記: [2, 4, 6, 8, 10]
結果が同じ: True
どちらを使っても、同じ結果が得られます。
それぞれの特徴
それぞれの使い分けのポイントを見てみましょう:
# 複雑な条件の場合def is_prime(n): """素数かどうかを判定""" if n < 2: return False for i in range(2, int(n ** 0.5) + 1): if n % i == 0: return False return True
numbers = list(range(1, 21))
print("=== 使い分けの例 ===")
# 1. 既存の関数を使う場合print("既存の関数を使う場合(素数抽出):")print("filter関数:", list(filter(is_prime, numbers)))print("リスト内包表記:", [x for x in numbers if is_prime(x)])
# 2. 単純な条件の場合print("単純な条件の場合(偶数抽出):")print("filter関数:", list(filter(lambda x: x % 2 == 0, numbers)))print("リスト内包表記:", [x for x in numbers if x % 2 == 0])
# 3. フィルタ + 変換の場合print("フィルタ + 変換の場合(偶数の2乗):")print("filter + map:", list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers))))print("リスト内包表記:", [x**2 for x in numbers if x % 2 == 0])
実行結果:
=== 使い分けの例 ===
既存の関数を使う場合(素数抽出):
filter関数: [2, 3, 5, 7, 11, 13, 17, 19]
リスト内包表記: [2, 3, 5, 7, 11, 13, 17, 19]
単純な条件の場合(偶数抽出):
filter関数: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
リスト内包表記: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
フィルタ + 変換の場合(偶数の2乗):
filter + map: [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
リスト内包表記: [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
どちらを選ぶべき?
使い分けの指針をまとめます:
filter関数が適している場面:
- 既存の関数を条件として使いたい時
- 条件だけを抽出したい時(変換は不要)
- 関数型プログラミングのスタイルを重視する時
リスト内包表記が適している場面:
- 条件が比較的シンプルな時
- フィルタリングと同時に要素の変換もしたい時
- Pythonらしい書き方を重視する時
data = [ {"name": "田中", "age": 25, "score": 85}, {"name": "佐藤", "age": 30, "score": 92}, {"name": "鈴木", "age": 22, "score": 78}, {"name": "高橋", "age": 28, "score": 88}]
print("=== 実用的な比較 ===")
# 単純なフィルタリング(80点以上)print("80点以上の学生(filter関数):")high_scorers_filter = list(filter(lambda s: s["score"] >= 80, data))for student in high_scorers_filter: print(f" {student['name']}: {student['score']}点")
print("80点以上の学生(リスト内包表記):")high_scorers_comp = [s for s in data if s["score"] >= 80]for student in high_scorers_comp: print(f" {student['name']}: {student['score']}点")
# フィルタ + 変換(名前だけ抽出)print("80点以上の学生の名前のみ(filter + map):")names_filter = list(map(lambda s: s["name"], filter(lambda s: s["score"] >= 80, data)))print(f" {names_filter}")
print("80点以上の学生の名前のみ(リスト内包表記):")names_comp = [s["name"] for s in data if s["score"] >= 80]print(f" {names_comp}")
実行結果:
=== 実用的な比較 ===
80点以上の学生(filter関数):
田中: 85点
佐藤: 92点
高橋: 88点
80点以上の学生(リスト内包表記):
田中: 85点
佐藤: 92点
高橋: 88点
80点以上の学生の名前のみ(filter + map):
['田中', '佐藤', '高橋']
80点以上の学生の名前のみ(リスト内包表記):
['田中', '佐藤', '高橋']
どちらを使っても結果は同じですが、用途に応じて使い分けると良いでしょう。
まとめ
Pythonのfilter関数について、基本から実践的な使い方まで詳しく解説しました。
重要なポイント:
- filter関数は条件に合う要素だけを抽出する
- lambda式と組み合わせて簡潔に書ける
- 実践的な場面でも幅広く活用できる
- リスト内包表記との使い分けが大切
filter関数の特徴:
- 条件が明確で読みやすい
- 既存の関数を活用しやすい
- 大量データの処理に適している
- 関数型プログラミングのスタイル
実践的な活用場面:
- データ分析での条件抽出
- ログ分析での問題特定
- ファイル処理での絞り込み
- Webデータの整理
filter関数を使いこなすことで、データの条件抽出がとても効率的になります。 最初は簡単な数値や文字列のフィルタリングから始めて、徐々に複雑なデータ構造にも挑戦してみてください。
ぜひ実際のプログラムで、今回学んだfilter関数を活用してみてくださいね! きっとデータ処理がもっと楽しくなるはずです。