【NumPy入門】配列の基本操作(代入・参照・コピー・結合・分割など)

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入門 サンプル集
Python
スポンサーリンク
西住工房

コメント