トップページに戻る

Numpyの基礎(2)生成関数

In [1]:
import numpy as np
import matplotlib.pyplot as plt

# jupyter notebookに画像を表示
%matplotlib inline

ndarrayの生成関数

色々な配列を作る便利な関数が用意されています。ここではそれらを紹介します。

np.array, np.asarray

上でも述べた関数です。リストやタプルを引数に取り、配列を返す関数です。
型を指定しない場合は、勝手に推測してくれますが、キーワード引数で指定することも出来ます。 np.asarrayは引数がndarrayであれば、そのままndarrayを返し、そうでないならnp.arrayと同じ働きをする関数です。

In [2]:
x = np.array([1, 2, 5, 10])
x
Out[2]:
array([ 1,  2,  5, 10])
In [3]:
x.dtype
Out[3]:
dtype('int32')
In [4]:
plt.plot(x) #matplotlibにndarrayを渡せる
Out[4]:
[<matplotlib.lines.Line2D at 0x295ef133e48>]
In [5]:
x = np.array([1., 4., 0.5]) # float(浮動小数)で初期化するとdtypeが自動的にfloatになる
x
Out[5]:
array([1. , 4. , 0.5])
In [6]:
x.dtype
Out[6]:
dtype('float64')

np.arange

Python組み込み関数のrange()と似ていますが、さらに高機能な関数で、ndarrayを返します。

In [7]:
np.arange(10) # 0から9まで
Out[7]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [8]:
np.arange(3, 10) # 3から9まで
Out[8]:
array([3, 4, 5, 6, 7, 8, 9])
In [9]:
np.arange(0, 10, 0.5) # 0から10まで0.5刻み(10は含まない)
Out[9]:
array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. ,
       6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])
In [10]:
# 整数ステップ
np.arange(0, 20, 4)
Out[10]:
array([ 0,  4,  8, 12, 16])

np.ones, np.ones_like

np.onesは、引数で与えられたshapeで、全ての要素が1であるようなndarrayを返します。

In [11]:
np.ones(5)
Out[11]:
array([1., 1., 1., 1., 1.])
In [12]:
np.ones((2, 5)) # 多次元配列ver.
Out[12]:
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])
In [13]:
x = np.array([10, 3, -1, 3, 0])
x
Out[13]:
array([10,  3, -1,  3,  0])
In [14]:
np.ones_like(x) # xのようなshape, dtypeで、要素が全て1の配列
Out[14]:
array([1, 1, 1, 1, 1])

np.zeros, np.zeros_like

全ての要素を0にする他は、np.ones, np.ones_likeと同じです。

In [15]:
np.zeros(5)
Out[15]:
array([0., 0., 0., 0., 0.])
In [16]:
np.zeros((5, 2))
Out[16]:
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])

np.empty, np.empty_like

上の2つと似ていますが、こちらは初期化をせずに配列の領域だけを確保する関数です。
巨大な配列を自分で初期化したい場合や、値は何でもいいが配列だけ作りたいときに使います。
初期化のプロセスを省略するためにnp.zerosなどよりも高速に動作します。
ときどき変な値が入ってることがあるので、自分ですべての要素をあとで初期化するときのみ使うのが吉です。

In [17]:
# 2048個のbool型の中にあるTrueの個数(実行するたびに変化する)
for i in range(5):
    print(np.empty(2048, dtype=np.bool).sum())
1531
1531
1531
1531
1531
In [18]:
x = np.empty(10)
x[::2] = 1 # 自分で初期化
x[1::2] = 2
x
Out[18]:
array([1., 2., 1., 2., 1., 2., 1., 2., 1., 2.])

np.linspace

ある範囲を均等に分割する関数です。
連続的な配列を作るときに非常に便利です。 デフォルトで50個に分割します。

In [19]:
np.linspace(0, 1) # 0から1まで50個分割した配列
Out[19]:
array([0.        , 0.02040816, 0.04081633, 0.06122449, 0.08163265,
       0.10204082, 0.12244898, 0.14285714, 0.16326531, 0.18367347,
       0.20408163, 0.2244898 , 0.24489796, 0.26530612, 0.28571429,
       0.30612245, 0.32653061, 0.34693878, 0.36734694, 0.3877551 ,
       0.40816327, 0.42857143, 0.44897959, 0.46938776, 0.48979592,
       0.51020408, 0.53061224, 0.55102041, 0.57142857, 0.59183673,
       0.6122449 , 0.63265306, 0.65306122, 0.67346939, 0.69387755,
       0.71428571, 0.73469388, 0.75510204, 0.7755102 , 0.79591837,
       0.81632653, 0.83673469, 0.85714286, 0.87755102, 0.89795918,
       0.91836735, 0.93877551, 0.95918367, 0.97959184, 1.        ])
In [20]:
np.linspace(0, 1, 10) # 3つ目の引数は個数を表す。
Out[20]:
array([0.        , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
       0.55555556, 0.66666667, 0.77777778, 0.88888889, 1.        ])
In [21]:
t = np.linspace(0, 2 * np.pi) # 0から2πまで50個に分割する。
plt.plot(t, np.sin(t)) # なんだかなめらかに見える
Out[21]:
[<matplotlib.lines.Line2D at 0x295ef2f1748>]
In [22]:
#endpoint=Falseの場合。stopの値を含まない。
np.linspace(0, 1, 10, endpoint=False)
Out[22]:
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

np.eye, np.identity

この2つは単位行列を生成する関数です。

In [23]:
np.eye(4) # 4×4の単位行列
Out[23]:
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])

関数を使ってみよう

np.reshape

配列の次元を自在に変えられる関数です。

In [24]:
a = np.arange(10)
a
Out[24]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [25]:
b = a.reshape(2, 5)
b
Out[25]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

例えば、x, y, zが並んだバイナリデータを一次元配列として読み取ったあと、reshapeすれば、2次元配列として扱えます。

文字通り、ndarrayのshapeを変更する関数なのですが、2つほど注意する点があります。
一つ目は、全体のsizeを変更しないようにしなければならないということです。
つまり、新しいshapeの各要素を掛け合わせた値が元々の要素の総数を一致する必要があるということです。

In [26]:
#ndarray.sizeは配列に含まれるデータの総数
a.size
Out[26]:
10
In [27]:
# total sizeが変化しているので怒られる
# a.reshape(1000, 100000) ==> ValueError: total size of new array must be unchanged

逆に言うと、sizeが同じであるならば、どのようなshapeでも作ることが出来ます。

In [28]:
a.reshape(1, 1, 2, 1, 5)
Out[28]:
array([[[[[0, 1, 2, 3, 4]],

         [[5, 6, 7, 8, 9]]]]])

このような変形は、次元の異なる配列同士の計算をするために用いることがあります(ブロードキャスト)。
また、reshapeの引数に-1を入れると、sizeを変えないようなshapeを計算してくれます。

In [29]:
a = np.arange(10)
In [30]:
a.reshape(-1, 5) # reshape(2,5)と等価
Out[30]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

二つ目の注意する点は、reshapeによって返ってくるndarrayは、元々のndarrayと同じメモリを参照している点です。
つまり、reshapeによって返ってきたndarrayを書き換えると、オリジナルも変化するということです。
このような参照をコピーと対比させて、ビューと言います。

In [31]:
b = np.arange(10) # オリジナル
c = b.reshape(2,5) # reshapeされたビュー
c[0, 0] = 100 # ビューを書き換える
b # オリジナルが変わる
Out[31]:
array([100,   1,   2,   3,   4,   5,   6,   7,   8,   9])

科学技術計算の上ではこの性質は重要です。なぜなら、大規模な配列をいちいちreshapeのたびにコピーされると困るからです。
もし、オリジナルを書き換えたくないならば、copy関数を使う必要があります。

In [32]:
b = np.arange(10)
c = b.reshape(2,5).copy() # copyは複製したndarrayを返す
c[0, 0] = 100
b
Out[32]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

reshape関数まとめ

  • sizeを変化させない。引数に-1を使うと便利。
  • reshapeはコピーではなくビューを返す。コピーしたいときはcopy()を使う。

誤字やおかしい点などがあったら @zawawahoge (Twitter) にお気軽にご連絡ください。

トップページに戻る