[python][numpy]配列を繰り返したいときのrepeatとtileの違い

white concrete building python
Photo by Mitchell Luo on Pexels.com

tileとrepeat

numpy.repeat — NumPy v1.26 Manual
numpy.tile — NumPy v1.26 Manual

numpyで配列を繰り返したいとき、今までrepeatしか知らなかったけれど、tileというmethodもあることに気がついた。

まずはrepeatから

基本的な使い方は以下。

import numpy as np

np.repeat(a, repeats, axis = None)

\(a\)は配列、\(repeats\)は整数か整数配列、\(axis\)も整数で、どちらの軸に繰り返すかを指定する。\(axis\)は指定しなくても良い。

いくつか例を見てみる。

np.repeat(3, 4)
#array([3, 3, 3, 3])

この場合、\(3\)が\(4\)回繰り返される。

x = np.array([[1, 2], [3, 4]])
np.repeat(x, 2)
#array([1, 1, 2, 2, 3, 3, 4, 4])

このように、\(axis\)を指定しないとFlattened(平坦化)された\(1\)次元配列が生成される。ここで\(axis\)を指定すると以下のようになる。

np.repeat(x, 3, axis = 1)
#array([[1, 1, 1, 2, 2, 2], 
#        [3, 3, 3, 4, 4, 4]])

\(repeats\)には整数の配列を指定することもできる。

np.repeat(x, [1, 2], axis = 0)
#array([[1, 2],
#       [3, 4],
#       [3, 4]])

最後は少しわかりにくい。\([1, 2]\)を\(1\)回、\([3, 4]\)を\(2\)回繰り返している。ここで\(axis = 1\)とすると以下のようになる。

np.repeat(x, [1, 2], axis = 1)
#array([[1, 2, 2], 
#       [3, 4, 4]])

ここでは軸が行方向になるので、\(1\)を\(1\)回、\(2\)を\(2\)回、\(3\)を\(1\)回、\(4\)を\(2\)回繰り返している。

repeatは便利だが

このように(少し複雑だが)numpyのrepeat methodは便利である。しかし、例えば

np.arange(10)
#array([1, 2, 3, 4, 5, 6, 7, 8, 9])

に対してrepeatを適用すると、下のようになる。

np.repeat(np.arange(10), 3)
#array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7,
#       7, 7, 8, 8, 8, 9, 9, 9])

これが必要な場合もあるだろうが、多くの人が想像するのは以下のような配列ではないであろう。

#array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
#       0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

そんなときに便利なのが、以下のtileである。

tile

基本的な使い方は以下。

np.tile(A, reps)

\(A\)は整数配列で、\(reps\)は整数あるいは整数配列である。もしも\(reps\)の長さが\(d\)であれば、結果の配列は\(max(d, A.ndim)\)になる。ただし。\(A.ndim\)は\(A\)の次元である。また、\(A.ndim < d\)のときは、\(A\)は新しい軸を付加することで\(d\)次元に昇格する。したがって、形状\((3, )\)の配列は、\(2\)次元の場合は\((1, 3)\)に、\(3\)次元の場合は形状\((1, 1, 3)\)にプロモートされる。この動作が望ましくない場合は関数を呼び出す前に、\(A\)を手動で\(d\)次元にに昇格させておくと良い。また、\(A.ndim > d\)のときは、\(reps\)は\(A.ndim\)に\(1\)をプリペンドして昇格させる。したがって、\((2, 3, 4, 5)\)の形状の\(A\)に対して、\(reps = (2, 2)\)は\((1, 1, 2, 2)\)として扱われることになる。

よくわからないと思うので、いくつか具体例を見てみる。

a = np.array([0, 1, 2])
np.tile(a, 2)
#array([0, 1, 2, 0, 1, 2])

これは正に最初に望んでいたものである。

np.tile(a, (2, 2))
#array([[0, 1, 2, 0, 1, 2],
#       [0, 1, 2, 0, 1, 2]])

また、

np.tile(a, (2, 2))
#array([[0, 1, 2, 0, 1, 2]])

np.tile(a, (2, 1, 2))
#array([[[0, 1, 2, 0, 1, 2]],
#       [[0, 1, 2, 0, 1, 2]]])

の違いがわかるであろうか。注意深く眺めてみると良い。リンク先に習っていくつか例をあげておく。

b = np.array([[1, 2], [3, 4]])
np.tile(b, 2)
#array([[1, 2, 1, 2],
#       [3, 4, 3, 4]])
np.tile(b, (2, 1))
#array([[1, 2],
#       [3, 4],
#       [1, 2],
#       [3, 4]])
c = np.array([1, 2, 3, 4])
np.tile(c, (4, 1))
#array([[1, 2, 3, 4],
#       [1, 2, 3, 4],
#       [1, 2, 3, 4],
#       [1, 2, 3, 4]])

関連記事

numpy

numpyでリストごとの大小比較

コメント

タイトルとURLをコピーしました