NumPyのarrayとndarrayの違いを調べた

numpy.arrayとnumpy.ndarray、どちらもたまに見かけるのだが、あまり区別がついていなかった。arrayとndarrayの違いを調べてまとめた。 「numpy array ndarray difference」で検索すると出てくるStack Overflowの質問の内容を主に、検証結果をまとめる。

numpy.arrayとnumpy.ndarrayの違いを一言でいうと

2つの違いを一言でいうと、こうなる。

  • numpy.ndarrayはデータ型(正確にはpythonのクラス)である
  • numpy.arrayは、numpy.ndarrayを作成するための関数である

2つをごっちゃにしてたけど、別物だった。
なので自分で使うのはarray。typeを調べた結果として登場するのがndarray。という違いである。
以下、具体例を使って細かく見ていく。

両者の公式ドキュメントは以下。
numpy.ndarray — NumPy v1.18 Manual
numpy.array — NumPy v1.18 Manual

numpy.arrayとnumpy.ndarrayの簡単な例

import numpy as np
import pandas as pd
pd.options.display.notebook_repr_html = False  # jupyter notebook上での出力形式を制御するために書いています。無くても動きます。
x = np.array([1, 2, 3])
x

# --------------------

array([1, 2, 3])
type(x)

# --------------------

numpy.ndarray
isinstance(x, np.ndarray)

# --------------------

True

pythonの標準リストをもとに、numpy.array関数を使ってxを作った。
type()を使ってどのクラスに属しているかを表示している。
また、isinstance関数で、xがnumpy.ndarrayクラスのインスタンスであることを確認している。

xを表示したときに登場するarrayとは何ものなのか?

上記でxを表示するとarray([1, 2, 3])と表示される。このarrayは何なんだろうか。
[1, 2, 3]と単に書いてあったらpythonの標準リストになってしまうので、区別をつけるために必要なんだろうけど。
でもこのarrayはnumpy.array関数とは別物だよね。arrayとは何だろうか。その答えはnumpyの用語集に書いてある。

https://numpy.org/doc/stable/glossary.html

array
A homogeneous container of numerical elements. Each element in the array occupies a fixed amount of memory (hence homogeneous), and can be a numerical element of a single type (such as float, int or complex) or a combination (such as (float, int, float)). Each array has an associated data-type (or dtype), which describes the numerical type of its elements:
DeepL翻訳して修正:数値要素の同質な入れ物。arrayの中の各要素は一定量のメモリを占有します(そのため同質です)。 各要素は単一の型(float, int, complex など)の数値要素であっても、その組み合わせ( (float, int, float) など)であっても構いません。それぞれのarrayには,その要素の数値型を表すデータ型(別名 dtype)が関連付けられています。

Glossary(用語集)に載っている、numpyの「用語」である。
numpy.array()関数と紛らわしいので、正確を期すために以降では「array(numpy用語)」と書くことにする。言葉の使い方に気をつけつつ、上記の操作を説明すると、以下のようになる。

  • pythonのlistから、numpy.array関数を使って、xを作りました。
  • xはarray(numpy用語)です。
  • xはnumpy.ndarrayクラスのインスタンスです。

※ 「array(numpy用語)」というくどい書き方をする人はあまりいないだろうと思って、参考書ではどう書いているのか調べてみた。「NumPy配列」という書き方でした。

2.2 NumPy配列の基礎
Pythonデータサイエンスハンドブック p.41

本書で「配列」、「NumPy配列」、あるいは「ndarray」という言葉が出てきた場合、ほぼ例外なくndarrayオブジェクトを指すものと考えてください。
Pythonによるデータ分析入門 p.96

というわけで、「Numpy配列」と「array(numpy用語)」は同じことである。以下のように言うことも可能だ。

  • xはNumPy配列です。

pythonのリストやpandasのSeries/dataframeからNumPy配列にしたいんだけど?

(arrayかndarrayのどちらかを使うなら)arrayを使う。できあがったものはnumpy.ndarrayクラスのインスタンスになる。

x = np.array([[1, 2], [3, 4]])
x

# --------------------

array([[1, 2],
       [3, 4]])
df = pd.DataFrame({
    'col_A': [1 ,3, 5],
    'col_B': [9, 7, 5],
    'col_C': [111, 222, 333],
})
df

# --------------------

   col_A  col_B  col_C
0      1      9    111
1      3      7    222
2      5      5    333
x = np.array(df)
x

# --------------------

array([[  1,   9, 111],
       [  3,   7, 222],
       [  5,   5, 333]])

だけど、ndarrayで配列を作ることも出来るよね?

できる。numpy.ndarray()と書けば、numpy.ndarrayクラスのコンストラクタを呼び出すことになるからだ。 しかし、推奨されていない方法である。一般的には使わないほうがよいだろう。

Arrays should be constructed using array, zeros or empty (refer to the See Also section below). The parameters given here refer to a low-level method (ndarray(…)) for instantiating an array.
拙訳:array(numpy用語)を作るときは、array, zeros, empty関数を使うべきです(下記のSee Alsoセクションも参照)。ここで(=ndarrayのコンストラクタで)与えられたパラメータは、array(numpy用語)を初期化するために、低レベルのメソッドndarray(…)を参照する。 https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html

numpy.ndarray()の第一引数には、サイズをタプルで指定する。そうするとそのサイズの配列が作成される。

下記に、2行3列の2次元配列の例を示す。

np.ndarray((2, 3))

# --------------------

array([[0., 0., 0.],
       [0., 0., 0.]])

下記に、サイズ2×4×3の3次元配列の例を示す。

np.ndarray((2, 4, 3))

# --------------------

array([[[-3.10503618e+231,  1.73060162e-077,  6.91691904e-323],
        [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000],
        [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000],
        [ 0.00000000e+000,  0.00000000e+000,  0.00000000e+000]],

       [[ 0.00000000e+000,  0.00000000e+000, -3.10503618e+231],
        [-3.10503618e+231, -3.10503618e+231, -3.10503618e+231],
        [ 2.96439388e-323,  0.00000000e+000,  0.00000000e+000],
        [ 0.00000000e+000, -3.10503618e+231, -3.10503618e+231]]])

注意:値は初期化されず、ランダムな値になる。(メモリを確保して全く初期化せず、その値を表示している、ように推測される。)したがって実行しても上記と違う値になる可能性がある。

コンストラクタを呼び出すときに、他のリストなどから値を取り込む方法もあるらしい。 しかし、そんなマニアックな方法でnumpy配列を作る人は稀だろうから、ここでは紹介しない。重要なのは、上記の方法は普通は推奨されない方法だ、ということだ。

arrayと書くべきところで、間違ってndarrayを使ってしまった例

arrayを使うべきところでndarrayを書くと、エラーが返ったり変な結果になる。例を示す。

# x = np.array([1, 2, 3]) が正しいのに
x = np.ndarray([1, 2, 3])
x

# --------------------

array([[[0., 0., 0.],
        [0., 0., 0.]]])

はい。普通は使わないコンストラクタのやり方を上記で説明した甲斐がありましたね。 意図せずにnumpy.ndarrayクラスのコンストラクタを呼び出してしまっている。 その結果、サイズが1×2×3の3次元配列ができて、それに不定の値が入ったものが返っている。

# x = np.array(range(100)) が正しいのに
x = np.ndarray(range(100))

# --------------------

---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-12-49769a0574ac> in <module>
          1 # x = np.array(range(100)) が正しいのに
    ----> 2 x = np.ndarray(range(100))
    
    ValueError: maximum supported dimension for an ndarray is 32, found 100

1次元で長さ100の配列を作るつもりでこのように書くと、100次元の配列を作ることになってしまう。 NumPyで作れるarray(numpy用語)は32次元までなので、ValueErrorが発生している。

# x = np.array([1.2, 3.4]) が正しいのに
x = np.ndarray([1.2, 3.4])

# --------------------

---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-13-8dca5cc9ca48> in <module>
          1 # x = np.array([1.2, 3.4]) が正しいのに
    ----> 2 x = np.ndarray([1.2, 3.4])
    
    TypeError: 'float' object cannot be interpreted as an integer

また当然、整数でない値を入れるとエラーになる。長さが小数というNumPy配列は作れないからだ。

ndarrayと書くべきところで、間違ってarrayを使ってしまった例

x = np.array([1, 2, 3])
# isinstance(x, np.ndarray) が正しいのに
isinstance(x, np.array)

# --------------------

---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-14-e2e9a9c75584> in <module>
          2 # isinstance(x, np.ndarray) が正しいのに
          3 
    ----> 4 isinstance(x, np.array)
    
    TypeError: isinstance() arg 2 must be a type or tuple of types

こっちは簡単ですね。isinstanceの2番めの引数はtypeじゃなきゃいけない(のに、typeじゃなくて関数を入れてますよ)、というエラーになる。

参考資料

Python - arrayとndarrayの違い|teratail

python - What is the difference between ndarray and array in numpy? - Stack Overflow

それでは。