LoginSignup
16
14

More than 3 years have passed since last update.

isliceの紹介、具体例添え(pythonのitertoolsを使いこなすために)

Posted at

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個だけ出力している。

islice_sample.py
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_sample2.py
# 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)

word2vec.py
for line in itertools.islice(self.source, self.limit):
    line = utils.to_unicode(line).split()

引用元 gensim/models/word2vec.py

  • source ファイルから limit だけ読み込む。
    • one line one sentence のファイルからsentence(文)を読み込むクラスの、 __iter__() 内で使われている。
    • limitのデフォルトはNone なので、基本的にはファイル全てのsentenceを読み込んでいる

ミニバッチ生成(spacy)

util.py
    while True:
        batch_size = next(size_)
        batch = list(itertools.islice(items, int(batch_size)))
        if len(batch) == 0:
            break
        yield list(batch)

引用元 spaCy/spacy/util.py

  • minibatch 関数における実装。
    • batch_size の数だけitems を受け取り、ミニバッチをyieldで返す
      • size_ のデフォルトは itertools.repeat(size) で定義されるため、items の中身がなくなるまで返し続ける

ラベルの色指定(scikit-learn)

plot_linkage_comparison.py
         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))))と等価
  • プロットに使う色数がわからないとき、使う色数を制限したいときに便利かもしれない

ちなみに、実際にプロットした結果はscikit-learnのサイトにある→該当ページ

まとめ

  • isliceはテキストファイルから特定の行数のみ取得したいときに便利
  • repeatcycle のような Infinite iteratorsと組み合わせて、ループを止めるカウンターのように扱うときにも便利
    • zip_longest でも、以下のように書いてある。

iterables の1つが無限になりうる場合 zip_longest() は呼び出し回数を制限するような何かでラップしなければいけません(例えば islice() or takewhile())。
https://docs.python.org/ja/3/library/itertools.html#itertools.zip_longest


  1. もう少し詳しく書くと、TextIOWrapperはIOBaseを継承しているため(参考: 【Python】open関数で返されたtext objectがiterableなわけ

16
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14