Pythonの標準ライブラリであるitertoolsを(超個人的に)推している。
しかしながら、まわりで活用している人は多くない様子なので、 布教 紹介記事を書くことにする。
この記事では isliceを紹介する。
Pythonのバージョンは3.7を使っている。
はじめに:どうすれば良さが伝わるか
itertoolsを紹介するブログは日本語でもちゃんと存在する。
にも関わらず、なぜみんな使わないのか。
話を聞いていくうち、「実際に、どういうタイミングで、どう使うかわからない」という意見があることを知った。
確かに、「ここはもっとスマートに書く方法がある気がする」と思っても、今まで一度も使ったこともないitertoolsを使えばよいとは思いつかないものだ。
そういう訳で、実際のコードでどう使われているのかも調べることにした。これは後半で紹介する。
isliceとは
iterable から要素を選択して返すイテレータを作成します。 start が0でない場合、iterable の要素は start に達するまでスキップされます。その後、 要素が順に返されます。
itertools.islice 公式ドキュメントより
isliceは、簡単に言えば、listの [start:stop:step]
がiterableな要素に対して実行できる関数。
下記の例では 0~999までの要素が入った data
から最初の10個だけ出力している。
from itertools import islice
data = [ i for i in range(1000)]
for line in islice(data,10): # data[:10]と等価
print(line)
ファイルオブジェクトに対してisliceを使う
上記例(islice_sample.py
)だと、「data[:10]
でいいじゃん」と思うかもしれない。
しかし、isliceは、listではないiterableに対しても使えることがキモだ。
具体的には、ファイルを特定の行数だけ読み込む時に便利だ。
- テキストファイルを読み込んだ時のオブジェクト TextIOWrapper はiterableなため1、以下の例(
islice_sample2.py
)のように、ファイルの最初のn行だけ読み込むことができる。- 例えば、巨大なファイルを扱う時、とりあえず最初の10行だけ受け取り、関数の動作を確認できる。
# isliceで10行だけ読み込み
with open("./sample.txt", "r")as f:
for line in islice(f, 10):
print(line.strip())
# 20~30行目だけdataに読み込み
with open("./sample.txt", "r")as f:
for line in islice(f, 20, 30):
print(line.strip())
具体例
テキストファイルの他にも活用方法はないか、普段使っているレポジトリでisliceが使われている箇所を調べてみた。
ざっとみたところ、やはりファイル読み込みに使われることが多く、クラスの __iter__()
に活用されているようだ。
ファイル読み込みの例を含め、利用しているコードを紹介する。
ファイル読み込み(gensim)
for line in itertools.islice(self.source, self.limit):
line = utils.to_unicode(line).split()
-
source
ファイルからlimit
だけ読み込む。-
one line one sentence のファイルからsentence(文)を読み込むクラスの、
__iter__()
内で使われている。 -
limit
のデフォルトはNone
なので、基本的にはファイル全てのsentenceを読み込んでいる
-
one line one sentence のファイルからsentence(文)を読み込むクラスの、
ミニバッチ生成(spacy)
while True:
batch_size = next(size_)
batch = list(itertools.islice(items, int(batch_size)))
if len(batch) == 0:
break
yield list(batch)
-
minibatch
関数における実装。 -
batch_size
の数だけitems
を受け取り、ミニバッチをyieldで返す-
size_
のデフォルトはitertools.repeat(size)
で定義されるため、items
の中身がなくなるまで返し続ける
-
ラベルの色指定(scikit-learn)
colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a',
'#f781bf', '#a65628', '#984ea3',
'#999999', '#e41a1c', '#dede00']),
int(max(y_pred) + 1))))
引用元 scikit-learn/examples/cluster/plot_linkage_comparison.py
- scikit-learnにある
examples
のクラスタリングの結果をプロットする例 -
cycle
によって色情報のlistが無限ループしているので、そのうちint(max(y_pred) + 1
(=予測クラス数) だけ読み込む- クラス数が1~9ならば
cycle
なし(islice(['#377eb8', '#ff7f00', '#4daf4a', ..., '#dede00']), int(max(y_pred) + 1)))
)と等価
- クラス数が1~9ならば
- プロットに使う色数がわからないとき、使う色数を制限したいときに便利かもしれない
ちなみに、実際にプロットした結果はscikit-learnのサイトにある→該当ページ
まとめ
- isliceはテキストファイルから特定の行数のみ取得したいときに便利
-
repeat
やcycle
のような Infinite iteratorsと組み合わせて、ループを止めるカウンターのように扱うときにも便利- zip_longest でも、以下のように書いてある。
iterables の1つが無限になりうる場合 zip_longest() は呼び出し回数を制限するような何かでラップしなければいけません(例えば islice() or takewhile())。
https://docs.python.org/ja/3/library/itertools.html#itertools.zip_longest
-
もう少し詳しく書くと、TextIOWrapperはIOBaseを継承しているため(参考: 【Python】open関数で返されたtext objectがiterableなわけ) ↩