[Python]配列(List/Numpy)の基本を理解する

先生

Pythonの配列モジュールであるListとNumpyについて紹介します。

「配列」とは

プログラミングにおける「配列」とは、複数の値を一つの変数名でまとめて扱うためのデータ構造です。英語ではArrayと表現されます。一般的には、各要素(値)にインデックス(番号)を使ってアクセスすることができます。

Pythonでは、いくつか配列のライブラリがありますが、ここでは基本としてListとNumpyについて紹介します。

Listの基本を理解する

Listは標準モジュールのためimportする必要はありません。少し特徴的なのが、異なるデータ型でもまとめて格納することができます。

定義・初期化

空リストの作成は[ ]でOKです。要素をカンマ区切りで書けば、要素を格納できます。

Listの場合、データ型が違ってもOKです。

# 空リスト
list_empty = []

# 数値型
list_a = [ 1 , 2 , 3 ]

#文字型
list_b = [ "A" , "B" , "C" ]

# 二次元配列
list_c = [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] ]

# 異なるデータ型
list_d = [ 1 , "B" , 3.2 ]

要素指定

配列のインデックスは「0」から始まり、「配列の長さ-1」まで指定できます。「-1」を指定すると、末尾の要素を指定したことになり、「配列の長さ-1」と同じ指定になります。また:(コロン)を使って範囲を指定することもできます。

list_a = [ 1 , 2 , 3 , 4 , 5 , 6 ]

# 要素指定
print(list_a[2])
# 出力
# 3

# 末尾指定
print(list_a[-1])
# 出力
# 6

# 範囲指定
print(list_a[1:4])
# 出力
# [2, 3, 4]

追加

appendメソッドで要素の追加が可能です。末尾に追加されます。

list_a = [ 1 , 2 , 3 ]

list_a.append(4)
print(list_a)
# 出力
# [1, 2, 3, 4]

その他

そのほか、筆者がよく使うものを記載します。もっと詳しく知りたい場合は公式ドキュメントを確認してください。

list_a = [ 1 , 2 , 3 ]
list_b = [ "A" , "B" , "C" ]

# 要素数取得
print(len(list_a))
# 出力
# 3

# 結合
print(list_a + list_b)
# 出力
# [ 1 , 2 , 3 , "A" , "B" , "C" ]

Numpyの基本を理解する

Numpyは、importして使用します。別名としてよく「np」が使われるのでここでも「np」を使います。

import numpy as np

Listと異なり、一つの配列に同じデータ型しか格納することができません。ただし、行列計算などの数値計算が容易にできます。

定義・初期化

空の配列を定義する場合は、Listと同様に要素は何も書きません。「[ ]」の部分は、前節で説明した空のListです。直接要素を指定してもよいし、Listの変数を定義してからその変数を指定してもよいです。

Listのように整数型と文字型を混在させることはできません。混在させても全て文字型として格納されてしまいます。

import numpy as np

list_a = [ 1 , 2 , 3 ]

# 空の配列
np_empty = np.array([])
# 出力
# []


# Listの使用
np_a = np.array([ 1 , 2 , 3 ])
np_b = np.array(list_a)

print(np_a)
print(np_b)

# 出力
# [1, 2, 3]
# [1, 2, 3]


## 文字列と数値が混在する場合は、文字型となる
np_c = np.array([ 1 , "A", 3 ])
print(np_c)
print(np_c.dtype) # .dtypeで要素のデータ型を確認できる

# 出力
# ['1' 'A' '3']
# <U21

また指定のサイズで全てを0で定義する場合、np.zerosを使用して要素数を指定します。

import numpy as np

np_zero = np.zeros([ 2 , 3 ])
print(np_zero)

# 出力
# [[0. 0. 0.]
# [0. 0. 0.]]

要素指定

List同様に指定可能です。

import numpy as np
np_a = np.array([ 1 , 2 , 3 , 4 , 5 , 6 ])

# 要素指定
print(np_a[2])
# 出力
# 3

# 末尾指定
print(np_a[-1])
# 出力
# 6

# 範囲指定
print(np_a[1:4])
# 出力
# [2, 3, 4]

追加

appendメソッドで要素を追加できます。一つ目の引数に指定した変数自身は更新されないため注意してください。

import numpy as np

np_a = np.array([ 1 , 2 , 3 ])
np_b = np.append(np_a, [ 4 , 5 , 6 ])
print(np_a)
print(np_b)

# 出力
# [1, 2, 3]
# [1, 2, 3, 4, 5, 6]

## np_aは更新されないので注意

また、2次元以上の配列に対して追加をする場合、一次元になってしまうためこちらも注意が必要です。(サンプルコード参照)

import numpy as np

np_a = np.array([ [ 1 , 2 , 3 ],[ 4 , 5 , 6 ] ])
np_b = np.append(np_a, [ 7 , 8 , 9 ])
print(np_b)

# 出力
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

## 変数には一次元配列が格納されるので注意

演算

Numpyは行列の演算が得意で、以下のように演算ができます。加減乗除全て共通です。

# 行列同士の計算(要素数が異なるとエラー)
Numpy型1 + Numpy型2

# 行列と数値型の計算(Numpy型の全要素に加算される)
Numpy型 + 数値型

# 行列と文字型の計算(Numpy型の全要素に文字列が結合される)
Numpy型 + 文字列型

計算できない要素数・データ型であると、エラーとなるので注意してください。

import numpy as np

# 行列同士の計算
np_a = np.array([ 1 , 2 , 3 ])
np_b = np.array([ 4 , 5 , 6 ])
print(np_a + np_b)

# 出力
# [5, 7, 9]


# 行列と数値型の計算
np_a = np.array([ 1 , 2 , 3 ])
print(np_a + 10)

# 出力
# [11, 12, 13]


# 行列と文字型の計算
np_a = np.array([ "A" , "B" , "C" ], dtype=object)
print(np_a + "X")

# 出力
# ['AX', 'BX', 'CX']


# 行列同士の計算(計算できないデータ型)
np_a = np.array([ 1 , 2 , 3 ])
np_b = np.array([ "A" , "B" , "C" ])
print(np_a + np_b)

# 出力(エラー) 
# numpy.core._exceptions.UFuncTypeError: ufunc 'add' did not contain a loop with signature matching types (dtype('<U21'), dtype('<U21')) -> dtype('<U21')


# 行列同士の計算(計算できない要素数)
np_a = np.array([ 1 , 2 , 3 ])
np_b = np.array([ 4 , 5 ])
print(np_a + np_b)

# 出力(エラー) 
# ValueError: operands could not be broadcast together with shapes (3,) (2,) 

リサイズ

reshapeを使って、配列の形状を変更できます。1つ目の引数に指定した変数自身は更新されないので注意です。指定する行・列のうちどちらかを-1にすると、「全要素数/指定した行or列のサイズ」が自動でセットされます。例えば、行数が動的の場合、行数の指定を-1にすると対応できますね。

import numpy as np

# Exmaple 1
np_a = np.array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ])
np_b = np.reshape( np_a , (5,2) )
print(np_b)

# 出力
# [[ 1  2]
# [ 3  4]
# [ 5  6]
# [ 7  8]
# [ 9 10]]

## np_aは更新されないので注意
## 前後で行列のサイズが合わないとエラーとなるので注意


# Exmaple 2
np_a = np.array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ])
np_b = np_a.reshape( 5, 2 )
print(np_b)

# 出力
# [[ 1  2]
# [ 3  4]
# [ 5  6]
# [ 7  8]
# [ 9 10]]

## np_aは更新されないので注意
## 前後で行列のサイズが合わないとエラーとなるので注意


# Exmaple 3
np_a = np.array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ])
np_b = np_a.reshape( 5, -1 )
print(np_b)

# 出力
# [[ 1  2]
# [ 3  4]
# [ 5  6]
# [ 7  8]
# [ 9 10]]

## np_aは更新されないので注意
## 前後で行列のサイズが合わないとエラーとなるので注意

型変更

astypeを使って、Numpy配列のデータ型を変更できます。定義時にも指定できます。

import numpy as np

np_a = np.array([ 1 , 2 , 3 ])
np_b = np_a.astype(np.float64)

print(np_a)
print(np_a.dtype)
print(np_b)
print(np_b.dtype)

# 出力
# [1 2 3]
# int64
# [1. 2. 3.]
# float64

## np_aは更新されないので注意

その他

そのほか、Listの説明と同様に筆者がよく使うものを記載します。もっと詳しく知りたい場合は公式ドキュメントを確認してください。

import numpy as np

# 配列のサイズを取得(タプル型で取得)
np_a = np.array([ [ 1 , 2 , 3 ],[ 4 , 5 , 6 ] ])
print(np_a.shape)

# 出力
# (2, 3)


# 配列を一次元に変更
np_a = np.array([ [ 1 , 2 , 3 ],[ 4 , 5 , 6 ] ])
np_a_1d = np_a.flatten()
print(np_a_1d)

# 出力
# [1 2 3 4 5 6]

まとめ

以上です。今回はListとNumpyについてまとめました。

他にもpandasという代表的な配列ライブラリがあるので、別記事で紹介しようと思います。