【NumPy入門 np.empty】要素を初期化せずに新しい配列を作る

こんにちは、インストラクターのフクロウです!

この記事では、要素を初期化せずに新しい配列を生成する関数np.emptyとnp.empty_likeを紹介します。

配列の初期化関数はたくさんありますが、この関数は中身を初期化しないため高速であるのが利点です。

numpyを使った高速計算を実現するために、是非とも覚えておきたい関数だと思います!

是非この記事で、np.empty/ empty_likeについて詳しくなってください!

目次

np.empty/np.empty_likeの使い方

※この記事のコードは、jupyter notebookjuputer labを使って書かれています。
コードを試すときは是非これらを使ってみてください。

# コード In [1]:
import numpy as np

import time
import matplotlib.pyplot as plt

np.emptyの使い方

np.emptyは、np.onesやnp.zerosと同じ書き方で使うことができます。

np.empty(配列の形状)でOKです。多重配列の場合は形状をタプルで指定します。

# コード In [2]:
np.empty(10)
# 出力結果 Out [2]:
array([1.05894612e-153, 1.03106417e-259, 2.92552507e-014, 4.79243676e-322,
       4.66247658e-310, 6.94014615e-310, 6.01118976e+175, 3.21142670e-322,
       6.94014615e-310, 6.94014615e-310])

また、第二引数で要素の型を指定する事ができます。

# コード In [3]:
np.empty(10, np.int)
# 出力結果 Out [3]:
array([2318339454602921563,  732219104733978950, 4404652515446898186,
                        97,      94369576516320,     140470121950152,
       7236828713699074921,                  65,     140470121950072,
           140470121950072])

np.empty_likeの使い方

np.empty_likeはnp.ones_likeやnp.zeros_likeのように使えます。

既存の配列と同じサイズの配列を生成するので、引数には何かしらの配列を渡しましょう。

# コード In [4]:
x = np.arange(10)
y = np.empty_like(x)

print(x)
print(y)
# 出力結果 [4]:
[0 1 2 3 4 5 6 7 8 9]
[2318339454602921563  732219104733978950 4404652515446898186
                  97      94369576516320     140470121950152
 7236828713699074921                  65     140470121950072
     140470121950072]
# コード In [5]:
y = np.empty_like(x, np.float32)

print(x)
print(y)
# 出力結果 [5]:
[0 1 2 3 4 5 6 7 8 9]
[0.     0.     0.     1.625  0.     1.75   0.     1.8125 0.     1.875 ]

emptyやempty_likeは配列を生成する際に、要素を初期化しません

なのでたまたまメモリに残っていたデータを使ってしまう場合があり、配列の中身がどんな値になっているかは、作ってみるまでわかりません。

np.empty/np.empty_likeの速度

さて、初期化をしないおかげでこれらの関数はonesやzerosより高速に動作します。

これを確認してみましょう。

# コード In [6]:
x = np.arange(10000)

%timeit np.empty(10000)
%timeit np.zeros(10000)
%timeit np.ones(10000)

%timeit np.empty_like(x)
%timeit np.ones_like(x)
%timeit np.zeros_like(x)
# 出力結果 [6]:
740 ns ± 4.16 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
2.04 µs ± 7.42 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
4.4 µs ± 14.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
395 ns ± 19.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
4.44 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
5.15 µs ± 10.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

少し分かりづらいのでグラフで可視化してみます。

# コード In [7]:
funcs = [np.empty, np.zeros, np.ones, np.empty_like, np.zeros_like, np.ones_like]
funcs_name = ["np.empty", "np.zeros", "np.ones", "np.empty_like", "np.zeros_like", "np.ones_like"]

# 時間の計測
num_samples = 100000
times = []
for f in funcs[:3]:
    t1 = time.time()
    for j in range(num_samples):
        f(num_samples)
    t2 = time.time()
    times.append((t2-t1)/num_samples)

sample_arr = np.arange(num_samples)

for f in funcs[3:]:
    t1 = time.time()
    for j in range(num_samples):
        f(sample_arr)
    t2 = time.time()
    times.append((t2-t1)/num_samples)

# プロット
plt.figure(figsize=(15,5))
plt.bar(funcs_name, times)
plt.xlabel("function")
plt.ylabel("time")
# 出力結果 Out [7]:
Text(0,0.5,'time')

このグラフを見てもわかる通り、新しい配列を生成する関数の中で、empty/empty_likeは圧倒的に速いんです!

なので「0や1などで初期化する必要は無いけど、新しい配列を用意したい場合」には、これらの関数がおすすめです。

まとめ

この記事では、要素を初期化せずに新しい配列を作る関数、np.emptyとnp.empty_likeを紹介しました。

配列の初期化関数には様々な関数がありますが、それぞれが作れる配列には明確な差があるため、使い分けが大切です。

是非それぞれの関数を使いこなせるようになりましょう!

この記事を書いた人

第一言語はPythonです。
皆さんRustやりましょう。

目次