Python♪NumPyのa[i, j, k]とa[i][j][k]の違い

NumPy配列では要素を参照するときに、a[i, j, k]とa[i][j][k]という2つの表記法があります。「そうだっけ」と思った方はその違いを整理しておきましょう。計算速度、要素へのアクセスの考え方が違いますので注意が必要です。

0.「ゼロから作るDeep Learning」のポイント整理シリーズ

この記事は「ゼロから作るDeep Learning」のポイント整理シリーズの記事です。シリーズの名前のとおり私の自習用のメモです。書籍では説明されない基礎文法や補足・関連事項などを説明しており、書籍がなくてもわかる内容になっています。

1.計算速度の違い

実はa[i, j, k]とa[i][j][k]では要素を参照する速度が違います。そこで実際に計算速度を確認してみました。コード01では10行10列の要素の内容をランダムに100000回書き換えます。

計算結果はa[i, j, k]が9.1秒、a[i][j][k]が17.6秒であり、計算速度に約2倍程度の差が生じました。

NumPyは速度を重視する計算で使うことが多いと思いますし、一般的にはa[i, j, k]の表記法を使います。

また、この表記法を使うことでリストとはっきり区別できるためプログラムが読みやすくなります。

#コード01
import random
import time
import numpy as np

nnn = 100000  #データ数
n_dim = 10

a = np.zeros((nnn, n_dim, n_dim))
start = time.time()  #時間計測開始
#------(データ生成)-------
for i in range(nnn):
    for j in range(n_dim):
        for k in range(n_dim):
            a[i, j, k]= random.random()
#--------------------------
print('a[i, j, k]:', time.time() - start, '秒')

a = np.zeros((nnn, n_dim, n_dim))
start = time.time()  #時間計測開始
#------(データ生成)-------
for i in range(nnn):
    for j in range(n_dim):
        for k in range(n_dim):
            a[i][j][k]= random.random()
#--------------------------
print('a[i][j][k]:', time.time() - start, '秒')
#出力01
a[i, j, k]: 9.109277486801147 秒
a[i][j][k]: 17.570679187774658 秒

2.要素へのアクセスの考え方

NumPy配列において注意しなければならないのは、a[i, j, k]タイプとa[i][j][k]タイプでは違う値になることがあります。例えばa[:, :, 1]とa[:][:][1]のような場合です。

(1) a[i, j, k]タイプ

それでは、a[i, j, k]タイプの要素指定の結果を見てみます。コード02のa[:, :, 1]では「:」の部分は要素が限定されませんので、1を指定した次元だけ要素が限定されます。

具体的には3次元配列aの[0, 1, 2, 3]、[4, 5, 6, 7]、[8, 9, 10, 11]、[12, 13, 14, 15]、[16, 17, 18, 19]、[20, 21, 22, 23]は、それぞれ1番目の要素である1, 5, 9, 13, 17, 21が選択され、結果は出力02の10~12行目となります。

つまり、a[i][j][k]タイプの表記はi, j, kが、それぞれの次元の範囲を指定します。

#コード02
import numpy as np

a = np.arange(24).reshape(2, 3, 4)
print('a =')
print(a)
print('a[:, :, 1] =')
print(a[:, :, 1])
#出力02
a =
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
a[:, :, 1] =
[[ 1  5  9]
 [13 17 21]]

(2) a[i][j][k]タイプ

次にa[i][j][k]タイプの要素指定について考えます。a[i][j][k]タイプの出力が出力03です。

a[i, j, k]の時とは異なりa[i][j][k]では前から順番に考えます。具体的には最初にa[:]、次にa[:][:]、最後にa[:][:][1]を考えます。

具体的に説明すると、まず、a[:][:][1]は最初に左側のa[:]を考えます。a[:]はもちろんaと同じ内容の配列です。次にa[:][:]を考えます。a[:]がaと同じだったので「a[:][:] → (a[:])[:] → a[:] → a」と考えればaと同じです。最後にa[:][:][1]ですが、a[:][:]がaと同じなので、結局a[:][:][1]はa[1]と同じです。

#コード03
import numpy as np

a = np.arange(24).reshape(2, 3, 4)
print('a =')
print(a)
print('a[:][:][1] =')
print(a[:][:][1])
#出力03
a =
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
a[:][:][1] =
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]

コード04は、a、 a[:]、a[:][:]、a[:][:][1]の出力結果です。この出力結果を見ると、左側から順番に考えるという方法が理解できるのではないでしょうか。

#コード04
import numpy as np

a = np.arange(24).reshape(2, 3, 4)
print('a =')
print(a)
print('a[:] =')
print(a[:])
print('a[:][:] =')
print(a[:][:])
print('a[:][:][1] =')
print(a[:][:][1])
#出力04
a =
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
a[:] =
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
a[:][:] =
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
a[:][:][1] =
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]

このように、一見a[:, :, 1]とa[:][:][1]は同じに見えるかもしれませんが、全く出力結果が異なるので注意が必要です。

(3) リストのa[:][:][1]

最後にNumPyではなくリストのa[:][:][1]の出力を確認してみましょう。リストのa[i][j][k]の場合は、NumPyのa[i][j][k]のように前から順番に考えればよいことがわかります。

#コード05
a =[[[0, 1, 2, 3],
     [4, 5, 6, 7],
     [8, 9, 10, 11]],
 
    [[12, 13, 14, 15],
     [16, 17, 18, 19],
     [20, 21, 22, 23]]]

print('a =')
print(a)
print('a[:][:][1] =')
print(a[:][:][1])
#出力05
d3_list =
[[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]
d3_list[:][:][1] =
[[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]

3.まとめ

ディープラーニングの計算では速さが求められます。NumPy配列ではリストと同じa[i][j][k]タイプの表記も使えますが、a[i, j, k]タイプの表記を積極的に使いましょう。以下、この記事のまとめです。

  • a[i, j, k]の方がa[i][j][k]よりも計算速度が速い。
  • a[i, j, k]はそれぞれの次元の範囲をi, j, kで指定する。
  • a[i][j][k]は、a[i]→a[i][j]→a[i][j][k]のように前から順番に考える。

私が実際に購入した教材のご紹介

以下、私が実際に購入したPythonの教材をまとめてみました。 Pythonを学習する上で、少しでもお役に立つことができればうれしいです。

Python♪私が購入したPythonの書籍のレビュー
UdemyのPythonの動画講座を書籍を買う感覚で購入してみた

その他

Twitterへのリンクです。SNSもはじめました♪

以下、私が光回線を導入した時の記事一覧です。
 (1) 2020年「光回線は値段で選ぶ」では後悔する ←宅内工事の状況も説明しています。
 (2) NURO光の開通までWiFiルーターを格安レンタルできる
 (3) NURO光の屋外工事の状況をご紹介。その日に開通!
 (4) 光回線開通!実測するとNURO光はやっぱり速かった
 (5) ネット上のNURO光紹介特典は個人情報がもれないの?