はじめてのNumpuy 5.配列の連結と分割とコピー

配列の連結 配列の分割 配列のコピー

こんにちは。BOSSです。

今回は主に

  • 配列の連結の仕方

  • 配列の分割の仕方

  • 配列のコピーの仕方

について学習していきます。

前回まで

はじめてのNumpy 1.アレイの作成と要素へアクセス
はじめてのNumpy 2.要素の取り出し
はじめてのNumpy 3.arrayの計算、arrayの大きさを変え方
はじめてのNumpy 4.次元操作

配列の連結

2つ以上の配列を連結して1つの配列にする方法を見ていきましょう。

使用するメソッドは以下の通り。

連結する方向や軸を指定した連結が可能です。

  • hstack: 横方向に連結

  • vstack: 縦方向に連結 ​

  • concatenate :軸を指定して連結

それでは一つずつ見ていきましょう。まずはいつも通りimport

そしていくつかのarrayを定義しておきます。

import numpy as np

arr1 = np.arange(0, 10)
print(arr1)
arr2 = np.arange(10, 20)
print(arr2)
arr3=np.array([[1,2], [3, 4]])
print(arr3)
arr4 = np.array([[5, 6], [7, 8]])
print(arr4)
[0 1 2 3 4 5 6 7 8 9]
[10 11 12 13 14 15 16 17 18 19]
[[1 2]
 [3 4]]
[[5 6]
 [7 8]]

arr1とarr2が1×10の配列(1次元)です。

arr3とarr4が2×2の多次元配列(2次元)となっています。

これらを用いて連結の仕方を確認していきましょう。

arrayを横方向に連結

まずは横方向に連結して見ましょう。

np.hstack((array1, array2))

ちなみに"hstack"のhはhorizontal(水平な)のhです。

それでは上で定義したarrayを連結してみましょう。

# 横方向に連結
print(np.hstack((arr1, arr2)))
print(np.hstack((arr3, arr4)))
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
[[1 2 5 6]
 [3 4 7 8]]

上で定義したそれぞれのarrayが横方向に繋がっていることが確認できましたでしょうか。

では、arr1とarr3を連結してみるとどうでしょう?

次元が違う配列同士ですが、、、

print(np.hstack((arr1, arr3)))
ValueError: all the input arrays must have same number of dimensions

怒られましたね!

次元一緒にしろってエラーが出ます。

というわけで、連結するときは次元をそれぞれしっかりと確認しましょう。

arrayを縦方向に連結

つぎは縦方向です。

np.vstack((array1, array2))

"vstack"のvはvertical(垂直な)のvです。

同じく上で定義したarrayを連結して見ましょう。

# 縦方向に連結
print(np.vstack((arr1, arr2)))
print(np.vstack((arr3, arr4)))
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]]
[[1 2]
 [3 4]
 [5 6]
 [7 8]]

縦につながっていますね。

ここでarrayの大きさを確認してみましょう。

print(np.vstack((arr1, arr2)).shape)
print(np.vstack((arr3, arr4)).shape)
(2, 10)
(4, 2)

1×10のarrayが2×10に、2×2のarrayが4×2になっていることがわかるかと思います。

軸を指定してarrayを連結

次にconcatenateを用いて、軸を指定してarrayを連結していきます。

np.concatenate((array1, array2), axis=軸)

デフォルトはaxis=0で、0軸方向に連結されます。

確認して見ましょう。

print(np.concatenate((arr1, arr2), axis=0))

print(np.concatenate((arr3, arr4), axis=0))
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

[[1 2]
 [3 4]
 [5 6]
 [7 8]]

軸指定っていうのがちょっとややこしいですね。

arr1とarr2は一次元の配列です。
したがって軸は0軸しか指定できません。(ちょっとややこしいですが、軸は0スタート。概念として次元は一次元スタートだからズレがあります。)

一方でarr3とarr4は二次元配列です。
したがって連結方向として0軸方向と1軸方向が選択可能です。

この場合0軸方向(つまり行方向)への連結を指定しているためこのようになります。

また、連結の順番はarrayの記述の順番ですので次のように書くと

print(np.concatenate((arr4, arr3), axis=0))
[[5 6]
 [7 8]
 [1 2]
 [3 4]]

このように連結の順番が変わってることが確認できますね。

ちなみにもっと連結することも可能で

print(np.concatenate((arr4, arr3, arr4), axis=0))

このように書くと

[[5 6]
 [7 8]
 [1 2]
 [3 4]
 [5 6]
 [7 8]]

3つ連結されます。では1軸方向(列方向)の連結はどうでしょうか。

print(np.concatenate((arr3, arr4), axis=1))
print(np.concatenate((arr3, arr4), axis=1).shape)
[[1 2 5 6]
 [3 4 7 8]]

(2, 4)

たしかに列方向への連結ができていることが確認できるかと思います。

配列の分割

arrayの分割にはnp.split()を用います。

np.split(<array>, <分割数>)

で任意のarrayを分割できます。

分割されたarrayは配列に格納されます。

それではやってみましょう。以下のようなarrayを定義します。

arr5 = np.arange(0, 20)
print(arr5.shape)

大きさを確認してみると

(20,)

要素数20個の一次元配列です。これを4つに分割してみましょう。

print(np.split(arr5, 4))
[array([0, 1, 2, 3, 4]), array([5, 6, 7, 8, 9]), array([10, 11, 12, 13, 14]), array([15, 16, 17, 18, 19])]

0から順番に5つずつの要素で分割されてリストに格納されているのが確認できます。

配列のコピー

最後に配列のコピーの仕方をみていきましょう。配列のコピーは.copy()を用いると可能ですが、代入するのと何が違うのでしょうか?

そのあたりを確認してみましょう。

arr6として以下のようなarrayを定義します。

arr6 = np.zeros((5, 5), dtype=int)

5×5の0埋め配列です。

ここでarr7とarr8を次のようにします。

arr7 = arr6
arr8 = arr6.copy()

arr7はarr6を代入、arr8はarr6のコピーとします。そして、arr6の要素の一部を変更してみます。

arr6[(2, 3)] = 10

さて、arr7とarr8はどうなったでしょうか?

# 代入しただけ
print(arr7)

# コピーした場合
print(arr8)
[[ 0  0  0  0  0]
 [ 0  0  0  0  0]
 [ 0  0  0 10  0]
 [ 0  0  0  0  0]
 [ 0  0  0  0  0]]

[[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

わかりますでしょうか?

単に代入しただけだと、arrayの変更も共有されています。

一方でコピーしたarr8はコピーを行なった段階のarrayをそのまま保持し続けています。

変更を反映しないというのはロバスト性を考えると重要な考え方になります。

コピーと代入。必要な機能を適宜使い分けられるようにしておきましょう。

まとめ

今回は

  • 配列の連結の仕方

  • 配列の分割の仕方

  • 配列のコピーの仕方

をそれぞれ見ていきました。

array操作の基本となる部分ですのでしっかりと学習しておきましょう。