配列

Python に標準で備わっている仕組みの中で、他の言語でいう「配列」に一番近いものは、「リスト」(list)です。数値計算によく使う NumPy というライブラリには array(配列)が定義されています。いずれも添字(インデックス)は 0 から始まります。

リスト

3個の要素からなるリストを作ってみましょう:

a = [3, 5, 7]

a[0]
3
a[1]
5
a[2]
7

範囲外の要素、例えば a[3] にアクセスすると、エラーになります:

a[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Input In [ ], in <cell line: 1>()
----> 1 a[3]

IndexError: list index out of range

インデックスのループで各要素にアクセスする方法:

for i in range(3):
    print(a[i])
3
5
7

ただし、Python では次のようにするのが一般的です:

for x in a:
    print(x)
3
5
7

同じ要素のリストなら次のようにして作るのが簡単です:

a = [5] * 10   # 5が10個
a
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]

アペンドを繰り返して作ることもできます:

a = []
for i in range(3, 8, 2):  # 3から始まり8未満2ずつ増やす
    a.append(i)
a
[3, 5, 7]

上と同じことは次のようにも書けます(リスト内包表記、list comprehension):

a = [i for i in range(3, 8, 2)]
a
[3, 5, 7]

リストの要素は同じ型でなくてもかまいません。数値や文字列が混じったリストも可能です。

a = [3, 5.0, "seven"]
for x in a:
    print(x, type(x))
3 <class 'int'>
5.0 <class 'float'>
seven <class 'str'>

行列はリストのリストとして作れます:

a = [[2, 9, 4], [7, 5, 3], [6, 1, 8]]
a[1][2]
3

タプル

リスト以外に、Python には tuple(タプルまたはテュープルと読む)という1次元の配列のようなものがあります。[ ] を使えばリスト、( ) を使えばタプルです:

t = (2, 3, 4, 5)

要素1つのリストは [2] のように書きますが、タプルの場合は (2) ではなく特例として (2,) のように書きます。

リストは要素を変えられますが、タプルは変えられません:

x = [2, 3, 4, 5]
t = (2, 3, 4, 5)
x[2] = 30
t[2] = 30    # TypeError: 'tuple' object does not support item assignment
x.append(6)  # 末尾に 6 を追加
t.append(6)  # AttributeError: 'tuple' object has no attribute 'append'

NumPy の array

ベクトル・行列などの数値計算をより高速に行うには NumPy の array を使います。

import numpy as np

a = np.array([3, 5, 7])
a
array([3, 5, 7])
type(a)
numpy.ndarray
a.dtype
dtype('int64')

一つでも浮動小数点数にすると、全部が浮動小数点数になります。

a = np.array([3, 5, 7.])
a
array([3., 5., 7.])
a.dtype
dtype('float64')

データ型を指定することもできます:

a = np.array([3, 5, 7], dtype="int8")  # 8ビット整数型
a
array([3, 5, 7], dtype=int8)

0 に初期化された長さ n の配列を作るには np.zeros(n) とします。同様に、1 に初期化された長さ n の配列は np.ones(n) です。初期化の必要がなければ np.empty(n) とします。行列は、n のところを例えば (m, n) のように (行数, 列数) とします。同様に任意次元の配列が作れます。

a = np.ones((2, 3))
a
array([[1., 1., 1.],
       [1., 1., 1.]])
a = np.ones((2, 3, 4))
a
array([[[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]],

       [[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]]])

各要素にアクセスするには a[i, j] でも a[i][j] でも(通常は)同じ意味ですが、a[i, j] のほうが倍ほど速いようです:

a = np.empty((1000, 1000))

%timeit a[123][456]
%timeit a[123, 456]

ab が array なら、例えば a + b は要素ごとの和になります。つまり、ベクトルや行列の要素ごとの演算はループしなくても高速にできます。

a = a + ba + b を計算してそれに a という名前を付けます(古い a はアクセスできなくなります)。一方で、a += bab をその場で加えます。大きな array の計算はこの方がメモリを節約でき、速くなります:

a = np.empty((1000, 1000))
b = np.empty((1000, 1000))

%%timeit
global a, b
a = a + b  # または a += b

ベクトル・行列の生成は他に np.arange()(arange = array range)が便利です:

np.arange(6)
array([0, 1, 2, 3, 4, 5])
np.arange(6).reshape(2, 3)  # 2行3列にする
array([[0, 1, 2],
       [3, 4, 5]])

np.arange(3)array([0, 1, 2]) ですが np.arange(3.) は浮動小数点の array([0., 1., 2.]) になります。

np.empty(n, dtype="int8") などのようにデータ型を指定することもできます。

乱数のベクトルの生成は乱数を参照してください。

PyTorch や Tensorflow のテンソル

PyTorch や Tensorflow などの機械学習ライブラリでは「テンソル」(tensor)というものをよく使いますが、これも一種の配列です。

PyTorch なら import torch して、np.array() の代わりに torch.tensor()np.ones() の代わりに torch.ones() とします。


Last modified: