Get started learning Python with DataCamp's free Intro to Python tutorial. Learn Data Science by completing interactive coding challenges and watching videos by expert instructors. Start Now!

This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join 11 million other learners and get started learning Python for data science today!

Good news! You can save 25% off your Datacamp annual subscription with the code LEARNPYTHON23ALE25 - Click here to redeem your discount

マップ、フィルター、リデュース


Map、Filter、および Reduce は、関数型プログラミングのパラダイムです。これらはプログラマー(あなた)がループや分岐の複雑さに煩わされることなく、よりシンプルで短いコードを書けるようにします。

基本的に、これらの3つの関数は、あなたが一度に複数のイテラブルに関数を適用することを可能にします。mapfilterはPythonに組み込まれており(__builtins__モジュールに存在します)、インポートは必要ありません。しかし、reducefunctoolsモジュールにあるためインポートが必要です。では、これらの機能がどのように動作するかを理解していきましょう。まずはmapから始めます。

Map

Pythonにおけるmap()関数の構文は以下の通りです:

map(func, *iterables)

ここでfuncは、各要素が適用される関数であり、iterables(いくつでも可)の要素です。アスタリスク(*)はイテラブルがいくつでも可能であることを意味し、funcが必要な入力引数としてその正確な数を持っている限りです。例を見る前に、次の点に注意してください:

  1. Python 2では、map()関数はリストを返します。しかし、Python 3では、map()関数はmap objectというジェネレータオブジェクトを返します。結果をリストとして取得するには、組み込みのlist()関数をmap objectに対して呼び出すことができます。つまり、list(map(func, *iterables))
  2. funcの引数の数は、リストしたiterablesの数でなければなりません。

これらのルールがどのように作用するか、以下の例で見てみましょう。

私のお気に入りのペットの名前のリスト(iterable)が、すべて小文字であり、大文字に変換する必要があるとしましょう。従来のPythonコードでは、次のように書くでしょう:

my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = []

for pet in my_pets:
    pet_ = pet.upper()
    uppered_pets.append(pet_)

print(uppered_pets)

これは['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']を出力します。

map()関数を使用すれば、これがより簡単で柔軟性も増します。次のようにします:

# Python 3
my_pets = ['alfred', 'tabitha', 'william', 'arla']

uppered_pets = list(map(str.upper, my_pets))

print(uppered_pets)

これにより、同じ結果が出力されます。このケースでのfuncstr.upperであり、iterablesmy_petsリストです ― ちょうど1つのイテラブルです。また、str.upper関数を呼び出していないことに注意してください(str.upper()とすることはしません)、map関数がmy_petsリストの_各要素に対して_これを行います。

重要なことは、str.upper関数は定義上1つの引数だけを必要とするため、1つのイテラブルだけを渡します。きちんと理解するために別の例で説明します。

たとえば、どこかで計算した円の面積のリストがあり、すべて小数点以下5桁で、それらをそれぞれの位置の小数桁に丸める必要があるとします。つまり、リストの最初の要素を小数点以下1桁に、2番目の要素を小数点以下2桁に、3番目の要素を小数点以下3桁に丸める必要があります。map()を使えば簡単です。

Pythonではすでに、丸めたい数値と丸めたい小数桁数の2つの引数を取る組み込みのround()関数があります。関数が2つの引数を必要とするので、2つのイテラブルを渡す必要があります。

# Python 3

circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]

result = list(map(round, circle_areas, range(1, 7)))

print(result)

map()の美しさが見えますか?この柔軟性を想像できますか?

range(1, 7)round関数の2番目の引数として機能します(各反復の必要な小数桁数)。したがって、mapcircle_areasを反復する際に、最初の反復でcircle_areasの最初の要素3.56773range(1,7)の最初の要素1とともにroundに渡され、round(3.56773, 1)が形成されます。2番目の反復では、circle_areasの2番目の要素5.57668range(1,7)の2番目の要素2とともにroundに渡され、round(5.57668, 2)となります。この過程はcircle_areasリストの終わりまで続きます。

おそらく疑問に思っているでしょう: 「最初のイテラブルの長さより少ないまたは多いイテラブルを渡した場合はどうなるんだろう?つまり、上記の関数で2番目のイテラブルとしてrange(1, 3)range(1, 9999)を渡した場合にはどうなるのか」と。そして答えは単純です: 何も起こりません!いいえ、それは真実ではありません。「何も」起こらないというのは、map()関数が例外を発生させることはなく、ただ要素を反復し、関数の2番目の引数が見つからなくなると処理を止めて結果を返すという意味です。

したがって、たとえばresult = list(map(round, circle_areas, range(1, 3)))を評価しても、circle_areasrange(1, 3)の長さが異なっていてもエラーは発生しません。代わりに、Pythonは次のように動作します: circle_areasの最初の要素とrange(1,3)の最初の要素を取り、それをroundに渡します。roundはそれを評価し、結果を保存します。その後、2番目の反復、circle_areasの2番目の要素とrange(1,3)の2番目の要素roundが再度保存します。3番目の反復(circle_areasにはサード要素があります)には、circle_areasの3番目の要素を取り、range(1,3)の3番目の要素を取ろうとするが、range(1,3)に3番目の要素がないため、Pythonはその結果を出力して処理を停止します。場合によっては、 [3.6, 5.58]になります。

試してみてください。

# Python 3

circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]

result = list(map(round, circle_areas, range(1, 3)))

print(result)

同様に、もしcircle_areasが2番目のイテラブルより少なくても、Pythonは単に次の要素が見つからない場合に停止します。

map()関数の知識を深めるために、これを使って独自のzip()関数を実装しましょう。zip()は複数のイテラブルを受け取り、それぞれの要素を含むタプルを作成する関数です。Python 3では、map()のように、ジェネレータオブジェクトが返され、組み込みのlist関数を呼び出すことで簡単にリストに変換できます。zip()の理解を深めるため、以下のインタープリタセッションを使用しましょう。

# Python 3

my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]

results = list(zip(my_strings, my_numbers))

print(results)

おまけで、上記のセッションでmy_stringsmy_numbersが同じ長さでない場合に何が起きるか予想できますか?できない?試してみてください。一方の長さを変えてみてください。

それでは独自のzip()関数に進みましょう!

# Python 3

my_strings = ['a', 'b', 'c', 'd', 'e']
my_numbers = [1, 2, 3, 4, 5]

results = list(map(lambda x, y: (x, y), my_strings, my_numbers))

print(results)

見てください!zipと同じ結果が得られました。

def my_function()の標準的な方法を使わずに関数を作成する必要もありませんでした。このように、map()や一般的にPythonが非常に柔軟であることがわかります!単にlambda関数を使いました。これは、標準の関数定義方法(def function_name())を使用してはならないというわけではなく、今でも可能です。単にコードを少なく(「Pythonic」に)書きたかっただけです。

これがmap関数についてです。それではfilter()へ進みましょう。

Filter

filter()は、各要素を関数に通過させ、その結果を返すmap()とは異なり、まず関数がブール値(真または偽)を返すことを要求し、次にイテラブルの各要素を関数に通過させ、偽の要素を「フィルタリング」(除去)します。以下の構文があります:

filter(func, iterable)

filter()について注意すべき以下のポイントがあります:

  1. map()とは異なり、1つのイテラブルのみが必要です。
  2. func引数はブール型を返す必要があります。そうしない場合、filterは渡されたiterableをそのまま返します。また、1つのイテラブルのみが必要であるため、funcは必然的に1つの引数のみを取る必要があります。
  3. filterfuncに渡された各要素を評価し、と評価されるものだけを返します。その名の通り ― 「フィルター」です。

いくつか例を見てみましょう。

以下は化学の試験の10人の学生のスコアのリスト(iterable)です。75点以上の合格者をfilterを使ってフィルタリングします。

# Python 3
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]

def is_A_student(score):
    return score > 75

over_75 = list(filter(is_A_student, scores))

print(over_75)

次の例は回文検出器です。「回文」とは、前から読んでも後ろから読んでも同じになる言葉やフレーズ、または文字列のことです。回文である言葉をタプル(iterable)からフィルタリングします。

# Python 3
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")

palindromes = list(filter(lambda word: word == word[::-1], dromes))

print(palindromes)

これは['madam', 'anutforajaroftuna']を出力します。

素晴らしいですね?最後にreduce()です。

Reduce

reduceは、イテラブルの要素に渡された関数に2つの引数を適用し、オプションで初期値も指定できます。以下の構文があります:

reduce(func, iterable[, initial])

ここでfuncは、イテラブルの各要素が累積的に適用される関数で、initialは計算の前にイテラブルの要素の前に置かれ、イテラブルが空のときにデフォルトとして機能するオプションの値です。reduce()について次の点に注意してください: 1. funcは2つの引数を必要とし、最初の引数はイテラブル内の最初の要素(initialが指定されていない場合)と2番目の要素です。initialが指定されている場合、それはfuncの最初の引数となり、イテラブルの最初の要素が2番目になります。 2. reduceiterableを単一の値に「減らします」。

例を見てみましょう。

Pythonの組み込みsum()関数の独自バージョンを作成してみましょう。sum()関数は、渡されたイテラブル内のすべての項目の合計を返します。

# Python 3
from functools import reduce

numbers = [3, 4, 6, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, numbers)
print(result)

結果は予期通り68です。

では何が起こったのでしょう?

いつものように、それはすべての反復に関することです:reducenumbers内の最初と2番目の要素を取り、それぞれcustom_sumに渡します。custom_sumはそれらの合計を計算し、それをreduceに返します。その結果をreducecustom_sumの最初の要素として適用し、次の要素(3番目)をcustom_sumに2番目の要素として取ります。これを繰り返し(累積的に)、numbersが尽きるまで行います。

オプションのinitial値を使用した場合に何が起きるか見てみましょう。

# Python 3
from functools import reduce

numbers = [3, 4, 6, 9, 34, 12]

def custom_sum(first, second):
    return first + second

result = reduce(custom_sum, numbers, 10)
print(result)

結果は予測通り78になります。それはreduceが最初に10custom_sumの最初の引数として使用するためです。

これでPythonのMap、Reduce、及びFilterの全容です。各関数の理解を深めるため、以下の練習問題に挑戦してください。

Exercise

この演習では、map, filter, reduceを使用して壊れたコードを修正します。

from functools import reduce # Use map to print the square of each numbers rounded # to three decimal places my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59] # Use filter to print only the names that are less than # or equal to seven letters my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"] # Use reduce to print the product of these numbers my_numbers = [4, 6, 9, 23, 5] # Fix all three respectively. map_result = list(map(lambda x: x, my_floats)) filter_result = list(filter(lambda name: name, my_names, my_names)) reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers, 0) print(map_result) print(filter_result) print(reduce_result) #### Map from functools import reduce my_floats = [4.35, 6.09, 3.25, 9.77, 2.16, 8.88, 4.59] my_names = ["olumide", "akinremi", "josiah", "temidayo", "omoseun"] my_numbers = [4, 6, 9, 23, 5] map_result = list(map(lambda x: round(x ** 2, 3), my_floats)) filter_result = list(filter(lambda name: len(name) <= 7, my_names)) reduce_result = reduce(lambda num1, num2: num1 * num2, my_numbers) print(map_result) print(filter_result) print(reduce_result) test_output_contains("[18.922, 37.088, 10.562, 95.453, 4.666, 78.854, 21.068]") test_output_contains("['olumide', 'josiah', 'omoseun']") test_output_contains("24840") success_msg("Congrats! Nice work.")

This site is generously supported by DataCamp. DataCamp offers online interactive Python Tutorials for Data Science. Join over a million other learners and get started learning Python for data science today!

Previous Tutorial Next Tutorial Take the Test
Copyright © learnpython.org. Read our Terms of Use and Privacy Policy