第2回:NumPy配列の追加・挿入・コピー
はじめに
本記事は「Pythonで数値計算を行うためのNumPy入門」シリーズの第2回です.
このシリーズでは, "NumPyのよく使われる機能を分野ごとに理解すること" を目標としています.
Pythonで数値計算を行う際に欠かせないライブラリ「NumPy」について,基礎から応用まで体系的に学べます.
シリーズ構成予定(全10記事)
- NumPyのインストール 〜 arrayの基本
- 配列の追加・挿入・コピー(本記事)
- 配列の作成(ones / zeros / arange / linspace)(公開後にリンクを追加する予定)
- 配列の結合(hstack / vstack)(公開後にリンクを追加する予定)
- 条件抽出と検索(where / clip / unique)(公開後にリンクを追加する予定)
- 統計処理(mean / std / median ほか)(公開後にリンクを追加する予定)
- 線形代数(dot / inv / det / norm)(公開後にリンクを追加する予定)
- 乱数生成(rand / randint / normal)(公開後にリンクを追加する予定)
- 三角関数と角度変換(sin / arctan2 ほか)(公開後にリンクを追加する予定)
- ファイル入出力(savetxt / loadtxt)(公開後にリンクを追加する予定)
私のNumpyライブラリのバージョンは "1.26.4" です.
本記事のゴール
本記事では, 配列操作の基本となる「追加」「挿入」「コピー」 を学んでいきます.
今回扱う関数は以下の3つです.
| 関数 | 内容 | 備考 |
|---|---|---|
np.append() |
配列の末尾に追加 | 配列の末尾にデータが追加 |
np.insert() |
任意の位置に挿入 | 挿入位置を任意で設定 |
np.copy() |
配列のコピー | 実体コピーと参照コピーに注意 |
実体コピーと参照コピーの違いを理解しないと,想定通りに動かなくなりますので,しっかりと理解しましょう.
NumPyライブラリの読み込み
NumPyを使うためには,まずライブラリの読み込みから始めます.
import numpy as np
numpyライブラリを "np" という名前として,扱えるようにライブラリを読み込んでいます.
以降では "np" という名前を使っていきます.
np.append() (配列の末尾にデータを追加)
配列の末尾にデータを追加する np.append() 関数に関して説明する.
# 配列の初期化
arr = np.array([10, 20, 30])
print(arr)
# 出力:[10, 20, 30]
# 配列の末尾にデータを追加
new_arr = np.append(arr, 40)
print(new_arr)
# 出力:[10, 20, 30, 40]
print(arr)
# 出力:[10, 20, 30]
元の配列 arr には,データが追加されない.new_arr に arr の末尾にデータが追加された配列が保存される.
arr と new_arr は異なる配列となる.
np.insert() (配列の任意位置にデータを挿入)
配列の任意位置にデータを挿入する np.insert() 関数に関して説明する.
# 配列の初期化
arr = np.array([10, 20, 30])
print(arr)
# 出力:[10, 20, 30]
# 配列の位置1に15を挿入
new_arr = np.insert(arr, 1, 15) # 位置1に15を挿入
print(new_arr)
# 出力:[10, 15, 20, 30]
配列のインデックスは "0" スタートです.
配列にデータを挿入すると挿入位置以降のデータが1つずつ後ろに移動します.
np.copy() (配列のコピー)
配列をコピーする np.copy() 関数に関して説明する.
# 配列の初期化
arr = np.array([10, 20, 30])
print(arr)
# 出力:[10, 20, 30]
# 配列のコピー
arr_copy = arr.copy()
# 要素0を999へ更新
arr_copy[0] = 999
print(arr)
# 出力:[10, 20, 30]
print(arr_copy)
# 出力:[999, 20, 30]
np.copy() を使用すると,別の配列として,定義されます.arr と arr_copy は同じデータ内容だが,異なる配列となります.
要するに,arr と arr_copy はメモリ内で異なるアドレス上に同じデータがコピーされます.
詳細は後ほど説明します.
次に,np.copy() を使用せずに,= によるコピーを実行します.
# "=" による配列のコピー
arr_view = arr
arr_view[0] = 999
print(arr)
# 出力:[999, 20, 30]
print(arr_view)
# 出力:[999, 20, 30]
**= は「コピー」ではなく「参照コピー」**になるため,arr と arr_view の両方とも値が変更されます.
要するに,arr と arr_view はメモリ内で同じアドレス上を参照しているので,片方の変数を修正すると両方の変数に影響が生じます.
次に,実体コピー(np.copy())と参照コピー(=) の違いを説明します.
実体コピーと参照コピー
実体コピー(np.copy())と参照コピー(=)の違いを説明します.
前提条件としては,下記の図の通りとなります.
変数名 arr を定義している前提で話を進めます.

はじめに,実体コピー(np.copy())の処理を説明します.
内容は下図の通りとなります.

変数 arr_view は変数 arr と異なるアドレス上に同じデータを保持しています.要するに arr_view と arr は異なるアドレスを参照しています.
異なるアドレスを参照しているため,arr_view を変更しても arr は変わらないです.また,arr を変更しても arr_view は変わらないです.
次に,参照コピー('=')の処理を説明します.
内容は下図の通りとなります.

変数 arr_view は変数 arr のアドレスを保存しています.要するに arr_view と arr は同じアドレスを参照しています.
同じアドレスを参照しているため,arr_view を変更すると arr も変更されます.また,arr を変更すると arr_view も変更されます.
実体コピーと参照コピーを下表にまとめます.
| コピー方法 | コピー先のアドレスとコピー元のアドレス | 内容 |
|---|---|---|
| 実体コピー('np.copy()') | 異なるアドレス | コピー先のデータをコピー |
| 参照コピー('=') | 同じアドレス | コピー先のアドレスをコピー |
実体コピーと参照コピーの違いを理解しないと,想定外の挙動となるので,注意しましょう.
次に,どうやって実体コピーか参照コピーかどうかを確認する方法を説明します.
実体コピーと参照コピーの判別方法
Pythonにて,実体コピー(np.copy())と参照コピー(=)を判別する方法を説明します.
Pythonが提供している標準関数である id() を使用して,変数が保存されているアドレスを取得して,実体コピーなのか参照コピーなのかを判別していきます.
ソースコードの実装例を説明します.
# 配列の初期化
arr = np.array([10, 20, 30])
# 配列のコピー
arr_copy = arr.copy()
# "=" による配列のコピー
arr_view = arr
# 各変数が保存されているアドレスを表示
print(id(arr))
# 出力:4380430704
print(id(arr_copy))
# 出力:4380977040
print(id(arr_view))
# 出力:4380430704
注意:id()の結果は各々で異なります.また,他にソフトウェアを起動していたりすると,毎回異なる結果となります.表示している値は私のPCで実行した時の結果となります.
注目して欲しい箇所は,id(arr) と id(arr_view) の結果が一致することと,id(arr) と id(arr_copy) の結果が不一致なことです.
id(arr) と id(arr_view) の結果が一致するので,= は参照コピーとなっていることが判別できます.
id(arr) と id(arr_copy) の結果が不一致なので,np.copy() は実体コピーとなっていることが判別できます.
実体コピーか参照コピーかを判別するには,id() の結果を見ましょう.
まとめ
NumPyライブラリの append / insert / copy を比較します.
| 操作 | 関数 | よく使う場面 |
| 値を末尾に追加 | np.append() | データの拡張 |
| 中間に挿入 | np.insert() | 条件挿入 |
| 配列のコピー | np.copy() | 後で加工する時 |
次に,実体コピーと参照コピーを比較します.
| コピー方法 | コピー先のアドレスとコピー元のアドレス | 内容 |
|---|---|---|
| 実体コピー('np.copy()') | 異なるアドレス | コピー先のデータをコピー |
| 参照コピー('=') | 同じアドレス | コピー先のアドレスをコピー |
配列操作を理解することで,データ加工や分析がスムーズになります.
実体コピーや参照コピーを理解することで,不具合を未然に防ぐことができます.
次回予告
第3回:「配列の作成(ones / zeros / arange / linspace)」
- np.ones() (配列を1で初期化)
- np.zeros() (配列を0で初期化)
- arange() (初期値から目標値まで一定値で増加する配列の作成)
- linspace() (初期値と目標値を等間隔にN分割した配列の作成)
そんなときに使う「配列生成の基本」を説明します.
参考文献
本記事を作成するに当たって参考にしたサイトをまとめました.
Discussion