こんにちは、インストラクターのフクロウです!
この記事では、要素を初期化せずに新しい配列を生成する関数、np.emptyとnp.empty_likeを紹介します。
配列の初期化関数はたくさんありますが、この関数は中身を初期化しないため高速であるのが利点です。
numpyを使った高速計算を実現するために、是非とも覚えておきたい関数だと思います!
是非この記事で、np.empty/ empty_likeについて詳しくなってください!
np.empty/np.empty_likeの使い方
※この記事のコードは、jupyter notebookやjuputer 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を紹介しました。
配列の初期化関数には様々な関数がありますが、それぞれが作れる配列には明確な差があるため、使い分けが大切です。
是非それぞれの関数を使いこなせるようになりましょう!