YutaKaのPython教室

Python の文法やライブラリ、API、環境構築について画像・動画・ソースコード付きで徹底解説!

【完全保存版】numpy zeros, ones系列徹底解説 | 初期化した配列を生成【サンプルコード】

numpyには、配列ndarrayを生成するための様々な関数があります。

01で埋め尽くされた配列を生成する関数zeros, ones
その派生形full, emptyについて紹介します。

これらの関数は、配列の初期化に使用することが多い非常に重要な関数です。

配列ndarrayの基本はこちらをチェック!

zeros系統:形状を指定して配列生成

これらの関数は、引数で配列の形状を指定すると、それぞれ

  • zeros()0
  • ones()1
  • full(val)val
  • empty():初期化されていない数値

で埋め尽くされた配列を生成します。

配列の形状shapeは、

  • 一次元配列:整数のリテラル ⇒ 例:12など
  • 二次元以上の配列:整数のタプル ⇒ 例:(2, 2)(2, 2, 3)など

で与えます。

以下では、各関数についてサンプルコード付きで解説します。

zeros(shape) ⇒ 0で初期化

0で初期化された形状shapeの配列を生成します。

import numpy as np
a = np.zeros(5)
# a → array([0., 0., 0., 0., 0.])
A = np.zeros((2, 3))
# A → array([[0., 0., 0.],
#           [0., 0., 0.]])
my_shape = (2, 2)
B = np.zeros(my_shape)
# B → array([[0., 0.],
#            [0., 0.]])

形状の指定は

  • 一次元配列の形状:整数値のリテラルで指定
  • 二次元以上の配列の形状:タプルで指定

という点に注意しましょう。

次のように、二次元以上の配列を生成するときに、整数を連続して渡すとエラーになります。

C = np.zeros(2, 3)
# TypeError: data type not understood

私の場合、特に理由がなければ、zeros()で初期化します。

作成した配列は後ほど、0以外の値に書き換えて使用することが多いと思います。

書き換え忘れがあった場合、0で初期化しておいた方がわずかですが気づきやすいことがあります。例えば、zero divideで気づけたりします。

ones(shape) ⇒ 1で初期化

1で初期化された形状shapeの配列を生成します。

# ones(shape)
a = np.ones(5)
# a → array([1., 1., 1., 1., 1.])
A = np.ones((2, 3))
# A → array([[1., 1., 1.],
#           [1., 1., 1.]])
my_shape = (2, 2)
B = np.ones(my_shape)
# B → array([[1., 1.],
#            [1., 1.]])

形状の指定は

  • 一次元配列の形状:整数値のリテラルで指定
  • 二次元以上の配列の形状:タプルで指定

という点に注意しましょう。

次のように、二次元以上の配列を生成するときに、整数を連続して渡すとエラーになります。

C = np.zeros(2, 3)
# TypeError: data type not understood

full(val, shape) ⇒ valで初期化

1で初期化された形状shapeの配列を生成します。

やりがちなタイプミスですが、fulls()ではなくて、full()なので注意しましょう。

a = np.full(5, 3)
# a → array([3, 3, 3, 3, 3])
A = np.full((2, 3), 0.1)
# A → array([[0.1, 0.1, 0.1],
#            [0.1, 0.1, 0.1]])
my_shape = (2, 2)
B = np.full(my_shape, -2)
# B → array([[-2, -2],
#            [-2, -2]])

ones()で配列を生成して、valをかけても同じ結果になりますが、full()の方が僅かに速いです。

私の実行環境で、マジックコマンド%timeitで比較してみます。

val = 0.1
X = np.ones(3)*val
# X → array([0.1, 0.1, 0.1])
# 2.4 µs ± 21.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Y = np.full(3, val)
# Y → array([0.1, 0.1, 0.1])
# 1.85 µs ± 36.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

少しだけ速いですね…

形状の指定は

  • 一次元配列の形状:整数値のリテラルで指定
  • 二次元以上の配列の形状:タプルで指定

という点に注意しましょう。

次のように、二次元以上の配列を生成するときに、整数を連続して渡すとエラーになります。

C = np.ones(2, 3, 0.5)
# TypeError: data type not understood

empty(shape) ⇒ 初期化なし

empty()は、初期化されていない数値で配列を生成します。

初期値として、そのときたまたまメモリに残っていた値が使用されます。

a = np.empty(3)
# a → array([6.95216240e-310, 1.07193797e-311, 6.95218871e-310])
A = np.empty((2, 2))
# A → array([[4.67296746e-307, 1.69121096e-306],
#            [7.56597770e-307, 1.89146896e-307]])

一見、empty()には使い道がなさそうですが、初期化しない分だけzeros()などより僅かに速いです。

私の実行環境で、マジックコマンド%timeitで比較してみます。

X = np.zeros(3)
# X → array([0., 0., 0.])
# 570 ns ± 17.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Y = np.empty(3)
# Y → array([ 6.95216299e-310,  5.55977121e-312, -0.00000000e+000])
# 555 ns ± 0.951 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

empty()の方が僅かに速いです…。
この僅かな速度差のために初期化忘れの危険があるempty()を使用した方がいいのかは疑問ですが…。

zeros_like系統:既存配列と同じ形状の配列生成

スクリプトを書く際には、zeros()よりもzeros_like()系統で初期化することの方が多いかもしれません。

zeros_like()では、引数に既存の配列を与えると、既存配列と同じ形状の配列を生成します。

初期値はそれぞれ

  • zeros_like()0
  • ones_like ()1
  • full_like (val)val
  • empty_like ():初期化されていない数値

で埋め尽くされた配列を生成します。

同じ形状の配列を複数作成する際には、1つ目をzeros()系統で作成して、2つ以降をzeros_like()系統にすることが多いと思います。

既存配列としては、

  • ndarray
  • リスト
  • タプル

等いわゆるarray_likeを与えることができます。

zeros_like(array) ⇒ 0で初期化

arrayで与えた配列と同じ形状の配列を、0で初期化して作成します。

A = np.zeros((2,2))
# A → array([[0., 0.],
#            [0., 0.]])
B = np.zeros_like(A)
# B → array([[0., 0.],
#            [0., 0.]])

ones_like(array) ⇒ 1で初期化

arrayで与えた配列と同じ形状の配列を、1で初期化して作成します。

A = np.zeros((2,2))
# A → array([[0., 0.],
#            [0., 0.]])
B = np.ones_like(A)
# B → array([[1., 1.],
#            [1., 1.]])

ones_full(array, val) ⇒ valで初期化

arrayで与えた配列と同じ形状の配列を、valで初期化して作成します。

A = np.zeros((2,2))
# A → array([[0., 0.],
#            [0., 0.]])
B = np.full_like(A, 0.5)
# B → array([[0.5, 0.5],
#           [0.5, 0.5]])

empty_like(array) ⇒ 初期化なし

arrayで与えた配列と同じ形状の配列を、初期化せずに生成します。

初期値として、そのときたまたまメモリに残っていた値が使用されます。

A = np.zeros((2,2))
# A → array([[0., 0.],
#            [0., 0.]])
B = np.empty_like(A)
# B → array([[2.23754644e-312, 2.05210913e-307],
#            [3.32653424e-111, 1.13477778e+118]])

【応用】zeros, onesで真偽値配列を生成

生成する配列要素の型をdtypeで指定することができます(デフォルトfloat64)。

dtype=boolとすると、真偽値booleanの配列を作成できます。

TrueFalseから構成される、マスク式の初期化に便利です。

  • zeros(shape, dtype=bool)Falseで初期化
  • ones(shape, dtype=bool)Trueで初期化
mask = np.zeros((2,2), dtype=bool)
# mask → array([[False, False],
#               [False, False]])
mask = np.ones((2,2), dtype=bool)
# mask → array([[ True,  True],
#               [ True,  True]])

おわりに

他にもnumpyの記事を書いていますのでぜひ見てみてください!