Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

【Numpy入門】Numpyの多次元配列ndarrayの基本的な考え方を解説。ベクトルや行列との違いとは?

初心者向けにPythonのNumpyにおける多次元配列ndarrayについて、その初歩的なイメージを解説

今回はPythonで数値計算などを専門的に行うための定番パッケージ(モジュールが集まったもの)Numpyについての初歩を説明する記事です。特に今回はNumpyにおける多次元配列であるnumpy.ndarrayについて、その基本的な構造や考え方の入り口を大雑把ではありますが説明したいと思います。

プログラミング言語の人気オンラインコース

多次元配列(ndarray)は、ベクトルや行列とどう違うのか?

ベクトルとは

学問という観点でベクトルとは何か?とはなかなか説明が難しいです。数学と物理学では同じ「ベクトル」という言葉を使っていても意味が違うことも多いです。

ですが、通常のプログラミングでいうところのベクトルはほとんどの場合が、数字を並べたもの、という程度の意味だと思っておけば良いでしょう。大学受験レベルの数学と同じように考えておけばいいと思います。

たとえば、

$$ (-3, 1, \displaystyle\frac{\sqrt{2}}{7}) $$

みたいなやつですね。なお、上の例では横並びにしていますが、次のように縦並びにしてもおなじ意味のベクトルです。表し方が違うだけで同じベクトルを意味します。

\begin{pmatrix} -3 \\ 1 \\ \displaystyle\frac{\sqrt{2}}{7} \end{pmatrix}

これは要素が3つあります。これを3次元ベクトルと言います。ベクトルにおける次元とは単純に要素の数です。

行列とは

次に行列ですが、これも普通のプログラミングの世界では数学ほど厳密で正確な知識は不要ですし、普通のプログラミングで使う行列とは上のベクトルを複数個並べたもの、というレベルの理解でいいでしょう。

たとえば、

\begin{bmatrix} -3 & 5 \\ 1 & 0 \\ \displaystyle\frac{\sqrt{2}}{7} & \displaystyle\frac{1}{3}\\ \end{bmatrix}

という感じです。

行列とは「行」と「列」が組み合わせれた概念です。では縦方向か横方向のどちらが行でどちらが列なのでしょう?次の画像を見てください。

行列の行と列の考え方
行列の考え方

左から右に見るのが「行」、上から下に見るのが「列」です。最初はどっちがどっちか混乱しがちですが、簡単な覚え方があります。どちらが行でどちらが列か迷ったときは、横書きノートの思い出してください。

数学やプログラミングは当たり前ですが横書きです。欧米の文化(?)ですから。なので、「何行目」かは横書きノートの行数を数える場合と同じになります。横書き文化なので横方向に見るのが行です。

上の例だと、行が3つと列が2つです。それゆえ、上の例の行列は「3 × 2行列」と呼ばれたりします。「行列」なのでその文字面にあわせて行の情報が先、列の情報がその後となります。

ベクトルと行列は基本的には数学上の概念です。

多次元配列ndarrayの基本的なイメージと考え方

総論

Numpyにおける多次元配列ndarrayについてですが、まず名前のndarrayとは、「N-dimension array」という英語の略で、これは「N次元配列」という日本語になります。

ですが配列というのは数学の世界の概念ではありません。乱暴にいうと数学上はベクトルを1つ以上組み合わせたものが行列ですが、だからといって行列を組み合わせると数学上の配列になるかというとそうではありません。

あくまでプログラミングという分野の用語です。また日常用語の「配列」とも少し意味が違います。さらにいえば、ndarrayはやはりndarrayであって、「ndarrayという独特な配列」だと考えておくほうが良いと思います。

では、ndarrayとは何かですが、誤解を恐れず乱暴にいってしまうと、Pythonにおける「見た目は多重リストっぽい形をした特別なコンテナオブジェクト」を生成するクラス(型)です。Pythonのコンテナオブジェクトの例としてタプル、リスト、辞書がありますが、ndarrayによってそれらとは別の型のオブジェクトが作成されることになります。

使用例とndarrayのイメージ

具体的なコードを見てみましょう。

# as npとすると、毎回numpyと入力せずにnpだけで済む
import numpy as np

#リストを2つ作成
element_1 = [1, 2, 3, 4]
element_2 = [5, 6, 7, 8]

#np.arrayメソッドを使って、np.ndarrayオブジェクトを作成
ndarray_1 = np.array([element_1, element_2])

#表示
print(ndarray_1)

実行すると、(見やすくなるようちょっと加工しています)

[
 [1 2 3 4]
 [5 6 7 8]
]

となります。Pythonのリストそのものではありませんが、リストっぽいものが作れました。コンマによって各要素が区切られていないことに注意してください。

型を確認してみましょう。

type(ndarray_1)

結果は、

<class 'numpy.ndarray'>

リストでもタプルでも辞書でもない、numpy.ndarrayという型(クラス)のオブジェクトであることが確認できます。

さてできあがったndarrayオブジェクトをもう1度みてみましょう。

[
 [1 2 3 4]
 [5 6 7 8]
]

このようにまるで「リストの中にリスト」があるという外観です。リストそのものではありません。「あくまでリストっぽい」というだけです。上述にように要素を区切るコンマがなくなっている点に着目してください。

そしてこれだけなら、上で紹介した行列と同じです。縦x横という感じですね。

次に少し複雑なndarrayを作ってみます。

ndarray_2 = np.array([ndarray_1, ndarray_1+[10]])
print(ndarray_2)

ndarray_1+[10]の部分については実は、

[1+10, 2+10, 3+10, 4+10]
[5+10, 6+10, 7+10, 8+10

という感じの計算になります。今回は[10]というのは、[[10, 10, 10, 10], [10, 10, 10, 10]]ということと同じ扱いになります。このへんの理屈は上で紹介した数学における行列をその入門だけでも学習してからのほうがわかりやすいかもしれません。数学という学問上でそうなるようになっています。

さて結果は、(これも見た目を少しいじっています)

[
   [
     [1 2 3 4]
     [5 6 7 8]
   ]

   [
     [11 12 13 14]
     [15 16 17 18]
   ] 
]

3重リストのような形になっているのがわかるでしょうか。ここも通常のリストとは違いコンマがない点に注目してください。

大きな構造としては、

[ [□], [■] ]

このようになっていて、その[□]と[■]の中に、

[□]: [1 2 3 4]と[5 6 7 8]
[■]: [11 12 13 14]と[15 16 17 18]

この構造のイメージは次のようになります。

numpyのndarrayのイメージ図

2つの行列が上下に並んでいる感じですね。建物の1Fと2Fというイメージでもいいと思います。行列は縦×横というイメージでしたが、今回はそれに「高さ」のような情報が加わっているイメージです。立体的(3次元っぽく)なったという感じですね。

この作った多次元配列の構造を見てみましょう。shapeプロパティを使って、

ndarray_2.shape

と実行すると、

(2, 2, 4)

となります。

これの意味ですが、オフィスビルのイメージでいうと次のようになります。

  • 最初の2:何階建てか。今回は2階建て
  • 中央の2:各階にいくつの会社が入っているか。今回は2つ
  • 最後の4:1つの会社に従業員は何人か。今回は4人ずつ

このようなイメージですね。

では一番上にあるフロアのデータ(行列)だけを取得してみましょう。Pythonのリストのインデックスを使う感じで、「高さ」という情報のインデックスを使いましょう。Pythonの普通のリストとおなじく、一番最初のインデックスは0から始まるので、次のようにします。

print(ndarray_2[0])

結果は、

[
  [1 2 3 4]
  [5 6 7 8]
]

上手くいきました。

そしてこの部分のndarray_2[0]もやはりndarrayオブジェクトです。

もちろんもう1枚のほうは、

ndarray_2[1]

を使えばOKです。

ndarray_2[0]によって取得した最初の高さに入っているデータをもう1度みましょう。

[
  [1 2 3 4]
  [5 6 7 8]
]

この中にもまた2つのリストっぽいndarrayが含まれています。そのうち2つ目のデータを取得してみましょう。

ndarray_2[0][1]

というようにインデックス情報を2つ重ねます。すると、

print(ndarray_2[0][1])

結果は、

[5 6 7 8]

いけました。これも型を調べてみるとやはりNumpyのndarray型(numpy.ndarray)だとわかります。

では最後にこの3つの数字から最後の6だけを取り出してみましょう。

print(ndarray_2[0][1][2])

結果は、

6

成功です。ちなみにこれはただの整数ですが、型はnumpy.int32という型になります。

さて、上の総論のところで、

ndarrayとは何かですが、誤解を恐れず乱暴にいってしまうと、Pythonにおける「見た目は多重リストっぽい形をした特別なコンテナオブジェクト」を生成するクラス(型)

このように書きましたが、ここまでの使用例で「見た目は多重リストっぽい」、しかしリスト型などとは異なる型の特別なオブジェクトを生み出しているのがわかっていただけたと思います。

普通のリストとの違いは?

Pythonでも普通に多重リストを作成できます。次の記事でもその作り方・考え方を説明しています。

上述のようにndarrayは多重リストっぽい見た目(あくまで「ぽい」)ですが、だったら別に普通に多重リストそのものを使えばいいのではないか、とも思われます。

しかし、Pythonの普通の多重リストとndarrayとでは次のような違いがあります。

  • ndarrayのほうが計算が早い
  • 多重リストよりも、配列の形状の変更や配列同士の計算が便利
  • 普通の多重リストよりもnparrayはリスト内部の要素の型に厳格。基本的にリストの要素は全て同じ型でなければならな(エラーが絶対に出るわけではないが、そのほうが良い)
  • ndarrayのほうがリスト内部の要素の数についても厳格で、各行の列の数は同じでなければならない

などといった点で違いがあります。

以上、本当にNumpyにおける多次元配列ndarrayの初歩の初歩のイメージを簡単に説明しただけでしたが、今回の内容だけでも頭に入れておいてもらえるとNumpyの理解がはかどると思います。なお、Numpyについても動画学習サイトUdemyでも解説講座があるので以下の人気講座などをチェックしてみてください。

愛を分かち合いましょう