Helve’s Python memo

Pythonを使った機械学習やデータ分析の備忘録

NumPyのemptyで空(長さ0)の配列を作る

NumPyのempty関数を用いて、np.empty(0), np.empty([0, 0]), ...とすることで、空の(要素を持たない)任意の次元の配列を作成できる。
本記事では、空の配列の作り方、使い方について簡単に考察する。

目次

環境

Anaconda3 2019.03
Python 3.7.3
NumPy 1.16.2

本記事では、以下の通りライブラリをインポートしていることを前提とする。

import numpy as np

numpy.emptyについて

numpy.emptyは、中身を初期化せずに配列を作成する関数である。
主な引数は次の通り。

numpy.empty(shape, dtype=float)

shapeは配列の形状を決める引数であり、

  • int
  • intを格納するタプル
  • intを格納するリスト

のいずれかをとる。

dtypeは配列の数値の型を決める引数であり、

  • int
  • float
  • np.float32
  • np.int8

などを指定する(デフォルトはfloat)。

素数が5つの1次元配列を生成するには、shapeを5、または(5)、または[5]とする。
ただし、配列の中身は変わる場合がある。

>>> np.empty(5)
array([0.  , 0.25, 0.5 , 0.75, 1.  ])
>>> np.empty((5))
array([0.  , 0.25, 0.5 , 0.75, 1.  ])
>>> np.empty([5])
array([0.  , 0.25, 0.5 , 0.75, 1.  ])

素数が2×3の2次元配列を生成するには、shapeを(2, 3)、または[2, 3]とする。

>>> np.empty((2, 3))
array([[0.93137112, 0.92761264, 0.98531337],
       [0.28813119, 0.49620105, 0.41308828]])
>>> np.empty([2, 3])
array([[0.93137112, 0.92761264, 0.98531337],
       [0.28813119, 0.49620105, 0.41308828]])

numpy.emptyは、中身が全て0の配列を生成するnumpy.zeros()や、
中身が全て1の配列を生成するnumpy.ones()と比較すると、
配列を高速に生成できる利点がある。

空の配列の生成

冒頭に記したように、numpy.emptyのshapeを0や[0, 0], ...とすると、任意の次元の空の配列を生成できる。

1次元配列の場合

>>> a1 = np.empty(0)
>>> a1
array([], dtype=float64)
>>> a1.ndim # 次元数
1

2次元配列の場合

>>> a2 = np.empty([0, 0])
>>> a2
array([], shape=(0, 0), dtype=float64)
>>> a2.ndim # 次元数
2

同様に、

  • np.empty([0, 0, 0])で3次元の空の配列
  • np.empty([0, 0, 0, 0])で4次元の空の配列

がそれぞれ生成できる。

なお、zeros関数やones関数を使って、

  • np.zeros(0)
  • np.ones([0, 0])

としても、同様に空の配列を作成できる。
しかし、関数名と異なり要素に0や1を含まない配列が生成されて紛らわしいため、empty関数を使うべきだと個人的に思う。

空の配列の使い方

空の配列は、「配列を結合したいが、最終的に得られる配列のサイズが分からない場合」、あるいは「計算の規模が小さく、かつプログラムの可読性を重視したい場合」に用いるべきである。

配列を逐次的に結合すると、その都度メモリを確保する必要があるため、実行速度が低下する。
そのため、最終的な配列のサイズが分かっている場合には、初めに必要なサイズの配列を確保して、逐次的に配列を代入した方が速く実行できる。

比較のため、配列を結合する関数stack()と、最初に配列を確保する関数substitute()を作成した。
両者とも、長さが1000で要素が全て1の配列を返す。
関数stack()では長さ1の配列を1000回結合し、
関数substitute()では最初に確保した配列に長さ1の配列を1000回代入している。

def stack():
    a = np.empty(0)
    for i in range(1000):
        a = np.hstack([a, np.array(1)])
    return a
        
def substitute():
    a = np.empty(1000)
    for i in range(1000):
        a[i] = np.array(1)
    return a

IPythonの%timeitマジックコマンドで実行速度を測定する。

>>> %timeit stack()
5.93 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit substitute()
436 µs ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

すなわち、最初に配列を確保したsubstitute()の方が約14倍高速に実行できた。

※Adblockが有効の場合やモバイル版ページでは、シェアボタンをクリックできません