Pythonライブラリ講座

【NumPy】配列の結合・分割を完全マスター!練習問題付き

前のページ|次のページ

6章ではNumPy配列(ndarray)の結合・分割ができる『numpy.concatenate()』と『numpy.split()』について解説します。

練習問題も記事の最後に用意していますので、ぜひ最後まで読んでいってください!

本連載講座【Python ライブラリ編】では、データサイエンスに必要なPythonライブラリやその使い方を基礎から学ぶことができます。

NumPyPandasMatplotlibScipySeabornについて、初学者の方にも分かりやすいよう丁寧に解説しています。

さらに、学習した内容を定着させられるように各章演習問題を用意しています。

・Pythonでデータ分析ができるようになりたい

・Pythonの基礎事項は一通り学んだので、さらに深く学びたい

このように考えている方はTech Teacherが運営する【Python ライブラリ編】で、Pythonによるデータサイエンスの学習をすることをお勧めします!

なお、『Pythonについて全く知らない』・『Pythonの基礎事項がまだ分かっていない』という方は、まずコチラの【Python 基礎編】で基礎を一通り学習してからライブラリ編に取り掛かりましょう!

<ライブラリ編 目次>

<ライブラリの基礎>
1章:ライブラリとは

<NumPy>
2章:NumPyの概要と配列(ndarray)
3章:統計量や次元の取得/ソート
4章:配列のインデックス
5章:numpy.whereによる条件制御
6章:配列の結合/分割
7章:乱数

<SciPy>
8章:SciPyの概要と基本操作

<Pandas>
9章:SeriesDataFrame/統計量の取得
10章:データの読み込み/書き込み
11章:データの取り出し/追加
12章:データのソート
13章:データの結合
14章:階層型インデックス
15章:groupbyによる集計
16章:マッピング処理
17章:欠損値の扱い

<Matplotlib>
18章:Matplotlibの概要
19章:pyplotインターフェース
20章:オブジェクト指向インターフェース

<Seaborn>
21章:Seabornの概要と基本操作

numpy.concatenate()による配列の結合

NumPyの『concatenate()』関数を用いると、複数の配列を縦方向または横方向に結合することができます。

concatenate()関数の書式は以下の通りです。

np.concatenate(配列のリスト, 結合する方向)

第一引数に結合したい配列のリスト、第二引数に結合する方向(軸)を指定します。

この関数は、リスト中の配列を指定した方向に結合した新たな配列を返します。

第一引数には配列のリストを与えるようにしましょう。
np.concatenate(配列1, 配列2, …)のように配列一つひとつを別の引数として渡すと正しく実行できません。

縦方向に結合

配列を縦方向に結合するには、第二引数として0(またはaxis=0)を指定します。

x = np.arange(8).reshape(2,4)
#[[0 1 2 3]
# [4 5 6 7]]

y = np.zeros((2,4))
#[[0. 0. 0. 0.]
# [0. 0. 0. 0.]]

#xとyを縦方向(axis=0)に結合
arr_v = np.concatenate([x,y], axis=0)
print(arr_v)
[[0. 1. 2. 3.]
 [4. 5. 6. 7.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

第二引数を省略した場合は、縦方向に結合されます。

また、3つ以上の配列を結合することもできます。

a = np.arange(4).reshape(2,2)
#[[0. 1.]
# [2. 3.]]

b = np.ones((2,2))
#[[1. 1.]
# [1. 1.]]

c = np.zeros((2,2))
#[[0. 0.]
# [0. 0.]]

#a,b,cを縦方向に結合
arr_abc_v = np.concatenate([a,b,c])
print(arr_abc_v)
[[0. 1.]
 [2. 3.]
 [1. 1.]
 [1. 1.]
 [0. 0.]
 [0. 0.]]

横方向に結合

横方向の結合も縦方向とほとんど同じように書くことができ、第二引数を1(またはaxis=1)にするだけです。

x = np.arange(8).reshape(2,4)
#[[0 1 2 3]
# [4 5 6 7]]

y = np.zeros((2,4))
#[[0. 0. 0. 0.]
# [0. 0. 0. 0.]]

#xとyを横方向(axis=1)に結合
arr_h = np.concatenate([x,y], axis=1)
print(arr_h)
[[0. 1. 2. 3. 0. 0. 0. 0.]
 [4. 5. 6. 7. 0. 0. 0. 0.]]

第二引数を省略するとデフォルトである縦方向の結合が行われてしまいます。
配列を横方向に結合したい場合は、必ず第二引数axisを1にしましょう。

縦方向の場合と同様、3つ以上の配列を横方向に結合することもできます。

a = np.arange(4).reshape(2,2)
#[[0. 1.]
# [2. 3.]]

b = np.ones((2,2))
#[[1. 1.]
# [1. 1.]]

c = np.zeros((2,2))
#[[0. 0.]
# [0. 0.]]

#a,b,cを縦方向に結合
arr_abc_h = np.concatenate([a,b,c], axis=1)
print(arr_abc_h)
[[0. 1. 1. 1. 0. 0.]
 [2. 3. 1. 1. 0. 0.]]

結合する部分の次元はすべての配列で等しくなるようにしましょう。
例えば2行3列の行列と2行4列の行列を縦に結合しようとするとエラーが出てしまいます。

numpy.split()による配列の分割

split()』関数を用いると、配列を様々な方法で分割することができます。

split()関数の書式は以下の通りです。

np.split(分割したい配列, 分割方法, 分割する方向)

第一引数で分割したい配列、第二引数で分割方法、第三引数で分割する方向(0または1)を指定します。

第三引数の分割する方向についてはconcatenate()と同じく、0にすると縦方向、1にすると横方向の分割ができます。

この関数は分割された配列のリストを返すということに注意しましょう。

では、第二引数による分割方法の指定のしかたを実行例とともに詳しく見てみましょう。

個数を指定して分割

第二引数にint型の整数を渡すと、配列を指定した個数に等分割することができます。

split()関数の戻り値は配列のリストなので、分割後の配列を1つずつ取得するにはリストのインデックスを用います。

x = np.arange(18).reshape(6, 3) #6行3列
#[[ 0  1  2]
# [ 3  4  5]
# [ 6  7  8]
# [ 9 10 11]
# [12 13 14]
# [15 16 17]]

#縦方向に3つに分割
x_split = np.split(x, 3, axis=0)

print(x_split[0])
print()
print(x_split[1])
print()
print(x_split[2])
[[0 1 2]
 [3 4 5]]

[[ 6  7  8]
 [ 9 10 11]]

[[12 13 14]
 [15 16 17]]

横方向の分割でも同様に等分割することができます。

また、Pythonでは代入先の変数を「,(カンマ)」で区切ることで、リストやタプルの要素をそれぞれの変数に代入することができます。

これを利用して、分割後の配列を1つずつ別の変数に代入することができます。

y = np.ones((2, 4)) #2行4列
#[[1. 1. 1. 1.]
# [1. 1. 1. 1.]]

#横方向に2つに分割し、分割後の配列をそれぞれ別の変数に代入
y_split_1, y_split_2 = np.split(y, 2, axis=1)

print(y_split_1)
print()
print(y_split_2)
[[1. 1.]
 [1. 1.]]
 
[[1. 1.] [1. 1.]]

第二引数に渡す整数は元の配列の行数を割り切る数にしましょう。
元の配列の行数が第二引数で割り切れない場合、正しく実行できません。

分割の境界をインデックスとして指定

第二引数にint型整数のリストを渡すと、分割位置をインデックスで指定することができます。

例えば第二引数に「[1, 3]」を渡すと、1行目の手前3行目の手前が分割の境界となります。

なお、インデックスは0から始まるということに注意しましょう。

z = np.zeros((5, 2)) #5行2列
#[[0. 0.]
# [0. 0.]
# [0. 0.]
# [0. 0.]
# [0. 0.]]

#1行目の手前と3行目の手前で分割
z_split = np.split(z, [1,3], axis=0)

print(z_split[0])
print()
print(z_split[1])
print()
print(z_split[2])
[[0. 0.]]

[[0. 0.] [0. 0.]]
[[0. 0.] [0. 0.]]

確かに1行目の手前と3行目の手前で配列が分割されていることが分かると思います。

6章の練習問題

以下の練習問題を解いてみましょう。

練習問題

問1. 以下の2つの2次元配列x, yについて、(1)~(2)を出力するプログラムを書いてください。

x = np.zeros((3, 3))
y = np.ones((3, 3))

(1) xとyを縦方向に結合した配列
(2) xとyを横方向に結合した配列

問2. 以下の2次元配列zに対して、(1)~(3)を出力するプログラムを書いてください。

z = np.zeros((4, 4))

(1) zを縦方向に2等分した配列
(2) zを横方向に4等分した配列
(3) zを2行目と3行目の手前で横方向に分割した配列(ただし先頭を0行目とする。)

解答

問1(クリックして解答を表示)

x = np.zeros((3, 3))
y = np.ones((3, 3))

#(1)
print('(1):')
concat_v = np.concatenate([x,y], axis=0)
print(concat_v)
print()

#(2)
print('(2):')
concat_h = np.concatenate([x,y], axis=1)
print(concat_h)
(1):
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
(2):
[[0. 0. 0. 1. 1. 1.]
 [0. 0. 0. 1. 1. 1.]
 [0. 0. 0. 1. 1. 1.]]

問2(クリックして解答を表示)

(1)

z = np.zeros((4, 4))

#(1)
z_1 = np.split(z, 2, axis=0)

print(z_1[0])
print()
print(z_1[1])
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]]
 
[[0. 0. 0. 0.] [0. 0. 0. 0.]]

(2)

#(2)
z_2 = np.split(z, 4, axis=1)

for i in range(4):
    print(z_2[i])
    print()

[[0.]
 [0.]
 [0.]
 [0.]]
 
[[0.] [0.] [0.] [0.]]
[[0.] [0.] [0.] [0.]]
[[0.] [0.] [0.] [0.]]

(3)

#(3)
z_3 = np.split(z, [2,3], axis=1)

for i in range(3):
    print(z_3[i])
    print()
[[0. 0.]
 [0. 0.]
 [0. 0.]
 [0. 0.]]
 
[[0.] [0.] [0.] [0.]]
[[0.] [0.] [0.] [0.]]

次のページへ

【一覧表】NumPyの乱数生成(np.random)を分かりやすく解説! 前のページ|次のページ 7章では、NumPyの『乱数(rand, random_sample, randint, randn, ...