【Python】初心者の方のためのNumPy入門② -配列構造、要素の指定

前回は、NumPy入門として配列の作成方法およびソートや要素の追加・削除といった操作を紹介しました。

https://www.learning-nao.com/?p=2477

今回は前回に続いてNumPy入門の2回目です。今回は、配列の構造の確認、変更および要素の指定方法(インデックス、スライシング)について見ていきます。

前回の記事でも軽く触れていますが、配列は必ずしも1次元とは限りません。2次元の配列もあれば、それ以上の高次元のものもあります。NumPy配列では、その次元を操作(次元数を上げたり下げたり)することができます。

前回は特定の要素の指定方法を紹介しましたが、Python配列同様にスライシングを使って指定することもできます。スライシングの基本と共に挙動を確認していければと思います。

配列の構造を確認する

配列を操作するにあたって、配列の構造を把握しておく必要があります。構造とは例えば、何次元の配列で要素数はいくつなのか等です。

自分が今作った配列ならともかく、他人が作った配列、とりわけ短くないソースコードの中の配列の情報は、ぱっと見ではなかなか検討がつきません。そういった場合に簡単に配列の構造を把握する方法がNumPyには用意されています。

以下の配列を例にしてみます。配列が入れ子になった3次元配列です。

arr = np.array([[[0, 1, 2, 3],
                 [4, 5, 6, 7]],

                  [[10, 11, 12, 13],
                   [14, 15,16, 17]],

                  [[20 ,21 ,22, 23],
                   [24, 25, 26, 27]]])

配列arrの次元数を確認するにはndimを用います。今回の場合は3次元であることがわかります。

#次元を得る
arr.ndim
>> 3

要素数を確認するにはsizeを用います。この場合、最小単位の要素数がいくつあるかということがわかります。例の場合は要素数4の配列が2×3で計24となります。

#要素数を得る
arr.size
>> 24

各次元における要素数を確認するにはshapeを用います。タプルの形で返されますが、大きい単位の次元での要素数から順に左から並びます。例では、最大単位での要素数は3、最小単位の要素数は4となります。

#次元ごとの要素数を得る
arr.shape
>> (3, 2, 4)

配列の構造を変える(reshape)

先の例の配列は(3, 2, 4)の配列でしたが、reshape()を使うことでその構造を変更することができます。引数には変更後の要素数を高い次元から指定します。

各次元での要素数を入れ替えてみます。

arr.reshape(2,4,3)
>> array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7, 10],
        [11, 12, 13]],

       [[14, 15, 16],
        [17, 20, 21],
        [22, 23, 24],
        [25, 26, 27]]])

次元数は同じ3次元ですが、それぞれの次元における要素数が変わったことがわかります。

ここで注意すべきは、全体の要素数はオリジナルの配列と同じである必要があるという点です。たとえば、以下のようにreshapeしようとするとエラーになります。これは、オリジナルの配列の要素数と変換後の配列の要素数が異なるためです。

arr.reshape(2,4,1)
>> ValueError: cannot reshape array of size 24 into shape (2,4,1)

次元数を変える

要素数が同じになるのであれば、次元を減らしたり増やしたりすることもできます。

#次元数を減らす
arr.reshape(8, 3)
>> array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7, 10],
       [11, 12, 13],
       [14, 15, 16],
       [17, 20, 21],
       [22, 23, 24],
       [25, 26, 27]])

#次元数を増やす
arr.reshape(2, 2, 3, 2)
>> array([[[[ 0,  1],
         [ 2,  3],
         [ 4,  5]],

        [[ 6,  7],
         [10, 11],
         [12, 13]]],


       [[[14, 15],
         [16, 17],
         [20, 21]],

        [[22, 23],
         [24, 25],
         [26, 27]]]])

要素の指定

NumPy配列では、通常のPython配列と同じようにインデックス指定やスライシングで要素を指定することができます。

インデックス指定

インデックス指定とは、特定の要素のインデックス番号を指定して値を取得することを指します。

a = np.array([1, 3, 4, 5, 6, 7, 8, 9])
a[3]
>> 5

a[-1] #-1は一番最後の要素を指す
>> 9

スライシング

一方、スライシングでは始点と終点のインデックスをコロン(:)を挟むように指定します。こうすることで、始点から終点までに含まれる要素を取得できます。

注意すべきは、終点に指定したインデックスは結果には含まれません。例の場合、終点である5番目の要素は結果には含まれません。

#[始点:終点]
a[2:5]
>> [4, 5, 6]

始点および終点のインデックスは省略可能です。

始点を省略すれば配列の先頭から指定した終点まで、終点を省略すれば指定した始点から配列の最後まで取得できます。

始点、終点の両方を省略すれば、配列の全ての要素を取得します。

#始点を省略
a[:5]
>> [1, 3, 4, 5, 6]

#終点を省略
a[4:]
>> [6, 7, 8, 9]

# 始点、終点を省略
a[:]
>> [1, 3, 4, 5, 6, 7, 8, 9]

条件式による指定

Numpy配列では、条件式による要素の取得も可能です。

例えば、値が5未満の要素を取得してみます。

a[a < 5]
>> array([1, 3, 4])

偶数や奇数だけを取得することもできます。式中の%は割り算の余りを取得します。偶数の場合はa(の要素)を2で割ったときの余りが0であるという条件になります。

#偶数のみ取得
a[a % 2 == 0]
>> array([4, 6, 8])

#奇数のみ取得
a[a % 2 == 1]
>> array([1, 3, 5, 7, 9])

条件式を組み合わせて取得することもできます。

#値が5未満または9の要素を取得(OR条件)
condition = (a < 5) | (a == 9)
a[condition]
>> array([1, 3, 4, 9])

#値が5未満かつ2以上の要素を取得(AND条件)
condition = (a < 5) & (a > 2)
a[condition]
>> array([3, 4])

Pythonでデータサイエンスするなら

Pythonでデータサイエンスをするなら、以下の書籍がおすすめです。Pandas、matplotlib、Numpy、scikit-learnといったデータサイエンスに必要なライブラリを、体系立てて一通り学ぶことができます。

ややお値段高めですが、これ1冊で十分という内容・ボリュームなので、損はしないと思います^^

まとめ

NumPy配列の構造、次元の操作および要素の指定について紹介しました。

配列構造の操作や様々な条件での要素指定はよくやる操作なので、是非知っておいてもらいたいです。1から10まで内容を覚えておく必要はないですが、うっすらとでも頭に入れておくといざというときに変に悩まずに済みます。

次回も今回の続きとして、Numpyの基本を紹介していきたいと思います。

ではでは👋