NumPy

【NumPy】「NumPy配列(ndarray)のスライス」と「Python組み込みのlistのスライス」の違い

【NumPy】「NumPy配列(ndarray)のスライス」と「Python組み込みのlistのスライス」の違い
naoki-hn

「NumPy 配列(ndarray)のスライス」と「Python 組み込みの list のスライス」の違いについて解説します。

「NumPy 配列(ndarray)のスライス」と
「Python 組み込みの list のスライス」の違い

NumPy の配列(ndarray)は、データ分析で非常によく使用されるデータ型です。ndarray は、Python のリストのスライス同様に一部を取り出すことができます。しかし、NumPy の ndarray と 組み込みの list は、スライスの意味が異なります。

結論をまず言ってしまうと「NumPy の配列(ndarray)のスライスはビュー」であるため、スライスした値を変更すると元の配列のデータも更新されます。

一方で、「Python 組み込みの list のスライスはコピー」であるため、スライスした値を変更しても元のデータは変更されません。

この記事では、この違いについて例を用いながら紹介します。

NumPy 配列(ndarray)のスライスはビュー

NumPy 配列(ndarray)をスライスで取得した場合にはビューとなり、取得したデータを変更すると元のデータも変更されます。

NumPy は、科学技術計算や画像処理、機械学習等で、巨大な行列、高次元行列、膨大な件数のデータといった大規模データを扱います。そのため、スライスのたびにコピーが発生するとメモリも時間も浪費し、パフォーマンスに大きく影響します。そのため、ビューで問題ない状況であればビューにするという考え方の設計となっています。

1 次元配列と 2 次元配列の簡単な例で見てみましょう。

【1次元配列で確認】

import numpy as np

# ===== 1次元配列で確認
data_1d = np.arange(10)
print(f'元のデータ: {data_1d}')

# スライスで一部のデータを取得する
tmp_data_1d = data_1d[3:8]
print(f'スライスデータ: {tmp_data_1d}')
# スライスのデータを変更する
print('\n=== スライスしたデータを変更')
tmp_data_1d[:] = 0
print(f'スライスデータ: {tmp_data_1d}')
# 元のデータを確認する
print(f'元のデータ: {data_1d}')
【実行結果】
元のデータ: [0 1 2 3 4 5 6 7 8 9]
スライスデータ: [3 4 5 6 7]

=== スライスしたデータを変更
スライスデータ: [0 0 0 0 0]
元のデータ: [0 1 2 0 0 0 0 0 8 9]

【2次元配列で確認】

import numpy as np

# ===== 2次元配列で確認
data_2d = np.arange(25).reshape((5, 5))
print(f'元のデータ: \n{data_2d}')

# スライスで一部のデータを取得する
tmp_data_2d = data_2d[1:4, 1:4]
print(f'スライスデータ: \n{tmp_data_2d}')
# スライスのデータを変更する
print('\n=== スライスしたデータを変更')
tmp_data_2d[:, :] = 0
print(f'スライスデータ: \n{tmp_data_2d}')
# 元のデータを確認する
print(f'元のデータ: \n{data_2d}')
【実行結果】
元のデータ: 
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]]
スライスデータ: 
[[ 6  7  8]
 [11 12 13]
 [16 17 18]]

=== スライスしたデータを変更
スライスデータ: 
[[0 0 0]
 [0 0 0]
 [0 0 0]]
元のデータ: 
[[ 0  1  2  3  4]
 [ 5  0  0  0  9]
 [10  0  0  0 14]
 [15  0  0  0 19]
 [20 21 22 23 24]]

例では、tmp_data_1dtmp_data_2d にスライスのデータを保持しています。その値をすべて 0 にした後に、元データである data_1ddata_2d を参照すると、スライスした部分の数字がすべて 0 に変わっていることが分かります。

2 次元までで確認しましたが、3 次元以上の n 次元配列となっても同じです。

Python 組み込みの list のスライスはコピー

Python のリストの場合はスライスはコピーとなります。1 次元の例だけ確認してみましょう。2次元以降の多次元配列でも考え方は同じです。

data_1d = [i for i in range(10)]
print(f'元のデータ: {data_1d}')

# スライスで一部のデータを取得する
tmp_data_1d = data_1d[3:8]
print(f'スライスデータ: {tmp_data_1d}')
# スライスのデータを変更する
print('\n=== スライスしたデータを変更')
for i, _ in enumerate(tmp_data_1d):
    tmp_data_1d[i] = 0
print(f'スライスデータ: {tmp_data_1d}')
# 元のデータを確認する
print(f'元のデータ: {data_1d}')
【実行結果】
元のデータ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
スライスデータ: [3, 4, 5, 6, 7]

=== スライスしたデータを変更
スライスデータ: [0, 0, 0, 0, 0]
元のデータ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Python のリストの場合、スライスで取得したデータの要素を変更しても、元データの値が変わっていません。つまり、スライスではコピーとなっていることが分かります。

NumPy 配列(ndarray)のスライスを
コピーにする場合

NumPy 配列(ndarray)のスライスをビューではなく明示的にコピーして使用したい場合には、copy メソッドを使用します。

import numpy as np

data = np.arange(10)
print(f'data: {data}')

# データをコピーする
data_copy = data[3:8].copy()
print(f'data_copy: {data_copy}')

# コピーしたデータを変更する
print('\nコピーしたデータを変更する')
data_copy[:] = 0
print(f'data_copy: {data_copy}')
# もとのデータを確認する
print(f'data: {data}')
【実行結果】
data: [0 1 2 3 4 5 6 7 8 9]
data_copy: [3 4 5 6 7]

コピーしたデータを変更する
data_copy: [0 0 0 0 0]
data: [0 1 2 3 4 5 6 7 8 9]

例の data[3:8].copy() のようにスライスしたデータで copy メソッドを実行することで、スライスのコピーを作成することができます。コピーしたスライスの値を全て 0 に変更していますが、元データには変更がされていないことが分かります。

NumPy 配列のスライスで元データを変更したくない場合は、copy メソッドでコピーしてから操作するというように覚えておきましょう。

copy メソッドの公式ドキュメントはこちらを参照してください

まとめ

「NumPy 配列(ndarray)のスライス」と「Python 組み込みの list のスライス」の違いについて解説しました。

これらの違いは以下のようになります。

  • NumPy 配列(ndarray)のスライス:ビュー
  • Python 組み込みの list のスライス:コピー

この記事では、上記を例を使って確認してみました。NumPy 配列(ndarray)のスライスをコピーして使用したい場合には、明示的に copy メソッドを使用する必要があるので覚えておきましょう。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド
ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました