三点リーダ

January 31, 2021

ゼロから作るDeep Learning ❷ ―自然言語処理編に3点リーダを使う部分が出てたのですが、よく理解できてなかったので調べました。

Ellipsis

三点リーダはEllipsisというリテラルらしい。

>>> ...
Ellipsis
>>>

Ellipsisとは省略記号という意味。…とかいてもEllipsisとかいてもいいみたい。

>>> ...
Ellipsis
>>> Ellipsis
Ellipsis
>>>

Ellipsisの使い方

ドキュメントには以下のように書いてあます。

Ellipsis リテラル ”…” と同じです。 主に拡張スライス構文やユーザ定義のコンテナデータ型において使われる特殊な値です。

よく意味が分からいですね。Ellipsisの意味が省略ってことなので、こういう使い方は出来そう。

>>> def func():
...     ...
...
>>> func()
>>>

よくpassを使いますが、こっちのほうがとりあえず省略してるって意図は伝わりそうです。

numpyでのEllipsis

まずは検証用の配列を作成します。

>>> import numpy as np
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>

arrangeというメソッドで連番の配列を作成でるので、これにreshapeを適用して2 x 3 x 4の3次元配列を作成します。

>>> import numpy as np
>>> np.arange(24).reshape(2, 3, 4)
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>>

2 x 3 x 4を少しだけ見やすくかくとこういう配列になります。

[
    [
        [ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]
    ],
    [
        [12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]
    ]
]

たとえば、最後の次元の4つめの値([3, 7 ,11]と[15, 19, 23])だけを取得したい場合、スライス記法でかくとこうなります。

>>> import numpy as np
>>> np.arange(24).reshape(2, 3, 4)
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> a[:,:,3]
array([[ 3,  7, 11],
       [15, 19, 23]])
>>>

a[:,:,3]は1次元目はそのまま(:)、2次元目もそのまま(:)、3次元目だけindexが3の値を抽出となります。1次元目、2次元目がそのままなのでということでa[,,3]と書くとエラーになります。

>>> a[,,3]
  File "<stdin>", line 1
    a[,,3]
      ^
SyntaxError: invalid syntax
>>>

このそのままの部分を…を使うとまめて省略できます。

>>> a[...,3]
array([[ 3,  7, 11],
       [15, 19, 23]])
>>>

IPv6の省略記法と同様、複数個所で省略すると省略の範囲がわからなくなるので省略できるのは一か所だけです。

>>> a = np.arange(120).reshape(2, 3, 4, 5)
>>> a
array([[[[  0,   1,   2,   3,   4],
         [  5,   6,   7,   8,   9],
         [ 10,  11,  12,  13,  14],
         [ 15,  16,  17,  18,  19]],

        [[ 20,  21,  22,  23,  24],
         [ 25,  26,  27,  28,  29],
         [ 30,  31,  32,  33,  34],
         [ 35,  36,  37,  38,  39]],

        [[ 40,  41,  42,  43,  44],
         [ 45,  46,  47,  48,  49],
         [ 50,  51,  52,  53,  54],
         [ 55,  56,  57,  58,  59]]],


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])
>>> a[..., 3, ...]  # 3次元目の最後の要素だけ抽出したい
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: an index can only have a single ellipsis ('...')
>>> a[..., 3,:]  # 3次元目の最後の要素だけ抽出したい
array([[[ 15,  16,  17,  18,  19],
        [ 35,  36,  37,  38,  39],
        [ 55,  56,  57,  58,  59]],

       [[ 75,  76,  77,  78,  79],
        [ 95,  96,  97,  98,  99],
        [115, 116, 117, 118, 119]]])
>>>

もちろん、… is EllipsisなのでEllipsisと書いても大丈夫です。そんな人いないと思うけど。

>>> a[Ellipsis, 3,:]
array([[[ 15,  16,  17,  18,  19],
        [ 35,  36,  37,  38,  39],
        [ 55,  56,  57,  58,  59]],

       [[ 75,  76,  77,  78,  79],
        [ 95,  96,  97,  98,  99],
        [115, 116, 117, 118, 119]]])
>>>

今後、この本にこの記法が良く出てくるらしいので覚えておきます。