【NumPy入門】配列の形を変更・結合する方法!reshape・転置・concatenateを解説

Python

こんにちは、ぼりたそです!

今回は、NumPy配列の形を変更したり、複数の配列を結合したりする方法について解説します。

「1次元配列を表の形にしたい」「行と列を入れ替えたい」「別々のデータを1つにまとめたい」といった場面で使う操作です。

同じ形状の結果になっても、reshape()と転置では、値の並び方が異なることがあります。この記事では、その違いも図とコードで確認します。

Point
  • reshape():要素数を変えずに形状を変更する
  • -1:形状の一部を自動で決める
  • .T:2次元配列の行と列を入れ替える
  • np.concatenate():配列を指定した軸で結合する
  • 形状の条件と、元の配列とのデータ共有に関する注意点

それでは順番に確認していきましょう!

スポンサーリンク

配列の形状を確認するには?

NumPyでは、配列の形状をshapeで確認します。まずはライブラリを読み込み、配列を作成しましょう。

import numpy as np

a = np.array([1, 2, 3, 4, 5, 6])
print(a.shape)

# 実行結果:
# (6,)

(6,)は、要素数6の1次元配列を表します。1行6列の2次元配列なら、形状は(1, 6)です。

以降のコードは、NumPyの読み込みを実行した状態を前提とします。この記事では、主に1次元・2次元の数値配列を扱います。

reshape():要素数を変えずに形状を変更する

reshape()は、配列の要素数を保ったまま、形状を変更した結果を返します。

2次元配列へ変更する基本的な書き方は、次のとおりです。

配列.reshape(行数, 列数)

例えば、要素数6の配列を2行3列に変更してみましょう。

a = np.array([1, 2, 3, 4, 5, 6])
b = a.reshape(2, 3)

print(b)
print(b.shape)
print(a.shape)

# 実行結果:
# [[1 2 3]
#  [4 5 6]]
# (2, 3)
# (6,)

標準の並び順では、先頭から1行目に1, 2, 3、2行目に4, 5, 6が入ります。

bは2行3列ですが、元の配列aの形状は(6,)のままです。np.reshape(a, (2, 3))と書いても、同じ形状の結果を得られます。

変更前後の要素数を一致させる

今回の配列には6つの要素があります。2 × 3 = 6や3 × 2 = 6となる形状へ変更できますが、4 × 2 = 8には変更できません。

a.reshape(4, 2)を実行すると、要素数が一致しないためValueErrorになります。reshape()が足りない要素を補ったり、余った要素を削除したりするわけではありません。

-1:行数・列数の一方を自動で決める

形状の1か所に-1を指定すると、全体の要素数と、ほかに指定したサイズから自動で計算されます。

a = np.array([1, 2, 3, 4, 5, 6])

print(a.reshape(2, -1))
print(a.reshape(-1, 2))

# 実行結果:
# [[1 2 3]
#  [4 5 6]]
# [[1 2]
#  [3 4]
#  [5 6]]

reshape(2, -1)では、行数を2に固定し、列数を6 ÷ 2 = 3と計算します。

reshape(-1, 2)では、列数を2に固定し、行数を6 ÷ 2 = 3と計算します。

-1を指定できるのは1か所だけです。また、要素数6に対してreshape(4, -1)とするなど、整数のサイズに決められない指定もエラーになります。

1次元配列に戻す

reshape(-1)とすると、全要素を1次元に並べられます。

b = np.array([[1, 2, 3], [4, 5, 6]])
print(b.reshape(-1))

# 実行結果:
# [1 2 3 4 5 6]

この記事ではorderを指定しない標準の書き方を使っています。2次元配列では、各行を左から右へ、上の行から順に読む並びです。

.T:2次元配列の行と列を入れ替える

行と列を入れ替える操作を「転置」と呼びます。2次元配列では、配列に.Tを付けると転置できます。

b = np.array([[1, 2, 3], [4, 5, 6]])

print(b.T)
print(b.T.shape)

# 実行結果:
# [[1 4]
#  [2 5]
#  [3 6]]
# (3, 2)

元の1行目[1, 2, 3]が1列目に、2行目[4, 5, 6]が2列目になります。形状は(2, 3)から(3, 2)に変わります。

.Tは属性なので、後ろに()を付けません。np.transpose(b)と書いても、この2次元配列では同じ結果になります。

reshape()と転置は値の並び方が異なる

次の2つは、どちらも3行2列になりますが、結果は異なります。

b = np.array([[1, 2, 3], [4, 5, 6]])

print(b.reshape(3, 2))
print(b.T)

# 実行結果:
# [[1 2]
#  [3 4]
#  [5 6]]
# [[1 4]
#  [2 5]
#  [3 6]]

reshape(3, 2)は、標準の順番で読み出した1, 2, 3, 4, 5, 6を2つずつ区切って配置します。

一方、転置では行と列の位置関係を入れ替えます。形状が同じだからといって、同じデータの配置になるとは限りません。

1次元配列に.Tを付けても縦向きにならない

1次元配列は、行と列の2つの軸を持っていません。そのため、.Tを付けても形状は変わりません。

v = np.array([10, 20, 30])

print(v.T.shape)
print(v.reshape(-1, 1))
print(v.reshape(-1, 1).shape)

# 実行結果:
# (3,)
# [[10]
#  [20]
#  [30]]
# (3, 1)

3行1列にしたい場合は、reshape(-1, 1)で列数を1に固定します。1行3列にしたい場合は、reshape(1, -1)です。

np.concatenate():配列を結合する

np.concatenate()は、複数の配列を、指定した既存の軸に沿って結合する関数です。

np.concatenate((配列1, 配列2), axis=結合する軸)

結合したい配列は、(a, b)のようにタプルにまとめて渡します。[a, b]のようにリストで渡すこともできます。

1次元配列をつなげる

x = np.array([1, 2, 3])
y = np.array([4, 5, 6])

print(np.concatenate((x, y)))

# 実行結果:
# [1 2 3 4 5 6]

xの後ろにyの要素が並びます。axisの標準値は0で、1次元配列にはこの軸だけがあります。

axis=0:2次元配列の行を増やす

2次元配列でaxis=0を指定すると、行の軸に沿って結合します。

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
joined = np.concatenate((a, b), axis=0)

print(joined)
print(joined.shape)

# 実行結果:
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]
# (4, 2)

2行2列の配列を上下に結合して、4行2列にしています。この場合は、列数を一致させる必要があります。行数は異なっていても結合できます。

axis=1:2次元配列の列を増やす

axis=1では、列の軸に沿って結合します。

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
joined = np.concatenate((a, b), axis=1)

print(joined)
print(joined.shape)

# 実行結果:
# [[1 2 5 6]
#  [3 4 7 8]]
# (2, 4)

今度は2行4列になりました。この場合は、行数を一致させる必要があります。列数は異なっていても結合できます。

指定2次元配列での結合一致が必要なサイズ
axis=0上下に結合して行数を増やす列数
axis=1左右に結合して列数を増やす行数

一般には、結合する軸以外のサイズと次元数を一致させます。集計では指定した軸をまとめましたが、結合では指定した軸を伸ばす、と考えると整理しやすいですね。

形状変更・結合の注意点

reshape()や転置の結果は、元のデータを共有する場合がある

reshape()は、可能ならビューを返し、必要な場合にコピーを作ります。通常のNumPy配列の転置も、元データを共有するビューです。

そのため、結果の要素を書き換えると、元の配列にも変更が反映される場合があります。独立させたい場合はcopy()を付けます。

a = np.array([1, 2, 3, 4, 5, 6])
b = a.reshape(2, 3).copy()
b[0, 0] = 99

print(a)
print(b)

# 実行結果:
# [1 2 3 4 5 6]
# [[99  2  3]
#  [ 4  5  6]]

reshape()を呼んだだけでは元の配列の形状は変わりませんが、データを共有しているかどうかは別の話です。この2つを区別しましょう。

列を追加する場合は1次元と2次元の違いに注意する

例えば、2行3列の配列にnp.array([100, 200])を1列として追加したい場合、そのままでは次元数が合いません。

data = np.array([[1, 2, 3], [4, 5, 6]])
extra = np.array([100, 200]).reshape(-1, 1)

print(np.concatenate((data, extra), axis=1))

# 実行結果:
# [[  1   2   3 100]
#  [  4   5   6 200]]

追加する配列を(2, 1)に変えてから、列の軸で結合しています。

なお、concatenate()は既存の軸に沿う結合です。新しい軸を追加するnp.stack()とは役割が異なります。

NumPyを基礎から学びたい方へ

形状を整えられるようになると、データの結合や、異なる形状の配列を使った計算を扱いやすくなります。

NumPyの基本操作を体系的に学びたい方向けに、Kindle書籍『機械学習・データ分析のためのNumPy入門 ― 配列操作と数値計算の基本をPythonで学ぶ ―』を執筆しました。

配列の作成からデータの取り出し、条件抽出、集計、ブロードキャストなどを、コード例と演習問題を通じて学べます。

「必要な形状を自分で考えてコードを書きたい」「機械学習・データ分析のためにNumPyの基礎を整理したい」という方は、ぜひチェックしてみてください!

まとめ

今回は、配列の形状変更と結合について解説しました。

書き方用途・特徴
a.reshape(2, 3)要素数6の配列を2行3列にする
a.reshape(2, -1)行数2を指定し、列数を自動で決める
a.reshape(-1)1次元に並べる
a.T2次元配列の行と列を入れ替える
v.reshape(-1, 1)1次元配列を1列の2次元配列にする
np.concatenate((a, b), axis=0)2次元配列を上下に結合する
np.concatenate((a, b), axis=1)2次元配列を左右に結合する

形状変更では要素数、結合では結合する軸以外のサイズを確認することがポイントです。

まずは同じ配列でreshape()と.Tを試し、形状だけでなく、値の位置がどう変わるかを確認してみてください!

タイトルとURLをコピーしました