今回はToolzのAPIの中からItertoolzの関数をいくつか紹介します。
サンプルコードは”from toolz.itertoolz import *”の様に扱う関数をインポートしてある前提で書いてあります。
イテレータの出力から一部を取り出す関数
first(seq)
シーケンスの最初の要素を取得します。
result = first('ABC')
print(result)
【結果】
A
get(ind, seq, default=‘__no__default__’)
インデックスで指定したシーケンスや辞書の要素を取得します。
result = get(1, 'ABC')
print(result)
【結果】
B
文字列’ABC’ の要素番号が1の’B’を取得できます。
第1引数にリストを渡すと、複数の値をタプル型で取得できます。次の例では、(’ん’, ‘ご’)という値を取得できます。
get([1, 2], 'りんご') # 戻り値 ('ん', 'ご')
どんな値でも値を取得する方法に則ります。例えば、辞書の場合以下のようにキーを指定して値を取得できます。
phonebook = {
'アリス':'555-1234',
'けんじ':'555-5678',
'さき':'555-9999',
}
get('アリス', phonebook) # 戻り値 555-1234
文字列の時と同様に、リストで複数取得することもできます。
get(['アリス', 'けんじ'], phonebook) # 戻り値 ('555-1234', '555-5678')
第3引数に値が見つからなかった場合のデフォルト値を指定できます。
get(['アリス', 'イチロー'], phonebook, None) # 戻り値 ('555-1234', None)
その他の関数は以下のようなものがあります。
関数 | 説明 |
second(seq) | シーケンスの2番目の要素を取得 |
last(seq) | シーケンスの最後の要素を取得 |
nth(n, seq) | シーケンスのn番目の要素を取得 |
tail(n, seq) | シーケンスの最後のn個の要素を取得 |
以下の関数は上記の関数と、イテレーターを返す点が異なります。リスト型が欲しい場合は型変換をする必要があります。
関数 | 説明 |
take(n, seq) | シーケンスの最初のn個のイテレーターを取得 |
drop(n, seq) | 最初のn要素に続くイテレーターを取得 |
take_nth(n, seq) | seqのn番目ごとの要素から成るイテレーターを取得 |
既存のイテレータに基づいて新しいイテレータを作る関数
accumulate(binop, seq, initial=‘__no__default__’)
バイナリ関数をシーケンスに繰り返し適用し、結果を蓄積します
“binop” は “binary operation”の略で、二項演算をする関数(以下、二項関数と言います)のことです。
シーケンスに二項関数を繰り返し適用し、結果を累積した結果を得られます。
from operator import add, mul
result = list(accumulate(add, [2, 4, 6, 8]))
result2 = list(accumulate(mul, [2, 4, 6, 8]))
print(result)
print(result2)
【結果】
[2, 6, 12, 20]
[2, 8, 48, 384]
初期値を設定することもできます。
list(accumulate(add, [2, 4, 6], -1))
注)itertoolsにもaccumulate関数がありますが引数が異なります。関数型プログラミングにおいては、itertoolzのaccumulate関数の方が扱いやすいです。
イテレータの要素を引数として扱う関数
concat(seqs)
0個以上のイテラブルを連結します。
無限長のシーケンスを引数に指定する場合は最後に指定する必要があります。
この関数はitertools.chain.from_iterableと同等の処理をします。
result = list(concat([[], [1], [2, 3]]))
print(result)
【結果】
[1, 2, 3]
concatv(*seqs)というconcatの引数が可変のバージョンもあります。
join(leftkey, leftseq, rightkey, rightseq, left_default=’__no__default__’, right_default=’__no__default__’)
2つのシーケンスを結合します。
SQL文のjoin文と同様の機能です。
leftseqのシーケンスは全て評価され、メモリに配置されます。rightseqのシーケンスは遅延評価されるため任意のサイズにすることができます。
friends = [
('アリス', 'イチロウ'),
('アリス', 'リン'),
('イチロー', 'アリス'),
('リン', 'アリス'),
('リン', 'アリス'),
('リン', 'イチロウ')
]
cities = [
('アリス', 'ニューヨーク'),
('アリス', 'シカゴ'),
('さとし', 'シドニー'),
('イチロウ', 'パリ'),
('イチロウ', 'ベルリン'),
('リン', '上海')
]
join関数を使って「アリス、イチロー、リンの友だちはどこに住んでいるか?」を求めます。
result = join(second, friends, first, cities)
for ((a, b), (c, d)) in sorted(unique(result)):
print((a, d))
【結果】
('アリス', 'パリ')
('アリス', 'ベルリン')
('アリス', '上海')
('イチロー', 'シカゴ')
('イチロー', 'ニューヨーク')
('リン', 'シカゴ')
('リン', 'ニューヨーク')
('リン', 'パリ')
('リン', 'ベルリン')
left_defaultやright_defaultで外部結合を指定します。以下は、一致しない要素がNoneとペアになっている完全外部結合です。
identity = lambda x: x
list(join(identity, [1, 2, 3],
identity, [2, 3, 4],
left_default=None, right_default=None))
【結果】
[(2, 2), (3, 3), (None, 4), (1, None)]
通常、leftkeyやrightkeyはシーケンスに適用される呼び出し可能なオブジェクトです。
もし、明らかにキーが呼び出し可能でない場合、インデックスの作成を意図していると見なされます。たとえば、以下の書き方です。
# result = join(second, friends, first, cities)
result = join(1, friends, 0, cities)
イテレータをグループ分けする関数
frequencies
シーケンスの各値の出現回数をカウントします。
統計を取りたい時などに使えます。
result = frequencies(['ネコ', 'ネコ', '犬', '馬', '馬', 'ネコ'])
print(result)
【結果】
{'ネコ': 3, '犬': 1, '馬': 2}
groupby(key, seq)
keyに指定された関数で、シーケンスをグループ化します。
以下の例では名前を文字数でグループ化します。len関数を使って文字数をカウントしています。
names = ['アリス', 'イチロー', 'シャーロット', 'ライラ', 'エド', 'たけし']
result = groupby(len, names)
print(result)
【結果】
{3: ['アリス', 'ライラ', 'たけし'], 4: ['イチロー'], 6: ['シャーロット'], 2: ['エド']}
次の例は数値を整数と偶数でグループ化します。出力結果のキーはTrue、または、Falseになります。
iseven = lambda x: x % 2 == 0
result = groupby(iseven, [1, 2, 3, 4, 5, 6, 7, 8])
print(result)
【結果】
{False: [1, 3, 5, 7], True: [2, 4, 6, 8]}
countby(key, seq)
keyの関数に基づいてシーケンスの要素数を数えます。countbyは上記で説明した関数と異なり、toolz.recipesモジュールの関数です。
from toolz.recipes import countby
result = countby(len, ['cat', 'mouse', 'dog'])
print(result)
【結果】
{3: 2, 5: 1}
次の例はiseven関数の結果がTrueの要素数と、Falseの要素数を数えています。
def iseven(x): return x % 2 == 0
result = countby(iseven, [1, 2, 3])
print(result)
【結果】
{True: 1, False: 2}
まとめ
今回はtoolzのitertoolzの中から便利な関数をいくつか紹介しました。
イテレータを処理するシーンは非常に多いです。itertoolsやitertoolzの関数を有効活用して、バグが入りにくい堅牢なコーディングができると良いと思います。
また、functoolzやdicttoolzと組み合わせることで、更に複雑な処理も簡潔に書くことができる様になります。
コメントを残す