note.nkmk.me

Pythonのmap()でリストの要素に関数・処理を適用

Posted: 2021-03-28 / Tags: Python, リスト

Pythonのmap()を使うと、イテラブル(リストやタプルなど)のすべての要素に組み込み関数やlambda(ラムダ式、無名関数)、defで定義した関数などを適用できる。

ここでは以下の内容について説明する。

  • map()の基本的な使い方
    • Python3のmap()はイテレータを返す
    • リストに変換
  • lambda(ラムダ式、無名関数)を適用
  • defで定義した関数を適用
  • 複数のイテラブル(リストなど)を引数に指定
  • リスト内包表記・ジェネレータ式で代用
  • NumPyで代用(数値計算の場合)

なお、後半で述べるように、map()の処理はリスト内包表記やジェネレータ式で代用可能であり、多くの場合、リスト内包表記・ジェネレータ式を使用するほうが好ましいとされている。

スポンサーリンク

map()の基本的な使い方

map()の第一引数に適用する関数(呼び出し可能オブジェクト)、第二引数にリストなどのイテラブルオブジェクトを指定する。

Python3のmap()はイテレータを返す

絶対値を返す組み込み関数abs()を適用する例を示す。

Python3のmap()map型のオブジェクト(イテレータ)を返し、そのままprint()しても中身の値は出力されない。

l = [-2, -1, 0]
print(map(abs, l))
# <map object at 0x10651a400>

print(type(map(abs, l)))
# <class 'map'>
source: map_usage.py

イテレータの値はfor文などで取り出せる。

for i in map(abs, l):
    print(i)
# 2
# 1
# 0
source: map_usage.py

for文を使う場合は、map()を使わずにブロック内で処理を実行しても同じ結果となる。

for i in l:
    print(abs(i))
# 2
# 1
# 0
source: map_usage.py

なお、Python2のmap()はリストを返す。Python2のコードをPython3で実行する場合は注意。

リストに変換

結果をリストに変換したい場合はlist()を使う。

print(list(map(abs, l)))
# [2, 1, 0]
source: map_usage.py

以降のサンプルコードでは、便宜上、リストに変換した結果を出力する。

文字列のリストにlen()を適用して文字数のリストに変換する例。

l_s = ['apple', 'orange', 'strawberry']
print(list(map(len, l_s)))
# [5, 6, 10]
source: map_usage.py

map()の第二引数にはイテラブルオブジェクトを指定できる。リストだけでなくタプルやrangeなども指定可能。

print(list(map(abs, range(-2, 1))))
# [2, 1, 0]
source: map_usage.py

lambda(ラムダ式、無名関数)を適用

組み込み関数ではない任意の処理を適用したい場合、lambda(ラムダ式、無名関数)を用いる。

l = [-2, -1, 0]
print(list(map(lambda x: x**2, l)))
# [4, 1, 0]
source: map_usage.py

defで定義した関数を適用

ラムダ式ではなくdefで任意の関数を定義してmap()の第一引数に指定することも可能。

def square(x):
    return x**2

print(list(map(square, l)))
# [4, 1, 0]
source: map_usage.py

複数のイテラブル(リストなど)を引数に指定

map()は第三引数以降にさらにイテラブルを指定できる。

複数のイテラブルを指定した場合、第一引数にはその数の引数を受け取る関数・ラムダ式を指定する必要がある。イテラブルの数と第一引数の関数・ラムダ式が受け取る引数の数が一致していないとエラー。

l_1 = [1, 2, 3]
l_2 = [10, 20, 30]
print(list(map(lambda x, y: x * y, l_1, l_2)))
# [10, 40, 90]

# print(list(map(abs, l_1, l_2)))
# TypeError: abs() takes exactly one argument (2 given)
source: map_usage.py

イテラブルのサイズ(要素数)が異なる場合、多い分は無視される。

l_3 = [100, 200, 300, 400]
print(list(map(lambda x, y, z: x * y * z, l_1, l_2, l_3)))
# [1000, 8000, 27000]
source: map_usage.py

リスト内包表記・ジェネレータ式で代用

map()と同等の処理はリスト内包表記やジェネレータ式でも実現できる。

l = [-2, -1, 0]
print([abs(x) for x in l])
# [2, 1, 0]

print([x**2 for x in l])
# [4, 1, 0]

l_1 = [1, 2, 3]
l_2 = [10, 20, 30]
print([x * y for x, y in zip(l_1, l_2)])
# [10, 40, 90]
source: map_usage.py

list(map())のようにリストを取得したい場合はリスト内包表記、map()のようにイテレータを取得したい場合はジェネレータ式を使えばよい。

以下のStack Overflowの質問にもあるように、ほとんどの場合、map()よりもリスト内包表記・ジェネレータ式を使うほうがコードが簡潔・明解で好ましいとされている。

書籍『Effective Python』でもmap()よりもリスト内包表記を使うべきと書かれている。

処理速度に関しては、以下の回答が示されている。

ただし、処理速度は様々な要因で変動する可能性もあるので、処理速度が重要な状況であればなるべく想定に近い環境および処理で実際に計測してみることをおすすめする。

NumPyで代用(数値計算の場合)

数値のリストの場合、map()と同等の処理はNumPyでも実現できる。map()やリスト内包表記よりもさらに明解なコードになる。

import numpy as np

a = np.array([-2, -1, 0])
print(np.abs(a))
# [2 1 0]

print(a**2)
# [4 1 0]

a_1 = np.array([1, 2, 3])
a_2 = np.array([10, 20, 30])
print(a_1 * a_2)
# [10 40 90]
source: map_usage.py

大規模なリストに対する処理や複雑な処理はNumPyのほうが高速。NumPyには様々な関数も提供されているので、数値の配列を対象とする処理を行う場合は試してみるとよい。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事