メインコンテンツへスキップ

3.2.5 配列の変形と操作

NumPy 変形と軸操作の図

学習目標

  • reshape、flatten、ravel などの変形操作を身につける
  • 配列の結合(concatenate、stack、hstack、vstack)を学ぶ
  • 配列の分割(split、hsplit、vsplit)を学ぶ
  • 転置と軸の入れ替えを理解する

reshape:形状を変える

reshape は最もよく使う変形操作です。データを変えずに配列の形状だけを変えます。

基本的な使い方

import numpy as np

arr = np.arange(12) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(arr.shape) # (12,)

# 3 行 4 列にする
m1 = arr.reshape(3, 4)
print(m1)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]

# 4 行 3 列にする
m2 = arr.reshape(4, 3)
print(m2)
# [[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]

# 2×2×3 の3次元配列にする
m3 = arr.reshape(2, 2, 3)
print(m3)
# [[[ 0 1 2]
# [ 3 4 5]]
# [[ 6 7 8]
# [ 9 10 11]]]
要素数は必ず一致する

reshape の前後で要素数は同じでなければなりません。違うとエラーになります。

arr = np.arange(12)    # 12 個の要素
arr.reshape(3, 5) # ❌ エラー!3 × 5 = 15 ≠ 12
arr.reshape(3, 4) # ✅ 3 × 4 = 12

-1 を使って自動計算する

-1 は「NumPy にこの次元を自動計算してもらう」という意味です。

arr = np.arange(12)

# 3 行にしたい。列数は自動で計算してほしい
m1 = arr.reshape(3, -1) # 4 列になる
print(m1.shape) # (3, 4)

# 4 列にしたい。行数は自動で計算してほしい
m2 = arr.reshape(-1, 4) # 3 行になる
print(m2.shape) # (3, 4)

# 1 列にする(列ベクトル)
col = arr.reshape(-1, 1)
print(col.shape) # (12, 1)
-1 は1回だけ使える

reshape では、-1 にできる次元は1つだけです。未知の値が1つだけなら計算できるからです。

arr.reshape(-1, -1)  # ❌ エラー!-1 は2つ使えない

flatten と ravel:配列を平らにする

多次元配列を1次元に戻します。

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

# flatten:コピーを返す(変更しても元の配列に影響しない)
flat = matrix.flatten()
print(flat) # [1 2 3 4 5 6]
flat[0] = 99
print(matrix[0, 0]) # 1 ← 元の配列は変わらない

# ravel:ビューを返す(変更すると元の配列にも影響する)
rav = matrix.ravel()
print(rav) # [1 2 3 4 5 6]
rav[0] = 99
print(matrix[0, 0]) # 99 ← 元の配列も変わる!
方法戻り値の種類変更が元の配列に影響するか速度
flatten()コピーしないやや遅い(データをコピーする)
ravel()ビューするやや速い(コピーしない)
reshape(-1)ビューするやや速い

配列の結合

concatenate:汎用的な結合

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

# 1次元の結合
c = np.concatenate([a, b])
print(c) # [1 2 3 4 5 6]

2次元配列を結合するときは、方向(axis)を指定します。

m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[5, 6], [7, 8]])

# axis=0:上下に結合(行数が増える)
v = np.concatenate([m1, m2], axis=0)
print(v)
# [[1 2]
# [3 4]
# [5 6]
# [7 8]]

# axis=1:左右に結合(列数が増える)
h = np.concatenate([m1, m2], axis=1)
print(h)
# [[1 2 5 6]
# [3 4 7 8]]

vstack と hstack:手軽な結合

m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[5, 6], [7, 8]])

# vstack = vertical stack = 上下に結合 = concatenate(axis=0)
print(np.vstack([m1, m2]))
# [[1 2]
# [3 4]
# [5 6]
# [7 8]]

# hstack = horizontal stack = 左右に結合 = concatenate(axis=1)
print(np.hstack([m1, m2]))
# [[1 2 5 6]
# [3 4 7 8]]

stack:新しい次元を作る

stackconcatenate の違いは、stack次元を1つ増やすことです。

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

# stack で新しい次元に積む
s0 = np.stack([a, b], axis=0) # 「横に並べる」イメージ
print(s0)
# [[1 2 3]
# [4 5 6]]
print(s0.shape) # (2, 3)

s1 = np.stack([a, b], axis=1) # 「縦に並べる」イメージ
print(s1)
# [[1 4]
# [2 5]
# [3 6]]
print(s1.shape) # (3, 2)

結合方法のまとめ

関数役割次元の変化
np.concatenate()既存の軸に沿って結合次元は変わらず、どこかの軸が長くなる
np.vstack()上下に結合行数が増える
np.hstack()左右に結合列数が増える
np.stack()新しい軸に沿って積む次元が1つ増える

配列の分割

split:均等に分割する

arr = np.arange(12)   # [ 0  1  2  3  4  5  6  7  8  9 10 11]

# 3 つに均等分割
parts = np.split(arr, 3)
print(parts[0]) # [0 1 2 3]
print(parts[1]) # [4 5 6 7]
print(parts[2]) # [8 9 10 11]

# 指定した位置で分割
parts2 = np.split(arr, [3, 7]) # インデックス 3 と 7 で切る
print(parts2[0]) # [0 1 2]
print(parts2[1]) # [3 4 5 6]
print(parts2[2]) # [7 8 9 10 11]

2次元の分割

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

# vsplit:上下に分割
top, bottom = np.vsplit(matrix, 2)
print(top)
# [[0 1 2 3]
# [4 5 6 7]]

# hsplit:左右に分割
left, right = np.hsplit(matrix, 2)
print(left)
# [[ 0 1]
# [ 4 5]
# [ 8 9]
# [12 13]]

転置と軸の入れ替え

2次元の転置

転置は、行を列に、列を行に変えることです。

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

# 転置
t = matrix.T
print(t)
# [[1 4]
# [2 5]
# [3 6]]
print(t.shape) # (3, 2)

# transpose でもできる
t2 = matrix.transpose()
print(np.array_equal(t, t2)) # True

次元を追加する:np.newaxis と expand_dims

配列に次元を1つ追加したいことがあります。たとえば、行ベクトルを列ベクトルにしたいときです。

arr = np.array([1, 2, 3])      # shape: (3,)

# 方法 1:np.newaxis
row = arr[np.newaxis, :] # shape: (1, 3) 行ベクトル
col = arr[:, np.newaxis] # shape: (3, 1) 列ベクトル
print(row) # [[1 2 3]]
print(col)
# [[1]
# [2]
# [3]]

# 方法 2:np.expand_dims
row2 = np.expand_dims(arr, axis=0) # axis=0 に次元を追加 → (1, 3)
col2 = np.expand_dims(arr, axis=1) # axis=1 に次元を追加 → (3, 1)

# 方法 3:reshape
row3 = arr.reshape(1, -1) # (1, 3)
col3 = arr.reshape(-1, 1) # (3, 1)

次元を減らす:squeeze

サイズが 1 の次元を取り除きます。

arr = np.array([[[1, 2, 3]]])
print(arr.shape) # (1, 1, 3)

squeezed = arr.squeeze()
print(squeezed.shape) # (3,)
print(squeezed) # [1 2 3]

実践:データの再構成

import numpy as np

# 例:12か月分の売上データ(1次元)
monthly_sales = np.array([
120, 135, 150, 180, 200, 210,
195, 188, 220, 250, 280, 310
])

# 4 四半期 × 3 か月に再構成
quarterly = monthly_sales.reshape(4, 3)
print("四半期データ:")
print(quarterly)
# [[120 135 150] Q1
# [180 200 210] Q2
# [195 188 220] Q3
# [250 280 310]] Q4

# 各四半期の売上合計
q_totals = quarterly.sum(axis=1)
quarters = ["Q1", "Q2", "Q3", "Q4"]
for q, total in zip(quarters, q_totals):
print(f" {q}: {total}")

# 前半と後半
first_half, second_half = np.vsplit(quarterly, 2)
print(f"\n前半の合計: {first_half.sum()}")
print(f"後半の合計: {second_half.sum()}")

まとめ

操作関数説明
形状を変えるreshape()要素数は変えずに、次元の並びを変える
平らにするflatten() / ravel()多次元を1次元にする
結合するconcatenate() / vstack() / hstack()複数の配列をまとめる
積むstack()まとめて、さらに1次元増やす
分割するsplit() / vsplit() / hsplit()1つの配列を複数に分ける
転置する.T / transpose()行と列を入れ替える
次元を増やすnp.newaxis / expand_dims()size=1 の次元を追加する
次元を減らすsqueeze()size=1 の次元を取り除く

やってみよう

練習 1:reshape の練習

arr = np.arange(24)

# 1. 4×6 の行列にする
# 2. 2×3×4 の3次元配列にする
# 3. 6 行にする(列数は自動計算)
# 4. (2,3,4) の配列を1次元に戻す

練習 2:結合と分割

# 3 クラス分の成績データ
class_a = np.array([[85, 90], [78, 82], [92, 88]]) # 3 人 × 2 科目
class_b = np.array([[76, 80], [95, 91], [83, 87]]) # 3 人 × 2 科目
class_c = np.array([[88, 92], [71, 75], [90, 85]]) # 3 人 × 2 科目

# 1. 3 クラスの成績を 9×2 の行列にまとめる
# 2. 3 科目目の点数を追加したいとき、どう結合する?
extra_scores = np.array([[70], [65], [80], [75], [90], [85], [78], [72], [88]])
# 3. まとめた 9×3 の行列を、3 人ずつに分割して 3 グループに戻す

練習 3:データの再構成

# 1年365日の気温データ(ダミーデータ)
rng = np.random.default_rng(seed=42)
daily_temps = rng.uniform(low=-5, high=38, size=360) # 分割しやすいように360日を使う

# 1. 12 か月 × 30 日に再構成する
# 2. 各月の平均気温を計算する
# 3. 最も暑い月と最も寒い月を見つける
# 4. 前半と後半の平均気温の差を計算する