Pythonモジュール「NumPy」で配列を操作(代入・参照・コピー・結合・分割など)する方法についてまとめました。
【参照・アクセス・代入】1次元配列、2次元配列
NumPy配列では、リスト型と同じように要素の値に参照・アクセス・代入できます。
書式
– | 説明 |
---|---|
x[i] | 1次元配列xにおけるi番目の要素を参照します。(先頭は0番目) |
x[i:j] | 1次元配列xにおけるi番目からj-1番目までの要素を参照します。 |
x[i]=value | 1次元配列xにおけるi番目の要素に値を代入します。 |
x[i:j]=value | 1次元配列xにおけるi番目からj-1番目までの要素に値を代入します。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 x = np.array([1, 2, 3, 4, 5]) # 参照 print(x[4]) # 5 print(x[1:3]) # [2 3] # アクセス・代入 x[4] = 10 x[1:3] = 20 # 代入結果 print(x) # [ 1 20 20 4 10]
NumPyの2次元配列では、リスト型と同じように要素の値をスライスできます。
書式
– | 説明 |
---|---|
x = A[j][i] | 2次元配列Xにおけるj番目の行、i番目の列にある要素を参照します。 |
x = A[j, i] | 2次元配列Xにおけるj番目の行、i番目の列にある要素を参照します。 |
x = A[j+h, i+w] | 2次元配列Xにおけるj~j+h番目の行、i~i+w番目の列にある要素を参照します。 |
A[j][i] = value | 2次元配列Xにおけるj番目の行、i番目の列にある要素に値を代入します。 |
A[j, i] = value | 2次元配列Xにおけるj番目の行、i番目の列にある要素に値を代入します。 |
A[j+h, i+w] = value | 2次元配列Xにおけるj~j+h番目の行、i~i+w番目の列にある要素に値を代入します。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 X = np.array([[1, 2, 3], [4, 5, 6]]) # 参照 print(X[0][1]) # 2 (0行目1列目の要素) print(X[0,1]) # 2 (0行目1列目の要素) print(X[0:2, 0:2]) # [[1 2] (0~1行目,0~1列目の要素) # [4 5]] # 代入 X[0:2, 0:2] = 10 print(X) # [[10 10 3] # [10 10 6]]
– | 関連記事 |
---|---|
1 | ■【NumPy】1次元配列の先頭・末尾要素に参照・アクセス・代入 |
2 | ■【NumPy】2次元配列の要素を参照・スライス・代入 |
【配列のコピー】ndarray.copy
Pythonの数値計算モジュールNumPyでは、ndarray.copyで配列をコピーできます。
この場合、一方のオブジェクトの変更が他方に反映されません。
(「=」を使ってコピーすると一方の変更が他方にも反映されます)
# -*- coding: utf-8 -*- import numpy as np x = np.array([1, 2, 3]) # 配列xのコピー y = x.copy() y = y * 2 print("x=", x) # x=[1, 2, 3] print("y=", y) # y=[2, 4, 6]
– | 関連記事 |
---|---|
1 | ■【NumPy】配列のコピー (ndarray.copy) |
【縦方向に結合】numpy.vstack
Pythonの数値計算モジュール「NumPy」では、numpy.vstackメソッドで配列を縦方向に結合できます。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2]]) B = np.array([[3, 4], [5, 6]]) C = np.vstack([A, B]) # 画面出力 print(C) """ [[1 2] [3 4] [5 6]] """
– | 関連記事 |
---|---|
1 | ■【NumPy】配列を縦方向に結合 (numpy.vstack) |
【横方向に結合】numpy.hstack
numpy.hstackメソッドで配列を横方向に結合できます。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) B = np.array([[5, 6], [7, 8]]) C = np.hstack([A, B]) # 画面出力 print(C) """ [[1 2 5 6] [3 4 7 8]] """
– | 関連記事 |
---|---|
1 | ■【NumPy】配列を横方向に結合 (numpy.hstack) |
【配列の分割】縦方向、横方向
NumPy配列では配列の要素を配列を分割する機能が用意されています。
– | 説明 |
---|---|
y = numpy.vsplit(x, n) | 配列xを縦方向にn個に分割します。 |
y = numpy.hsplit(x, n) | 配列xを横方向にn個に分割します。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 x = np.array([[1, 2, 3], [4, 5, 6]]) # 縦方向に分割 print(np.vsplit(x,2)) # [array([[1, 2, 3]]), array([[4, 5, 6]])] # 横方向に分割 print(np.hsplit(x,3)) # [array([[1], [4]]), # array([[2],[5]]), # array([[3], [6]])]
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の分割(縦方向・横方向) |
【配列の次元数を変更】numpy.reshape
Pythonの数値計算モジュールNumPyでは、numpy.reshape(m, n)メソッドでm×n(行数m、列数n)の配列に変換できます。
# -*- coding: utf-8 -*- import numpy as np # 1次元配列の宣言・初期化 x = np.array([1, 2, 3, 4, 5, 6]) # 配列の次元を変更(行数2, 列数3の2次元配列に変換) X = x.reshape(2, 3) # 画面出力 print(X) """ [[1 2 3] [4 5 6]] """
1次元配列を2×3の2次元配列に変換できました。
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の次元数を変更(numpy.reshape) |
【データ型を変更】numpy.astype
Pythonの数値計算モジュールNumPyでは、numpy.astypeで配列要素のデータ型を変更できます。
【詳細】
・データ型の種類一覧
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) # データ型の変換 B = A.astype('float64') # 画面出力 print(B.dtype) #float64
– | 関連記事 |
---|---|
1 | ■【NumPy】配列要素のデータ型を変更 (numpy.astype) |
配列の書き込み禁止
配列にread only属性を与えることで、読み込み専用(書き込み禁止モード)に変更できます。
書式
a.flags.writeable = False
※Trueにすると書き込み許可(デフォルト)
# -*- coding: utf-8 import numpy as np # 配列生成 A = np.array([[1, 0], [0, 1]]) A.flags.writeable = False A[0, 0] = 5 # 結果表示 print(A)
上記のプログラムを実行すると、書き込み禁止モード中に書き込みしようとするので
ValueError: assignment destination is read-only
というエラーが出ます。
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の書き込み禁止 |
【昇順・降順ソート】numpy.sort
NumPy配列では配列の要素を昇順・降順ソートできる機能が用意されています。
書式
– | 説明 |
---|---|
y = numpy.sort(x) | 配列xの要素を昇順ソートします。 |
y = numpy.sort(x)[::-1] | 配列xの要素を降順ソートします。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 x = np.array([1, 3, 2]) print(np.sort(x)) # 昇順 [1 2 3] print(np.sort(x)[::-1]) # 降順 [3 2 3]
– | 関連記事 |
---|---|
1 | ■【NumPy】配列のソート(昇順・降順) |
【要素の位置をずらす】numpy.roll
NumPyには、numpy.roll(ndarray, num)メソッドで配列ndarrayの要素をnum個分だけ左右にずらします。
# -*- coding: utf-8 -*- import numpy as np # 1次元配列の生成 x = np.array([1, 2, 3, 4, 5]) # 右に1要素分ずらす x1 = np.roll(x, 1) print('x1=', x1) # x1= [5 1 2 3 4] # 左に1要素分ずらす x2 = np.roll(x, -1) print('x2=', x2) # x2= [2 3 4 5 1]
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の要素の位置をずらす |
【算術演算】配列の要素同士を計算
算術演算子(*, /, +, -)で配列の要素同士の掛け算、割り算、足し算、引き算ができます。
掛け算
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) B = np.array([[5, 6], [7, 8]]) C = A * B # 画面出力 print(C) """ [[ 5 12] [21 32]] """
割り算
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) B = np.array([[5, 6], [7, 8]]) C = B / A # 画面出力 print(C) """ [[5 3] [2 2]] """
余り
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) B = np.array([[5, 6], [7, 8]]) C = B % A # 画面出力 print(C) """ [[0 0] [1 0]] """
累乗
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) B = A**2 # 画面出力 print(B) """ [[ 1 4] [ 9 16]] """
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の要素同士の掛け算 |
2 | ■【NumPy】配列の要素同士の割り算 |
3 | ■【NumPy】配列の要素同士の余り |
4 | ■【NumPy】配列の要素の累乗 |
【データ抽出】行・列、要素、インデックス<
2次元配列から特定の行にある配列(1次元配列)を取り出せます。
書式
ndarray[要素番号]
■返り値
(要素番号+1)行目のデータ(1次元配列)
例えばndarray[1]なら2行目のデータを取り出せます。
# -*- coding: utf-8 import numpy as np # 2次元配列 A = np.array([[1, 2], [3, 4], [5, 6]]) # 1行目の値を取り出し a1 = A[0] # 結果表示 print(a1) # [1 2]
– | 関連記事 |
---|---|
1 | ■【NumPy】2次元配列から特定の行を取り出し |
Pythonの数値計算モジュールNumPyでは、次のようにして2次元配列から特定の列にあるデータを取り出せます。
書式
ndarray[ : , 要素番号]
■返り値
(要素番号+1)列目のデータ(1次元配列)
例えばndarray[1]なら2列目のデータを取り出せます。
# -*- coding: utf-8 import numpy as np # 2次元配列 A = np.array([[1, 2], [3, 4], [5, 6]]) # 1列目の値を取り出し a1 = A[:,1] # 結果表示 print(a1) # [2 4 6]
– | 関連記事 |
---|---|
1 | ■【NumPy】2次元配列から特定の列を取り出し |
NumPyの「numpy.where」を利用することで、指定した条件を満たす配列(リスト)の要素番号を取り出すことが出来ます。
書式
np.where(条件式)
今回は、配列y内の数値データのうち、10以上20未満のデータがある要素を取り出す。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言 y = np.array([8,9,10,11,15,18,22,21,20,29]) # 条件式(10以上20未満)を満たす要素を抽出 y2 = y[ np.where( (y>=10) & (y<20)) ] # 結果を表示 print(y2) #
– | 関連記事 |
---|---|
1 | ■【NumPy】配列から条件を満たす要素を抽出 |
NumPy配列では、numpy.random.choiceで配列から要素をランダム抽出できます。
書式
b = numpy.random.choice(a, n, replace=True)
配列aから要素n個をランダムに抽出します。
(replaceがTrueなら重複あり)
b = numpy.random.choice(a, n, p= [0.1, 0.9])
各要素の出現確率pを指定して配列aから要素n個をランダムに抽出します。
# -*- coding: utf-8 import numpy as np # 配列生成 a = np.array([1, 2, 3]) # 配列のランダム抽出 c1 = np.random.choice(a, 3, replace=True) # 重複あり c2 = np.random.choice(a, 3, replace=False) # 重複なし c3 = np.random.choice(a, 3, p=[0.1, 0.8, 0.1]) # 出現率pを指定 # 結果表示 print(c1) # [2 3 2] print(c2) # [1 2 3] print(c3) # [2 2 2]
– | 関連記事 |
---|---|
1 | ■【NumPy】配列から要素のランダム抽出 |
NumPyの「numpy.where」を利用することで、指定した条件を満たす配列(リスト)の要素番号を取り出すことが出来ます。
書式
np.where(条件式)
今回は、配列y内の数値データのうち、10以上20未満のデータがある要素番号xを取り出す。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言 y = np.array([8,9,10,11,15,18,22,21,20,29]) # 条件を満たすインデックスを取得 x = np.where((y>=10)&(y<20)) # [2 3 4 5] # 結果を表示 print(x[0])
– | 関連記事 |
---|---|
1 | ■【NumPy】配列から条件を満たす要素番号を抽出 |
NumPyでは、配列の非0要素のインデックス(要素番号・位置)を取り出す機能が用意されています。
書式
– | 説明 |
---|---|
index = numpy.nonzero(x) | 配列xの値が0以外の要素のインデックスを取得します。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言 x = np.array([2, 1, 0]) # 非0要素のインデックス print(np.nonzero(x)) # (array([0, 1]),)
– | 関連記事 |
---|---|
1 | ■【NumPy】非0要素のインデックス |
NumPyでは、配列の最大値・最小値要素のインデックス(要素番号・位置)を取り出す機能が用意されています。
書式
– | 説明 |
---|---|
index = numpy.argmin(x) | 配列xの最小値要素のインデックスを取得します。 |
index = numpy.argmax(x) | 配列xの最大値要素のインデックスを取得します。 |
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言 x = np.array([2, 1, 3]) # 最小値要素のインデックス print(np.argmin(x)) # 1 # 最大値要素のインデックス print(np.argmax(x)) # 2
– | 関連記事 |
---|---|
1 | ■【NumPy】最大値・最小値要素のインデックス |
【情報取得】配列サイズ、要素数、データ型、次元数、バイト数
numpy.shapeメソッドで配列の大きさ(行数・列数)を取得できます。
# -*- coding: utf-8 -*- import numpy as np # 2次元配列の宣言・初期化 A = np.array([[1, 2], [3, 4], [5, 6]]) # 行列の大きさ print("行列Aの大きさ:", A.shape) print("行列Aの行数:", A.shape[0]) print("行列Aの列数:", A.shape[1]) """ 行列Aの大きさ:(3, 2) 行列Aの行数:3 行列Aの列数:2 """
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の大きさ(行数・列数)を取得 |
numpy.dtypeで配列要素のデータ型を表示できます。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2],[3, 4]]) # 画面出力 print(A.dtype) # int32
– | 関連記事 |
---|---|
1 | ■【NumPy】配列要素のデータ型を表示 (numpy.dtype) |
numpy.diag(array)メソッドで配列arrayの対角成分を取り出すことができます。
# -*- coding: utf-8 -*- import numpy as np # 配列の宣言・初期化 A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # 画面出力 print(np.diag(A)) # [1 5 9]
– | 関連記事 |
---|---|
1 | ■【NumPy】行列(配列)の対角成分を取得 (numpy.diag) |
count_nonzeroメソッドで、値が0でない要素数をカウントできます。
今回はそれを用いて0の要素数を求めてみました。
書式
numpy.count_nonzero(ndarray)
■返り値
値が0でない要素数
# -*- coding: utf-8 import numpy as np # 配列生成 A = np.array([0, 1, 1, 1, 2, 0, 0]) # 0の要素数 = 全要素数 - 0でない要素数 num = len(A) - np.count_nonzero(A) # 結果表示 print(num) # 3
– | 関連記事 |
---|---|
1 | ■【NumPy】値が0である要素数をカウント |
count_nonzeroメソッドで、値が0でない要素数をカウントできます。
書式
numpy.count_nonzero(ndarray)
■返り値
値が0でない要素数
# -*- coding: utf-8 import numpy as np # 配列生成 A = np.array([0, 1, 1, 1, 2, 0, 0]) # 値が0でない要素数をカウント num = np.count_nonzero(A) # 結果表示 print(num) # 4
|関連記事
–|–
1|■【NumPy】値が0でない要素数をカウント
whereメソッドで、条件を満たす要素番号を抽出できます。
今回はそれを用いて任意の値をもつ要素数を求めてみました。
# -*- coding: utf-8 import numpy as np # 配列生成 A = np.array([0, 1, 1, 1, 2, 0, 0]) # 値が2の要素数 num = len(np.where(A==1)[0]) # 結果表示 print(num) # 3
– | 関連記事 |
---|---|
1 | ■【NumPy】任意の値をもつ要素数をカウント |
NumPy配列では、次元数・バイト数を簡単に計算できます。
# -*- coding: utf-8 -*- import numpy as np # 2次元配列の宣言・初期化 A = np.array([[1, 2], [3, 4], [5, 6]]) # 行列の大きさ print("行列Aの次元数:", A.ndim) # 行列Aの次元数: 2 print("行列Aのバイト数(全体):", A.nbytes) # 行列Aのバイト数(全体): 48 print("行列Aのバイト数(1要素):", A.itemsize) # 行列Aのバイト数(1要素): 8
– | 関連記事 |
---|---|
1 | ■【NumPy】配列の次元数・バイト数 |
【配列の比較】完全一致
allcloseメソッドで2つの配列を比較し、全ての要素が完全に一致するか調べることができます。
書式
numpy.allclose(ndarray)
■返り値
完全一致:True、不一致:False
# -*- coding: utf-8 import numpy as np # 配列生成 A = np.array([[1, 0], [0, 1]]) B = np.array([[1, 0], [0, 1]]) # 一致判定 flag = np.allclose(A, B) # 結果表示 print(flag) # True
– | 関連記事 |
---|---|
1 | ■【NumPy】2つの配列が完全一致するか判定 |
【高速化】Numbaモジュール
Python用数値計算ライブラリ「NumPy」の配列の要素にfor文でアクセスすると、処理速度が急低下する問題があります。
for文を使わずに「NumPyのメソッド」や「他のライブラリ」で処理を実装するのが鉄則ですがどうしてもfor文を使いたい場合があります。
そんなときは「Numba」ライブラリを使うことで元のソースコードをほとんど弄らずに高速化できます。
NumPy
#-*- coding:utf-8 -*- import cv2 import numpy as np import time def filter2d(src, kernel, fill_value=-1): # カーネルサイズ m, n = kernel.shape # 畳み込み演算をしない領域の幅 d = int((m-1)/2) h, w = src.shape[0], src.shape[1] # 出力画像用の配列 if fill_value == -1: dst = src.copy() elif fill_value == 0: dst = np.zeros((h, w)) else: dst = np.zeros((h, w)) dst.fill(fill_value) for y in range(d, h - d): for x in range(d, w - d): # 畳み込み演算 dst[y][x] = np.sum(src[y-d:y+d+1, x-d:x+d+1]*kernel) return dst def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input2.jpg", 0) # 処理開始時間の計測 start = time.time() # カーネル(縦方向の輪郭検出用) kernel = np.array([[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]) # 方法1(NumPyで実装) dst1 = filter2d(gray, kernel, -1) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output1.jpg", dst1) if __name__ == "__main__": main()
NumPy+Numba
#-*- coding:utf-8 -*- import cv2 import numpy as np import time import numba @numba.jit def filter2d(src, kernel, fill_value=-1): # カーネルサイズ m, n = kernel.shape # 畳み込み演算をしない領域の幅 d = int((m-1)/2) h, w = src.shape[0], src.shape[1] # 出力画像用の配列 if fill_value == -1: dst = src.copy() elif fill_value == 0: dst = np.zeros((h, w)) else: dst = np.zeros((h, w)) dst.fill(fill_value) for y in range(d, h - d): for x in range(d, w - d): # 畳み込み演算 dst[y][x] = np.sum(src[y-d:y+d+1, x-d:x+d+1]*kernel) return dst def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input2.jpg", 0) # 処理開始時間の計測 start = time.time() # カーネル(縦方向の輪郭検出用) kernel = np.array([[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]]) # 方法1(NumPyで実装) dst1 = filter2d(gray, kernel, -1) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output1.jpg", dst1) if __name__ == "__main__": main()
OpenCV
#-*- coding:utf-8 -*- import cv2 import numpy as np import time def main(): # 入力画像をグレースケールで読み込み gray = cv2.imread("input.jpg", 0) # 処理開始時間の計測 start = time.time() # 方法3(OpenCVで実装) dst3 = cv2.blur(gray, ksize=(3,3)) # 処理終了時刻の計測 end = time.time() # 処理時間の表示 print("処理時間" + str(end-start) + "[sec]") # 結果を出力 cv2.imwrite("output3.jpg", dst3) if __name__ == "__main__": main()
結果は以下の通りになりました。
■実行環境
項目 | 説明 |
---|---|
入力画像 | グレースケール画像(440×450[px]) |
OS | Windows10 Home Premium 64bit |
メモリ容量 | 4GB |
CPU | Core i3-2330M 2.20GHz |
■結果
– | 処理速度[sec] |
---|---|
NumPy | 3.2753376960754395[sec] |
NumPy+Numba | 1.225313425064087[sec] |
OpenCV | 0.13230109214782715[sec] |
Numbaを使うと3倍くらい速くなりました。
ただし、今回の画像処理の場合ではOpenCVの方がさらに10倍程度速くなりました。
NumbaはGPUを使うなど色々な設定が出来るので、もうちょっと弄ればより速くなると思います。
– | 関連記事 |
---|---|
1 | ■【Numba】NumPy + for文の高速化 |
– | 関連記事 |
---|---|
1 | ■Python入門 サンプル集 |
2 | ■NumPy入門 サンプル集 |
コメント