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などを使用して、配列の連結と分割について記載しました。