NumPy 配列 初期化:知っておきたい全手法まとめ
はじめに:なぜNumPyと配列初期化が重要なのか?
Pythonを使った数値計算、データ分析、機械学習などの分野では、NumPy(Numerical Python)はデファクトスタンダードとなっています。NumPyが提供する多次元配列オブジェクト ndarray
は、Python標準のリストに比べてはるかに高速かつ効率的に動作し、膨大な数値データを扱う上で不可欠なツールです。
NumPy配列を使う最初のステップは、「配列を作成すること」、すなわち「初期化」です。どのような値を持つ配列を、どのような形状(shape)で、どのようなデータ型(dtype)で作成するかは、その後の計算や処理の効率、正確性に直接影響します。用途に応じて最適な初期化手法を選択できることは、NumPyを効果的に使いこなすための基礎であり、非常に重要なスキルと言えます。
この記事では、NumPy配列のさまざまな初期化手法を網羅的に解説します。単にメソッドを紹介するだけでなく、それぞれのメソッドがどのように機能するのか、どのような場合に使うのが適切なのか、パラメータの詳細、知っておくべき注意点などを、豊富なコード例とともに詳細に説明します。この記事を読むことで、あなたはNumPy配列の初期化に関するあらゆる疑問を解消し、自信を持ってNumPyを使い始めることができるようになるでしょう。
それでは、NumPy配列の初期化の世界へ深く潜っていきましょう。
第1章:NumPyの基本と配列初期化の意義
NumPy配列 ndarray
は、同一のデータ型を持つ要素で構成される多次元配列です。これはPythonのリストとは異なり、要素がメモリ上に連続して配置されるため、高速な数値演算が可能になります。また、NumPyはブロードキャスティングやユニバーサル関数(ufunc)といった強力な機能を持ち、要素ごとの操作を効率的に行うことができます。
配列の「初期化」とは、まさにこの ndarray
オブジェクトを生成し、その中に格納する最初の値を設定するプロセスです。初期化の目的は多岐にわたります。
- 計算のためのプレースホルダー: 後で値を代入するための「空箱」として配列を作成する(例: 全要素がゼロの配列)。
- データの格納: ファイルから読み込んだデータや、別の処理で生成したデータをNumPy配列としてラップする。
- 特定のパターンを持つ配列の生成: 等差数列、等比数列、特定の分布に従う乱数などで埋められた配列を作成する。
- 特殊な行列の作成: 単位行列や対角行列など、線形代数でよく使う行列を生成する。
これらの目的に応じて、NumPyは様々な初期化のための関数やメソッドを提供しています。次に、具体的な初期化手法をカテゴリー別に見ていきましょう。
第2章:既存のPythonデータ(リスト、タプル)からの初期化
最も基本的な初期化方法は、PythonのリストやタプルなどのシーケンスからNumPy配列を作成することです。これには numpy.array()
関数を使用します。
“`python
import numpy as np
1次元配列の作成
list_1d = [1, 2, 3, 4, 5]
array_1d = np.array(list_1d)
print(“1次元配列:”)
print(array_1d)
print(“形状:”, array_1d.shape)
print(“データ型:”, array_1d.dtype)
2次元配列の作成 (リストのリスト)
list_2d = [[10, 20, 30], [40, 50, 60]]
array_2d = np.array(list_2d)
print(“\n2次元配列:”)
print(array_2d)
print(“形状:”, array_2d.shape)
print(“データ型:”, array_2d.dtype)
3次元配列の作成 (リストのリストのリスト)
list_3d = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
array_3d = np.array(list_3d)
print(“\n3次元配列:”)
print(array_3d)
print(“形状:”, array_3d.shape)
print(“データ型:”, array_3d.dtype)
タプルからの作成も可能
tuple_data = (100, 200, 300)
array_from_tuple = np.array(tuple_data)
print(“\nタプルからの配列:”)
print(array_from_tuple)
“`
np.array()
関数の主な引数:
object
: 配列に変換したいリスト、タプル、またはその他の配列ライクなオブジェクト。dtype
: 要素のデータ型を指定します。指定しない場合、NumPyが入力データから自動的に推測します。例えば、整数と浮動小数点数が混在している場合、NumPyは自動的に浮動小数点型を選択します。明示的に指定することで、メモリ使用量を抑えたり、意図しない型変換を防いだりできます。copy
:True
に設定すると、入力オブジェクトのデータを必ずコピーして新しい配列を作成します。False
に設定すると、可能な場合はコピーせずに元のデータのビューを返そうとします。デフォルトはTrue
です。order
: 多次元配列のメモリ上での格納順序を指定します。'C'
(C-contiguous、行優先) または'F'
(Fortran-contiguous、列優先) を指定します。デフォルトは'C'
です。特別な理由がない限り、デフォルトの'C'
で問題ありません。subok
:True
に設定すると、入力がndarray
のサブクラスである場合にサブクラスを保持します。デフォルトはFalse
で、ベースクラスであるndarray
を返します。ndmin
: 作成される配列の最小次元数を指定します。入力データが指定した次元数より小さい場合、先頭に次元が追加されます(shapeに1が追加される)。
dtype
の指定例:
“`python
データ型を明示的に指定
data = [1, 2, 3]
array_float = np.array(data, dtype=np.float64) # 64ビット浮動小数点数
array_int32 = np.array(data, dtype=np.int32) # 32ビット整数
print(“\ndtype指定 (float64):”, array_float, array_float.dtype)
print(“dtype指定 (int32):”, array_int32, array_int32.dtype)
文字列型の配列(ただし、数値計算には不向き)
string_data = [‘apple’, ‘banana’, ‘cherry’]
array_string = np.array(string_data)
print(“\n文字列配列:”, array_string, array_string.dtype)
要素の型が混在する場合の自動推測
mixed_data = [1, 2.5, ‘hello’]
array_mixed = np.array(mixed_data) # 文字列型に推測される
print(“\n混在データからの配列:”, array_mixed, array_mixed.dtype)
“`
np.array()
は、既存のPythonシーケンスから配列を作成する最も一般的で直感的な方法です。特に小さなデータセットや、手入力で配列を定義したい場合に適しています。ただし、非常に大きな配列を作成する場合や、特定の値(ゼロや乱数など)で配列を埋めたい場合は、次に説明する専用の初期化関数の方が効率的です。
第3章:特定の値で埋め尽くされた配列の作成
すべての要素が特定の値である配列を作成したい場合、NumPyは便利な関数を提供しています。これらの関数は、必要な形状を指定するだけで、その形状の配列を指定された値で埋めて作成します。
3.1 numpy.zeros()
: 全要素がゼロの配列を作成
np.zeros()
は、指定された形状とデータ型で、すべての要素がゼロ(またはゼロに相当する値)の配列を作成します。これは、計算結果を格納するための初期化された配列や、マスク配列などとしてよく使用されます。
“`python
形状を指定して全ゼロ配列を作成
zeros_1d = np.zeros(5) # 1次元、要素数5
print(“全ゼロ配列 (1D):”, zeros_1d, zeros_1d.dtype) # デフォルトはfloat64
zeros_2d = np.zeros((3, 4)) # 2次元、3行4列
print(“\n全ゼロ配列 (2D):\n”, zeros_2d)
zeros_3d = np.zeros((2, 3, 2)) # 3次元、2ブロック、各ブロック3行2列
print(“\n全ゼロ配列 (3D):\n”, zeros_3d)
データ型を指定
zeros_int = np.zeros((2, 2), dtype=np.int32) # 要素が整数のゼロ
print(“\n全ゼロ配列 (int32):\n”, zeros_int)
zeros_bool = np.zeros(3, dtype=np.bool_) # Falseで埋められる
print(“\n全ゼロ配列 (bool):”, zeros_bool)
“`
np.zeros()
の主な引数:
shape
: 作成する配列の形状を指定します。整数または整数のタプルで指定します。dtype
: 要素のデータ型を指定します。デフォルトはfloat64
です。order
: メモリ上の格納順序 ('C'
または'F'
)。デフォルトは'C'
です。like
: (NumPy 1.20以降)既存のNumPy配列a
を指定すると、a
と同じ形状、データ型、順序の全ゼロ配列を作成します。これはnp.zeros_like(a)
とほぼ同等です。
3.2 numpy.ones()
: 全要素がイチの配列を作成
np.ones()
は np.zeros()
と同様に、指定された形状とデータ型で配列を作成しますが、すべての要素がイチ(またはイチに相当する値)になります。これは、初期値がイチである計算や、重み配列などで使われることがあります。
“`python
全イチ配列を作成
ones_1d = np.ones(4)
print(“全イチ配列 (1D):”, ones_1d, ones_1d.dtype) # デフォルトはfloat64
ones_2d = np.ones((2, 5), dtype=np.int16) # 整数型で作成
print(“\n全イチ配列 (2D, int16):\n”, ones_2d)
“`
np.ones()
の引数は np.zeros()
とほぼ同じです (shape
, dtype
, order
, like
)。
3.3 numpy.full()
: 全要素が指定した値の配列を作成
np.full()
は、np.zeros()
や np.ones()
のより汎用的なバージョンです。指定した形状とデータ型で、すべての要素が任意の値 fill_value
の配列を作成できます。
“`python
全要素が特定の値の配列を作成
full_array = np.full((3, 3), 7) # 3×3の配列を値7で埋める
print(“全要素が7の配列:\n”, full_array) # デフォルトは整数型 (fill_valueが整数の場合)
full_array_float = np.full((2, 4), 3.14) # 値3.14で埋める
print(“\n全要素が3.14の配列:\n”, full_array_float) # デフォルトは浮動小数点型
full_array_string = np.full((2, 2), “hello”, dtype=object) # 文字列で埋める (dtype=objectが必要な場合あり)
print(“\n全要素が’hello’の配列:\n”, full_array_string)
“`
np.full()
の主な引数:
shape
: 作成する配列の形状。fill_value
: 配列を埋める値。dtype
: 要素のデータ型。指定しない場合、fill_value
から推測されます。order
: メモリ上の格納順序。like
: 既存の配列を基に形状などを決定。
これらの zeros
, ones
, full
関数は、固定値で埋められた配列を効率的に作成する際に非常に便利です。特に大きな配列を扱う場合、Pythonのリスト内包表記で同様の配列を作成するよりも、これらのNumPy関数を使う方がはるかに高速です。
第4章:シーケンス(等差数列、等比数列など)の生成
規則的なパターンを持つ数値シーケンスの配列を作成することもよくあります。NumPyはこのような用途のために arange
, linspace
, logspace
といった関数を提供しています。
4.1 numpy.arange()
: 等差数列(指定した間隔)
np.arange()
は、Pythonの組み込み関数 range()
に似ていますが、NumPy配列を返します。指定した開始値、停止値、ステップサイズで等差数列を作成できます。
“`python
0から9までの整数配列 (停止値は含まれない)
arange_1 = np.arange(10)
print(“arange(10):”, arange_1)
1から10まで、ステップ2の配列
arange_2 = np.arange(1, 11, 2)
print(“arange(1, 11, 2):”, arange_2)
浮動小数点数のステップ
arange_3 = np.arange(0.0, 1.0, 0.1)
print(“arange(0.0, 1.0, 0.1):”, arange_3)
負のステップ
arange_4 = np.arange(10, 0, -1)
print(“arange(10, 0, -1):”, arange_4)
“`
np.arange()
の主な引数:
start
: シーケンスの開始値(デフォルトは 0)。stop
: シーケンスの停止値(この値は含まれません)。必須step
: 値の間隔(デフォルトは 1)。dtype
: 出力配列のデータ型。指定しない場合、入力引数から推測されます。浮動小数点数の引数がある場合、デフォルトは浮動小数点型になります。
注意点: np.arange()
を浮動小数点数で使う場合、浮動小数点数の精度問題により、期待通りの結果が得られないことがあります。例えば、np.arange(0.1, 0.3, 0.1)
は [0.1, 0.2]
となることを期待するかもしれませんが、内部表現の誤差により [0.1, 0.2, 0.3]
となる可能性や、逆に [0.1, 0.2]
となる可能性、あるいは予期しない微小な値を含む可能性があります。固定数の要素を持つ等間隔のシーケンスが必要な場合は、次に説明する np.linspace()
の方が適しています。
4.2 numpy.linspace()
: 等間隔の点列(指定した要素数)
np.linspace()
は、指定した開始値と停止値の間に、指定した数の等間隔の点を生成します。これは、グラフを描画するためのx軸のデータポイント生成や、特定の区間を等分割してサンプリングする際によく使用されます。
“`python
0から1までを10個の等間隔な点に分割 (停止値を含む)
linspace_1 = np.linspace(0, 1, 10)
print(“linspace(0, 1, 10):”, linspace_1)
10から20までを5個の等間隔な点に分割
linspace_2 = np.linspace(10, 20, 5)
print(“\nlinspace(10, 20, 5):”, linspace_2)
停止値を含めない場合 (endpoint=False)
linspace_no_endpoint = np.linspace(0, 1, 10, endpoint=False)
print(“\nlinspace(0, 1, 10, endpoint=False):”, linspace_no_endpoint)
“`
np.linspace()
の主な引数:
start
: シーケンスの開始値。stop
: シーケンスの停止値。デフォルトでは 含まれます。num
: 生成する要素の数。デフォルトは 50。必須ではないが、通常は指定するendpoint
:True
の場合、stop
の値をシーケンスに含めます。False
の場合、含めません。デフォルトはTrue
。retstep
:True
に設定すると、生成された配列と共にステップサイズが返されます。dtype
: 出力配列のデータ型。
np.linspace()
は、要素数を正確に制御したい場合に np.arange()
より優れています。特に浮動小数点数の区間を扱う際には、精度問題の心配がない np.linspace()
を使うのが一般的です。
4.3 numpy.logspace()
: 対数スケールでの等間隔な点列
np.logspace()
は、np.linspace()
に似ていますが、対数スケールで等間隔な点を生成します。開始値と停止値は、指定した基数(base)の指数として解釈されます。これは、対数グラフや周波数帯域などを扱う際によく使用されます。
“`python
10^0 から 10^2 (つまり 1 から 100) までを5個の対数的に等間隔な点に分割 (基数10)
logspace_1 = np.logspace(0, 2, 5)
print(“logspace(0, 2, 5):”, logspace_1)
2^1 から 2^4 (つまり 2 から 16) までを4個の対数的に等間隔な点に分割 (基数2)
logspace_2 = np.logspace(1, 4, 4, base=2.0)
print(“\nlogspace(1, 4, 4, base=2.0):”, logspace_2)
“`
np.logspace()
の主な引数:
start
:base**start
がシーケンスの開始値。stop
:base**stop
がシーケンスの停止値。デフォルトでは 含まれます。num
: 生成する要素の数。デフォルトは 50。endpoint
:True
の場合、base**stop
の値をシーケンスに含めます。デフォルトはTrue
。base
: 対数の基数。デフォルトは 10.0。dtype
: 出力配列のデータ型。
np.logspace()
は、線形スケールではなく対数スケールでの変化を表現したい場合に非常に役立ちます。
第5章:ランダムな値を持つ配列の作成
シミュレーションや統計モデリング、ニューラルネットワークの重み初期化など、ランダムな値を持つ配列が必要となる場面は非常に多いです。NumPyの numpy.random
モジュールは、様々な種類の乱数を生成するための包括的な機能を提供しています。
NumPy 1.17以降では、numpy.random.Generator
オブジェクトを使用する新しい方法が推奨されています。以前のモジュールレベル関数 (np.random.rand
, np.random.randn
など) も引き続き利用可能ですが、新しい方法の方が柔軟性が高く、複数の独立した乱数ストリームを管理しやすいという利点があります。ここではまず新しい方法を中心に説明し、その後従来のモジュールレベル関数についても触れます。
5.1 新しいランダムモジュール (numpy.random.default_rng()
)
numpy.random.default_rng()
を呼び出すことで、乱数生成器(Generator)のインスタンスを作成します。このインスタンスを通じて様々な乱数生成メソッドを呼び出します。
“`python
乱数生成器のインスタンスを作成
rng = np.random.default_rng()
0.0から1.0までの一様乱数を生成 (形状指定)
random_uniform = rng.random(size=(3, 3))
print(“一様乱数 (Generator):\n”, random_uniform)
標準正規分布 (平均0、標準偏差1) に従う乱数を生成
random_normal = rng.standard_normal(size=(2, 4))
print(“\n標準正規分布乱数 (Generator):\n”, random_normal)
指定範囲の整数乱数を生成 (low <= x < high)
random_integers = rng.integers(low=0, high=10, size=(2, 5)) # 0から9までの整数
print(“\n整数乱数 (Generator, 0-9):\n”, random_integers)
他の様々な分布からの乱数生成メソッドも利用可能
例: 二項分布
random_binomial = rng.binomial(n=10, p=0.5, size=5)
print(“\n二項分布乱数 (Generator):”, random_binomial)
例: 正規分布 (平均と標準偏差を指定)
random_custom_normal = rng.normal(loc=10.0, scale=2.0, size=(3, 3)) # 平均10.0, 標準偏差2.0
print(“\n正規分布乱数 (Generator, 平均10, 標準偏差2):\n”, random_custom_normal)
“`
Generator
オブジェクトで利用できる主な乱数生成メソッド:
random(size=None)
: 0.0以上1.0未満の一様分布に従う浮動小数点数。size
で形状を指定。integers(low, high=None, size=None, dtype=np.int64)
:low
以上high
未満(high
が指定された場合)、または0以上low
未満(high
がNoneの場合)のランダムな整数。high
は含まれないことに注意。standard_normal(size=None)
: 標準正規分布(平均0、標準偏差1)に従う浮動小数点数。normal(loc=0.0, scale=1.0, size=None)
: 正規分布(平均loc
、標準偏差scale
)に従う浮動小数点数。uniform(low=0.0, high=1.0, size=None)
:low
以上high
未満の一様分布に従う浮動小数点数。- 他にも、
binomial
,poisson
,gamma
,beta
,exponential
など、多数の確率分布からの乱数生成メソッドがあります。各メソッドの引数はその分布に依存します。
5.2 乱数の再現性(Seed)
乱数生成は実際には「擬似乱数」であり、内部状態に基づいて生成されます。この初期状態を「シード(seed)」と呼びます。同じシードを使えば、毎回全く同じ乱数シーケンスを生成することができます。これは、実験結果の再現性を確保するために非常に重要です。
新しい Generator
システムでは、default_rng()
を呼び出す際にシードを指定します。
“`python
シードを指定して乱数生成器を作成
rng1 = np.random.default_rng(123)
rng2 = np.random.default_rng(123) # 同じシード
それぞれの生成器で乱数を生成
random_a = rng1.random(3)
random_b = rng2.random(3)
print(“\nシード123からの乱数 (Generator A):”, random_a)
print(“シード123からの乱数 (Generator B):”, random_b) # Aと同じになる
別のシード
rng3 = np.random.default_rng(456)
random_c = rng3.random(3)
print(“シード456からの乱数 (Generator C):”, random_c) # 上記とは異なる
“`
実験コードなど、結果を再現可能にしたい場合は、必ずシードを指定して Generator
を作成しましょう。
5.3 従来のモジュールレベル関数
np.random.default_rng()
が導入される前から存在した、np.random
モジュール直下で呼び出す関数群です。これらは現在でも広く使われていますが、内部で単一のグローバルな乱数生成器を使用しているため、複数の箇所で独立した乱数が必要な場合などに注意が必要です。シードの設定は np.random.seed()
関数で行います。
“`python
0.0から1.0までの一様乱数 (形状を引数で指定)
rand_array = np.random.rand(3, 3) # np.random.rand(d0, d1, …, dn) のように形状をバラバラの引数で指定
print(“\n一様乱数 (従来のrand):\n”, rand_array)
標準正規分布 (平均0、標準偏差1) に従う乱数 (形状を引数で指定)
randn_array = np.random.randn(2, 4) # np.random.randn(d0, d1, …, dn)
print(“\n標準正規分布乱数 (従来のrandn):\n”, randn_array)
指定範囲の整数乱数 (low <= x <= high)
注意: randintのhighの扱いがGenerator版と異なる (highを含むか含まないか)
randint_array = np.random.randint(low=0, high=10, size=(2, 5)) # low <= x < high
print(“\n整数乱数 (従来のrandint, 0-9):\n”, randint_array)
シードの設定 (グローバルな生成器に影響)
np.random.seed(123)
random_d = np.random.random(3) # np.random.random() は size=(…) を引数に取る
print(“\nシード123からの乱数 (従来のrandom, 1回目):”, random_d)
np.random.seed(123) # もう一度同じシードを設定
random_e = np.random.random(3)
print(“シード123からの乱数 (従来のrandom, 2回目):”, random_e) # Dと同じになる
np.random.seed(456)
random_f = np.random.random(3)
print(“シード456からの乱数 (従来のrandom, 3回目):”, random_f) # 上記とは異なる
“`
従来の関数を使用する際の注意点:
* np.random.rand()
と np.random.randn()
は、形状をタプルではなく個別の引数で指定します (np.random.rand(2, 3)
はOK, np.random.rand((2, 3))
はNG)。他のほとんどのNumPy関数では形状はタプルで指定するため、混同しやすいです。
* np.random.randint(low, high, size)
は low
以上 high
未満の範囲です。古いバージョンでは high
を含むことがありましたが、現在は high
は含まれません(Generator.integers
と同じ)。
* np.random.seed()
はグローバルな状態を変更するため、コードの異なる部分で乱数を使う場合に意図しない影響を与える可能性があります。複数の独立したプロセスやスレッドで乱数が必要な場合は、新しい Generator
システムを使うのが安全です。
特別な理由がなければ、NumPy 1.17以降を使用している場合は numpy.random.default_rng()
を使うことを推奨します。
第6章:未初期化配列の作成:numpy.empty()
numpy.empty()
は、指定された形状とデータ型を持つ新しい配列を作成しますが、その要素は初期化されません。つまり、その時点のメモリにある「ゴミ」の値がそのまま含まれます。
“`python
未初期化配列を作成
empty_array = np.empty((3, 3), dtype=np.float64)
print(“未初期化配列 (empty):\n”, empty_array) # 値は実行ごとに変わる可能性がある (メモリ上のゴミ)
empty_int_array = np.empty(5, dtype=np.int32)
print(“\n未初期化配列 (empty, int32):”, empty_int_array) # 整数型のゴミ
“`
np.empty()
の主な引数:
shape
: 作成する配列の形状。dtype
: 要素のデータ型。order
: メモリ上の格納順序。like
: 既存の配列を基に形状などを決定。
なぜ初期化されない配列が必要なのでしょうか?それはパフォーマンスのためです。np.zeros()
や np.ones()
は、配列の全要素を特定の値で埋めるために追加の処理時間を要します。一方、np.empty()
は配列に必要なメモリ領域を確保するだけで、その領域に何が入っているかは気にしません。
したがって、np.empty()
は、配列を作成した後すぐにすべての要素を上書きすることがわかっている場合に最も役立ちます。例えば、関数内で計算結果を格納するためのテンポラリな配列として使用し、計算が終わればすべての要素に値が代入される、といったケースです。
“`python
np.empty() を使った効率的な配列作成と上書き
result_array = np.empty((1000, 1000)) # 大きな配列を未初期化で作成
ここで result_array の各要素を計算結果で埋める処理を行う (省略)
例として、単純な値を代入
for i in range(1000):
for j in range(1000):
result_array[i, j] = i + j
print(“\n計算結果で埋められた配列 (emptyを利用):\n”, result_array[:5, :5]) # 結果は正しい
“`
もし result_array = np.zeros((1000, 1000))
とした場合、まず配列全体をゼロで埋める処理が実行され、その後で計算結果による上書きが行われます。これは冗長であり、特に大きな配列では無視できないオーバーヘッドとなります。np.empty()
を使えば、最初のゼロ埋め処理をスキップできます。
重要: np.empty()
で作成した配列の要素には絶対に未初期化のままアクセスしないでください。含まれている値は不定であり、実行するたびに異なる可能性があります。デバッグが非常に困難になるだけでなく、セキュリティ上の問題を引き起こす可能性もゼロではありません。np.empty()
を使うのは、その直後に配列のすべての要素を有効な値で上書きすることが保証されている場合に限るべきです。
第7章:既存の配列を基にした初期化 (_like
系関数)
すでに存在するNumPy配列と同じ形状やデータ型を持つ新しい配列を作成したい場合があります。このようなシナリオのために、NumPyは既存の配列を「テンプレート」として利用する便利な関数を提供しています。これらの関数は通常、元の配列名に _like
を付けた名前になっています。
7.1 numpy.zeros_like()
: 元の配列と同じ形状・データ型の全ゼロ配列
“`python
元となる配列
arr = np.arange(12).reshape(3, 4)
print(“元の配列 (arr):\n”, arr)
arrと同じ形状・データ型の全ゼロ配列を作成
zeros_like_arr = np.zeros_like(arr)
print(“\nzeros_like(arr):\n”, zeros_like_arr)
print(“形状:”, zeros_like_arr.shape)
print(“データ型:”, zeros_like_arr.dtype)
float型の元の配列を基にint型のゼロ配列を作成 (dtypeを指定)
arr_float = np.array([[1.1, 2.2], [3.3, 4.4]])
zeros_like_float_int = np.zeros_like(arr_float, dtype=np.int32)
print(“\narr_float:\n”, arr_float)
print(“zeros_like(arr_float, dtype=int32):\n”, zeros_like_float_int)
print(“形状:”, zeros_like_float_int.shape)
print(“データ型:”, zeros_like_float_int.dtype)
“`
np.zeros_like(a, dtype=None, order='K', subok=True, shape=None)
は、配列 a
を基に、a
と同じ形状とデータ型(dtype
を指定しない場合)、同じ順序(order
を指定しない場合)の全ゼロ配列を作成します。dtype
, order
, shape
を明示的に指定することも可能です。shape
を指定すると、元の配列 a
の形状ではなく指定した形状が優先されますが、dtype
や order
は引き続き a
から引き継がれます(これは少し直感的でないかもしれません。通常は shape
を指定しないか、np.zeros
を使う方が自然です)。
7.2 numpy.ones_like()
: 元の配列と同じ形状・データ型の全イチ配列
“`python
元の配列 (arrを使用)
arr = np.arange(12).reshape(3, 4)
arrと同じ形状・データ型の全イチ配列を作成
ones_like_arr = np.ones_like(arr)
print(“\nones_like(arr):\n”, ones_like_arr)
print(“形状:”, ones_like_arr.shape)
print(“データ型:”, ones_like_arr.dtype)
“`
np.ones_like(a, dtype=None, order='K', subok=True, shape=None)
は np.zeros_like
と同様に、配列 a
を基に、a
と同じ形状、データ型、順序の全イチ配列を作成します。
7.3 numpy.full_like()
: 元の配列と同じ形状・データ型の特定値配列
“`python
元の配列 (arrを使用)
arr = np.arange(12).reshape(3, 4)
arrと同じ形状・データ型で、値99で埋められた配列を作成
full_like_arr = np.full_like(arr, 99)
print(“\nfull_like(arr, 99):\n”, full_like_arr)
print(“形状:”, full_like_arr.shape)
print(“データ型:”, full_like_arr.dtype)
arr_floatを基に、値3.14で埋められた整数型配列を作成
full_like_float_int = np.full_like(arr_float, 3.14, dtype=np.int32) # 3.14はintにキャストされる (3)
print(“\nfull_like(arr_float, 3.14, dtype=int32):\n”, full_like_float_int)
print(“形状:”, full_like_float_int.shape)
print(“データ型:”, full_like_float_int.dtype)
“`
np.full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None)
は np.zeros_like
や np.ones_like
と同様ですが、指定した fill_value
で配列を埋めます。fill_value
は単一の値である必要があります。
7.4 numpy.empty_like()
: 元の配列と同じ形状・データ型の未初期化配列
“`python
元の配列 (arrを使用)
arr = np.arange(12).reshape(3, 4)
arrと同じ形状・データ型の未初期化配列を作成
empty_like_arr = np.empty_like(arr)
print(“\nempty_like(arr):\n”, empty_like_arr) # 値はゴミ
print(“形状:”, empty_like_arr.shape)
print(“データ型:”, empty_like_arr.dtype)
“`
np.empty_like(a, dtype=None, order='K', subok=True, shape=None)
は np.empty
と同様に未初期化配列を作成しますが、形状、データ型、順序は元の配列 a
に従います。これも、元の配列と同じ形状で結果を格納する一時配列として非常に効率的です。
_like
系関数は、既存の配列に対する操作結果を格納する新しい配列を生成する際に、元の配列のメタデータ(形状やデータ型)を再指定する手間を省くことができるため便利です。特に、関数内で受け取った配列と同じ形状の配列を返す場合などに重宝します。
第8章:特殊行列(単位行列、対角行列)の生成
線形代数で頻繁に使用される特殊な構造を持つ行列(2次元配列)を作成するための関数もNumPyは提供しています。
8.1 numpy.eye()
: 単位行列または対角要素が1の行列を作成
np.eye()
は、対角要素が1で、それ以外の要素が0の2次元配列(行列)を作成します。正方行列である必要はありません。
“`python
3×3の単位行列
identity_3x3 = np.eye(3)
print(“3×3 単位行列 (eye(3)):\n”, identity_3x3)
4×5で、対角要素が1の行列
eye_4x5 = np.eye(4, 5)
print(“\n4x5 eye(4, 5):\n”, eye_4x5)
対角要素をオフセット (k=1: 主対角より1つ上)
eye_offset = np.eye(4, k=1)
print(“\n4x4 eye(4, k=1):\n”, eye_offset)
対角要素をオフセット (k=-1: 主対角より1つ下)
eye_neg_offset = np.eye(4, k=-1)
print(“\n4x4 eye(4, k=-1):\n”, eye_neg_offset)
データ型を指定
eye_int = np.eye(3, dtype=np.int32)
print(“\n3x3 単位行列 (int32):\n”, eye_int)
“`
np.eye()
の主な引数:
N
: 行列の行数。M
: 行列の列数。指定しない場合、M
はN
と同じになり正方行列になります。k
: 対角線を指定します。k=0
(デフォルト) は主対角、k > 0
は主対角より上の対角線、k < 0
は主対角より下の対角線です。dtype
: 要素のデータ型。デフォルトはfloat64
です。order
: メモリ上の格納順序。
8.2 numpy.identity()
: 正方単位行列を作成
np.identity()
は、正方行列に限定された単位行列を作成します。機能的には np.eye(N, N)
と同じです。
“`python
4×4の正方単位行列
identity_4x4 = np.identity(4)
print(“4×4 正方単位行列 (identity(4)):\n”, identity_4x4)
“`
np.identity()
の主な引数:
n
: 行列の行数および列数(正方行列)。dtype
: 要素のデータ型。order
: メモリ上の格納順序。
np.identity(n)
は np.eye(n, k=0)
と等価ですが、単位行列を作成するという意図がより明確になります。
8.3 numpy.diag()
: 対角行列の作成または対角要素の抽出
np.diag()
は少し特殊で、1次元配列を与えるとそれを対角要素とする2次元の対角行列を作成し、2次元配列を与えるとそその対角要素を抽出して1次元配列として返します。ここでは初期化(作成)の側面を説明します。
“`python
1次元配列
diag_elements = np.array([1, 2, 3, 4])
1次元配列を対角要素とする対角行列を作成
diag_matrix = np.diag(diag_elements)
print(“diag([1, 2, 3, 4]) から作成した対角行列:\n”, diag_matrix)
対角線をオフセット (k=1)
diag_offset_matrix = np.diag(diag_elements, k=1)
print(“\ndiag([1, 2, 3, 4], k=1) から作成した行列:\n”, diag_offset_matrix)
対角線をオフセット (k=-2)
diag_neg_offset_matrix = np.diag(diag_elements, k=-2)
print(“\ndiag([1, 2, 3, 4], k=-2) から作成した行列:\n”, diag_neg_offset_matrix)
“`
np.diag()
(1次元配列を入力として2次元配列を返す場合) の主な引数:
v
: 対角要素として使用する1次元配列。k
: 対角線を指定します。k=0
(デフォルト) は主対角、k > 0
は主対角より上の対角線、k < 0
は主対角より下の対角線です。
np.diag()
は、特定のベクトルを対角要素とする行列を効率的に作成したい場合に便利です。
第9章:numpy.meshgrid()
: グリッドポイントの生成
np.meshgrid()
は、複数の1次元配列から、それらの要素のすべての組み合わせを網羅するような多次元配列の「グリッド」を作成します。これは、2次元または3次元の関数を評価したり、等高線グラフや3Dプロットを作成したりする際によく使用されます。厳密には「初期化」とは少し異なりますが、特定のパターンを持つ多次元配列を作成する手法として関連性が高いため、ここで紹介します。
“`python
2つの1次元配列 (x軸とy軸のデータポイント)
x = np.array([1, 2, 3])
y = np.array([10, 20])
meshgridを作成
X, Y = np.meshgrid(x, y)
print(“x:”, x)
print(“y:”, y)
print(“\nX (meshgrid出力, x座標):\n”, X)
print(“\nY (meshgrid出力, y座標):\n”, Y)
これらのグリッドを使って関数を評価する例 (例: Z = X + Y)
Z = X + Y
print(“\nZ = X + Y:\n”, Z)
“`
この例では、x
の要素 [1, 2, 3]
と y
の要素 [10, 20]
のすべての組み合わせ (1, 10), (2, 10), (3, 10), (1, 20), (2, 20), (3, 20)
に対応する点がグリッドとして生成されています。X
配列は各点のx座標、Y
配列は各点のy座標を示します。
np.meshgrid()
の主な引数:
*xi
: 座標を表す1つ以上の1次元配列。copy
:True
(デフォルト) の場合、コピーを返します。False
の場合、可能な限りビューを返します(メモリ効率が良い)。indexing
: グリッドのインデックス順序を指定します。'xy'
(デフォルト) はデカルト座標のインデックス順序に対応し、出力配列の最初の次元はy座標、2番目の次元はx座標に対応します(形状は(len(y), len(x))
)。'ij'
は行列のインデックス順序に対応し、最初の次元は1番目の入力配列、2番目の次元は2番目の入力配列に対応します(形状は(len(x), len(y))
)。多くのNumPy操作と互換性が高いのは'ij'
ですが、matplotlibのプロット関数は'xy'
を想定していることが多いです。
np.meshgrid()
は、特に科学技術計算やデータ可視化において、多次元データの基盤となる座標グリッドを効率的に準備するために強力なツールです。
第10章:各初期化手法の選択ガイド
これまでに見てきたように、NumPy配列の初期化には様々な方法があります。どの方法を選ぶべきかは、作成したい配列の特性や目的によって異なります。以下に、選択のための簡単なガイドを示します。
目的 | 推奨される関数 | 特徴と使い分け |
---|---|---|
既存のPythonシーケンスから作成 | np.array() |
リストやタプルなどのデータをNumPy配列に変換。最も基本的な方法。dtype を明示することが多い。 |
全要素がゼロの配列を作成 | np.zeros() |
計算結果の初期値やプレースホルダー。デフォルトは float64 。 |
全要素がイチの配列を作成 | np.ones() |
重み配列やカウントなど。デフォルトは float64 。 |
全要素が特定の値の配列を作成 | np.full() |
ゼロやイチ以外の任意の値で埋める。zeros , ones より汎用的。 |
等差数列を作成 (ステップ指定) | np.arange() |
開始、停止、ステップを指定。整数ステップに強い。浮動小数点数では精度に注意。 |
等間隔の点列を作成 (要素数指定) | np.linspace() |
開始、停止、要素数を指定。浮動小数点数の区間分割に強い。endpoint 引数に注意。 |
対数スケールで等間隔の点列を作成 | np.logspace() |
対数軸での点列生成。base (基数) を指定。 |
ランダムな値を持つ配列を作成 | np.random.default_rng()... |
様々な分布の乱数を生成。新しいシステム (Generator ) が推奨される。再現性にはシード指定が必須。 |
未初期化配列を作成 | np.empty() |
高速だが危険。作成後すぐに全要素を上書きする場合のみ使用。メモリ上のゴミを含む。 |
既存配列と同じ形状・データ型でゼロ | np.zeros_like() |
既存配列をテンプレートに。np.zeros(a.shape, a.dtype) とほぼ同じだがより簡潔。 |
既存配列と同じ形状・データ型でイチ | np.ones_like() |
既存配列をテンプレートに。np.ones(a.shape, a.dtype) とほぼ同じだがより簡潔。 |
既存配列と同じ形状・データ型で特定値 | np.full_like() |
既存配列をテンプレートに。np.full(a.shape, fill_value, a.dtype) とほぼ同じだがより簡潔。 |
既存配列と同じ形状・データ型で未初期化 | np.empty_like() |
既存配列をテンプレートに。np.empty(a.shape, a.dtype) とほぼ同じだがより簡潔、かつ高速だが危険。 |
単位行列を作成 | np.eye() / np.identity() |
線形代数で頻繁に使用。eye は非正方行列、identity は正方行列。k でオフセット対角を指定。 |
対角行列を作成 (1Dから2Dへ) | np.diag() |
1次元配列を主対角要素とする行列を作成。要素抽出にも使われる。 |
グリッドポイントを作成 | np.meshgrid() |
複数の1次元配列の要素の組み合わせで多次元グリッドを作成。プロットや関数評価に便利。インデックス順序に注意。 |
このガイドを参考に、あなたのプログラムの目的やデータの性質に最も適した初期化手法を選択してください。
第11章:初期化時のdtypeとshape、その他の考慮事項
配列の初期化において、形状(shape)とデータ型(dtype)は最も基本的な属性です。これらの属性を適切に設定することは、メモリ効率、計算速度、そして結果の正確性に直接影響します。
11.1 データ型(dtype)
NumPyは様々なデータ型をサポートしています。初期化関数で dtype
引数を指定しない場合、NumPyは入力データやデフォルト(多くの場合 float64
)に基づいてデータ型を推測します。しかし、明示的に指定することで、メモリ使用量を最適化したり、期待する精度の計算を行ったりすることが重要です。
よく使われるNumPyのデータ型:
- 整数型:
int8
,int16
,int32
,int64
(ビット数で精度と範囲が異なる),uint8
,uint16
,uint32
,uint64
(符号なし整数) - 浮動小数点型:
float16
,float32
,float64
(標準),float128
(拡張精度、ただしシステム依存) - 複素数型:
complex64
,complex128
(標準),complex256
- 真偽値型:
bool_
(True/False) - 文字列型:
bytes_
,unicode_
(固定長。文字列の長さはdtypeの一部に含まれる) - オブジェクト型:
object
(Pythonオブジェクトをそのまま格納。NumPyの恩恵が受けにくい)
例:
“`python
データ型を明示的に指定
int8_array = np.zeros(10, dtype=np.int8)
float32_array = np.ones((2, 2), dtype=np.float32)
bool_array = np.full(5, True, dtype=np.bool_)
complex_array = np.full(3, 1 + 2j, dtype=np.complex128)
print(“int8 dtype:”, int8_array.dtype)
print(“float32 dtype:”, float32_array.dtype)
print(“bool_ dtype:”, bool_array.dtype)
print(“complex128 dtype:”, complex_array.dtype)
dtypeの短縮形
int_array = np.zeros(3, dtype=’i4′) # int32
float_array = np.ones(3, dtype=’f8′) # float64
print(“int32 (short form) dtype:”, int_array.dtype)
print(“float64 (short form) dtype:”, float_array.dtype)
“`
データ型の選択は、メモリ使用量に大きく影響します。例えば、0から255までの整数しか扱わないことが分かっている場合は、int64
(8バイト) ではなく uint8
(1バイト) を選択することで、メモリ使用量を1/8に削減できます。適切なデータ型を選ぶことは、特に大規模データを扱う上で重要です。
11.2 形状(shape)
形状は、配列の各次元の要素数を示すタプルです。1次元配列なら (N,)
、2次元配列なら (N, M)
、3次元配列なら (N, M, P)
のように指定します。NumPyの多くの初期化関数では、形状を引数として渡します。
“`python
形状の指定例
shape_1d = (5,)
array_1d = np.empty(shape_1d)
print(“形状 (1D):”, array_1d.shape)
shape_2d = (3, 4)
array_2d = np.zeros(shape_2d)
print(“形状 (2D):”, array_2d.shape)
shape_3d = (2, 3, 5)
array_3d = np.ones(shape_3d)
print(“形状 (3D):”, array_3d.shape)
形状の指定が引数としてバラバラな rand/randn と、タプルで指定する他の関数の違いを再確認
print(np.random.rand((2, 3)).shape) # これはエラーになる
print(np.random.rand(2, 3).shape) # OK, (2, 3)
print(np.zeros((2, 3)).shape) # OK, (2, 3)
“`
形状は配列の構造を定義するものであり、計算や操作を行う際に非常に重要です。形状が一致しない配列間での演算は、ブロードキャスティングのルールに従うか、エラーとなります。
11.3 メモリ順序(order)
多次元配列の要素が、コンピュータのメモリ上でどのように並んでいるかを指定するのが order
引数です。主なオプションは 'C'
(C-contiguous、行優先) と 'F'
(Fortran-contiguous、列優先) です。
'C'
: 行ごとに要素が連続して配置されます。C言語の多次元配列と同じ順序です。NumPyのデフォルトです。'F'
: 列ごとに要素が連続して配置されます。Fortranの多次元配列と同じ順序です。
ほとんどの場合、デフォルトの 'C'
順序で問題ありません。しかし、特定のライブラリ(例えばSciPyの一部やFortranで書かれたコードと連携する場合)がFortran順序を期待している場合や、列方向のアクセスが支配的な処理を行う場合に 'F'
を指定することがあります。性能に影響を与える可能性のある上級者向けのパラメータです。
“`python
orderを指定した配列作成
c_ordered_array = np.zeros((3, 3), order=’C’)
f_ordered_array = np.zeros((3, 3), order=’F’)
print(“C-ordered array flags:\n”, c_ordered_array.flags)
print(“\nF-ordered array flags:\n”, f_ordered_array.flags)
“`
.flags
属性を見ると、C_CONTIGUOUS
や F_CONTIGUOUS
が True
になっていることが確認できます。
11.4 その他の高度な初期化手法(簡単な紹介)
NumPyには他にも配列を作成する関数が存在しますが、上記で説明した手法ほど一般的ではないかもしれません。簡単に触れておきます。
-
numpy.fromfunction(function, shape, *, dtype=None)
: 関数function(i, j, ...)
を指定された形状shape
の各座標(i, j, ...)
で評価し、その結果を要素とする配列を作成します。これは、要素の値がその位置(インデックス)のみに依存する配列を作成するのに便利です。“`python
def element_value(i, j):
return i + jfrom_func_array = np.fromfunction(element_value, (3, 3), dtype=int)
print(“\nfromfunction 例:\n”, from_func_array)
“` -
numpy.fromfile(file, dtype=float, count=-1, sep='')
: ファイル(バイナリまたはテキスト)からデータを読み込んで配列を作成します。ファイルI/Oからの初期化です。 numpy.frombuffer(buffer, dtype=float, count=-1, offset=0)
: バッファオブジェクト(例: bytesオブジェクト)をNumPy配列として解釈します。既存のメモリ領域を配列として扱う高度な手法です。
これらの関数は特定の高度なシナリオで役立ちますが、日常的な配列初期化のほとんどは、np.array
, np.zeros
, np.ones
, np.full
, np.arange
, np.linspace
, np.random
系関数、_like
系関数でカバーできます。
結論:適切な初期化がNumPy活用の鍵
NumPy配列の初期化は、効率的かつ正確な数値計算を行うための第一歩です。この記事では、Pythonリストからの変換から、特定の値、シーケンス、乱数、さらには既存配列をテンプレートにした初期化まで、NumPyが提供する主要な初期化手法を網羅的に解説しました。
それぞれの関数には、作成できる配列の種類、引数、そして最適な使用シナリオに違いがあります。
- 手持ちのPythonデータを変換するなら
np.array()
。 - 特定の固定値で埋めたいなら
np.zeros()
,np.ones()
,np.full()
。 - 規則的なシーケンスが必要なら
np.arange()
,np.linspace()
,np.logspace()
。 - 確率的な要素が必要なら
np.random
モジュール(特に新しいGenerator
システム)。 - 効率を重視し、かつ直後に値を上書きするなら
np.empty()
。 - 既存配列の形状やデータ型を引き継ぎたいなら
_like
系関数。 - 特殊な行列が必要なら
np.eye()
,np.identity()
,np.diag()
。
これらの手法を理解し、状況に応じて適切に使い分けることで、あなたのNumPyコードはより効率的になり、意図した通りの結果が得られるようになります。また、dtype
や shape
, order
といった属性にも注意を払うことで、メモリ使用量や計算性能をさらに最適化することが可能です。
NumPyの学習は配列の初期化から始まります。この記事が、あなたがNumPyを使いこなし、データサイエンスや数値計算の様々な課題に取り組む上での強固な基盤となることを願っています。実際にコードを書いて、それぞれの関数を試しながら理解を深めていくことが最も効果的な学習方法です。ぜひ、様々な初期化方法をあなたのコードで実践してみてください。