python – numpyで配列スライス、形状変更、連結分割

python – numpyで配列スライス、形状変更、連結分割

今回は、業務の中で非常に頻繁に使用するNumpyの基本となる配列のスライスとreshape, newaxisを使用して配列の形状変更、最後にconcatenate, splitなどを使用して、配列の連結と分割について記載しようと思います。

1. NumPyで配列のスライス(slice)

角カッコを使用して個々の配列要素にアクセスできるように、コロン(:)でマークされたスライス(slice)表記を使用して部分配列にアクセスすることができます。

NumPyのスライス構文は、標準のPythonリストの構文に従います。配列xのスライスにアクセスするには、次の指定方法を使います。

x[start:stop:step]

これらのいずれかが指定されていない場合は、デフォルト値start = 0、stop = その次元のsize、step = 1になります。まず1次元配列の部分配列を扱い、その後で多次元配列に進みます。

x[start:stop]  # items start through stop-1
x[start:]      # items start through the rest of the array
x[:stop]       # items from the beginning through stop-1
x[:]           # a copy of the whole array

-1の扱いについてはこちら。

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

ちなみに、pandas dataframeのilocでもスライスを使います。1000レコードのdataframeにて、0~299をdf_1にセットし、300~1000をdf_2にセットする場合の例はこちら。

index = 300
df_1, df_2 = df.iloc[:index].copy(), df_out.iloc[index:].copy()

modeling時などに、最後のカラムにtarget variableがある場合は、このような指定でスライスを使ったりもしています。

X = df.iloc[:, :-1]
y = df.iloc[:, -1]

Numpyで1次元配列のスライス(slice)

まずは、numpyをimportしてから、今回使用するデータを作成して変数の中身を確認してみます。

In [1]: import numpy as np

In [2]: x = np.arange(10)

In [3]: x
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

次に、numpyのスライス(slice)を使用して先頭から5つの要素を取得してみます。

In [4]: x[:5]
Out[4]: array([0, 1, 2, 3, 4])

今度は、numpyのスライス(slice)を使用して末尾から5つの要素を取得してみます。

In [5]: x[5:]
Out[5]: array([5, 6, 7, 8, 9])

numpyのスライス(slice)を使用して配列中間の要素3つを取得してみます。

In [6]: x[3:6]
Out[6]: array([3, 4, 5])

numpyのスライス(slice)を使用して1つおきに要素を取得してみます。

In [7]: x[::2]
Out[7]: array([0, 2, 4, 6, 8])

それでは、1をスタート地点として1つおきに要素を取得してみます。

In [8]: x[1::2]
Out[8]: array([1, 3, 5, 7, 9])

少し混乱するケースは、stepが負の場合です。この場合、startとstopのデフォルト値は入れ替わります。つまり、末尾から配列データを取得する場合に使用します。

In [9]: x[::-1]
Out[9]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

では、同様に末尾からインデックス5からスタートして、1つおきの要素をnumpyのスライスを使用して取得します。

In [10]: x[5::-2]
Out[10]: array([5, 3, 1])

Numpyで多次元配列のスライス(slice)

Numpyで多次元配列のスライス(slice)は、複数のスライスをカンマ区切りで指定します。まずは、np.random.randintを使用して2次元配列データを作成します。

In [11]: x2 = np.random.randint(10, size=(3, 4))

In [12]: x2
Out[12]:
array([[0, 1, 2, 8],
       [8, 6, 0, 7],
       [1, 8, 0, 1]])

では、numpyのスライスを使用して、2行のデータと3列分のデータを取得します、

In [13]: x2[:2, :3]
Out[13]:
array([[0, 1, 2],
       [8, 6, 0]])

次に、3行全てと先頭から1つおきのデータを取得します。全ての列を取得する場合、”:(コロン)”のみでも取得できます。

In [14]: x2[:3, ::2]
Out[14]:
array([[0, 2],
       [8, 0],
       [1, 0]])

In [15]: x2[:, ::2]
Out[15]:
array([[0, 2],
       [8, 0],
       [1, 0]])

配列のアクセスに関して、最も頻繁に行われる操作は、行または列の抽出です。これは、インデクスと1つのコロン(:)による空のスライスとの組み合わせで行います。

numpyのスライスを使用して、先頭の列の値のみ取得してみます。

In [16]: x2[:, 0]
Out[16]: array([0, 8, 1])

今度は、先頭の行のみを取得してみます。

In [17]: x2[0, :]
Out[17]: array([0, 1, 2, 8])

また、上記と同様のデータを取得する場合(行にアクセスする場合)、空のスライスを省略することで非常にシンプルにデータを取得することができます。

In [18]: x2[0]
Out[18]: array([0, 1, 2, 8])

2. NumPyで配列の形状変更(reshape)

配列に対する重要な操作の1つが形状の変更です。これを行うには、reshapeメソッドを使う方法が最も柔軟です。

それではまず、np.arangeを使用して1〜9の配列を作成します。

In [19]: tmp = np.arange(1, 10)
    ...: tmp
Out[19]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])

次に、numpyのreshapeを使用して3行3列のデータに形状を変更してみます。

In [20]: grid = tmp.reshape((3, 3))
    ...: grid
Out[20]:
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

これが機能するためには、最初の配列の要素数と、変更後の要素数が一致しなければならないことに注意が必要です。

可能であれば、reshapeメソッドは初期配列のコピーではなくビューを使用しますが、メモリが連続していないなどの理由により、コピーが行われることもあります。

別の一般的な形状変更パターンは、1次元配列を2次元の行または列ベクトルに変換することです。これは、スライス操作にnewaxisキーワードを使用することで、より簡単に行うことができます。

In [4]: x = np.array([1, 2, 3])

In [5]: x
Out[5]: array([1, 2, 3])

In [6]: x.reshape((1, 3))
Out[6]: array([[1, 2, 3]])

次に、newaxisを使用して行ベクトルを作成してみます。

In [7]: x[np.newaxis, :]
Out[7]: array([[1, 2, 3]])

reshapeを使用して、列ベクトルを作成してみます。

In [8]: x.reshape((3, 1))
Out[8]:
array([[1],
       [2],
       [3]])

最後に、newaxisを使用して、列ベクトルを作成します。

In [9]: x[:, np.newaxis]
Out[9]:
array([[1],
       [2],
       [3]])

3. 配列の連結と分割

Numpy 配列の連結(np.concatenate, np.vstack, np.hstack)

NumPyによる2つの配列の連結または結合は、主にnp.concatenate、np.vstack、およびnp.hstackによって実行します。

a) np.concatenate

np.concatenateは、最初の引数としてタプルまたは配列のリストを取ります。

In [10]: x = np.array([1, 2, 3])
    ...: y = np.array([3, 2, 1])
    ...: np.concatenate([x, y])

Out[10]: array([1, 2, 3, 3, 2, 1])

一度に2つ以上の配列を連結することもできます。

In [11]: z = [99, 99, 99]
    ...: np.concatenate([x, y, z])

Out[11]: array([ 1,  2,  3,  3,  2,  1, 99, 99, 99])

np.concatenate([grid, grid], axis=1)は、2次元配列に対しても使用できます。

In [12]: grid = np.array([[1, 2, 3],
    ...:                  [4, 5, 6]])

In [13]: grid
Out[13]:
array([[1, 2, 3],
       [4, 5, 6]])

np.concatenate()を使用して、2次元配列をSQLのunionのように垂直方向に連結してみます。

In [14]: np.concatenate([grid, grid])
Out[14]:
array([[1, 2, 3],
       [4, 5, 6],
       [1, 2, 3],
       [4, 5, 6]])

np.concatenate()を使用して、2次元配列水平方向に連結してみます。

In [15]: np.concatenate([grid, grid], axis=1)
Out[15]:
array([[1, 2, 3, 1, 2, 3],
       [4, 5, 6, 4, 5, 6]])

b) np.vstack

混合次元の配列を扱う場合、np.vstack(垂直スタック)関数とnp.hstack(水平スタック)関数を使用する方がよりシンプルになります。

In [16]: np.vstack([x, grid])
Out[16]:
array([[1, 2, 3],
       [1, 2, 3],
       [4, 5, 6]])

c) np.hstack

np.hstack(水平スタック)関数を使用してみます。

In [17]: y = np.array([[99],
    ...:               [99]])

In [18]: np.hstack([grid, y])
Out[18]:
array([[ 1,  2,  3, 99],
       [ 4,  5,  6, 99]])

Numpy配列の分割(np.split, no.hsplit, np.vsplit)

連結の反対は分割です。これは、関数np.split、np.hsplit、およびnp.vsplitによって行います。これらの関数には、分割点を与えるインデクスのリストを渡すことができます。

a) np.split

まず、np.split()で分割を行なってみます。

In [19]: x = [1, 2, 3, 99, 99, 3, 2, 1]
    ...: x1, x2, x3 = np.split(x, [3, 5])
    ...: x1, x2, x3
Out[19]: (array([1, 2, 3]), array([99, 99]), array([3, 2, 1]))

b) np.hsplit

次に、np.vsplitで水平方向の分割を行なってみます。

In [20]: grid = np.arange(16).reshape((4, 4))
    ...: grid
Out[20]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

In [21]: upper, lower = np.vsplit(grid, [2])
    ...: upper, lower
Out[21]:
(array([[0, 1, 2, 3],
        [4, 5, 6, 7]]),
 array([[ 8,  9, 10, 11],
        [12, 13, 14, 15]]))

c) np.hsplit

最後に、np.hsplitで垂直方向の分割を行なってみます。

In [22]: left, right = np.hsplit(grid, [2])
    ...: left, right
Out[22]:
(array([[ 0,  1],
        [ 4,  5],
        [ 8,  9],
        [12, 13]]),
 array([[ 2,  3],
        [ 6,  7],
        [10, 11],
        [14, 15]]))

4. まとめ

ということで、python – numpyで配列スライス、形状変更、連結分割でした。業務の中で非常に頻繁に使用するNumpyの基本となる配列のスライスとreshape, newaxisを使用して配列の形状変更、最後にconcatenate, splitなどを使用して、配列の連結と分割について記載しました。

(Visited 119 times, 1 visits today)