NumPy配列の結合方法まとめ

python

NumPyを使ったデータ処理では、「複数の配列をまとめてひとつにしたい」場面がよくあります。

例えば、次のようなケースです:

  • 2つの測定データを横に並べて分析したい
  • 複数の画像データを縦方向に積み上げたい
  • データとラベルを1つの行列に結合したい

この記事では、NumPy配列を結合するための主要関数と、それぞれの使い分け・注意点をわかりやすく説明します。

スポンサーリンク

配列結合の基本概念

軸(axis)の理解

まず、結合を理解するために「軸(axis)」の概念を確認しましょう。

import numpy as np

# 2次元配列の例
arr = np.array([[1, 2, 3],
                [4, 5, 6]])
print(f"配列の形状: {arr.shape}")  # 結果: (2, 3)
print(f"axis=0の方向: 行方向(縦)、サイズ = {arr.shape[0]}")  # 2
print(f"axis=1の方向: 列方向(横)、サイズ = {arr.shape[1]}")  # 3

視覚的な理解:

    axis=1(横方向)→
    [1, 2, 3]
a   [4, 5, 6]
x
i
s
=
0
(
縦
方
向
)
↓

np.concatenate():基本的な結合関数

基本構文

np.concatenate((配列1, 配列2), axis=軸)
  • axis=0:縦に結合(行を増やす)
  • axis=1:横に結合(列を増やす)

例1:縦方向に結合(axis=0)

import numpy as np

# 2つの配列を準備
a = np.array([[1, 2], 
              [3, 4]])
b = np.array([[5, 6]])

print("配列a:")
print(a)
print(f"形状: {a.shape}")  # 結果: (2, 2)

print("配列b:")
print(b)
print(f"形状: {b.shape}")  # 結果: (1, 2)

# 縦方向に結合
result = np.concatenate((a, b), axis=0)
print("縦方向結合の結果:")
print(result)
# 結果:
# [[1 2]
#  [3 4]
#  [5 6]]
print(f"結合後の形状: {result.shape}")  # 結果: (3, 2)

図解:

[1 2]     [5 6]     [1 2]
[3 4]  +    ↓    =  [3 4]
  ↓                 [5 6]

例2:横方向に結合(axis=1)

import numpy as np

a = np.array([[1, 2], 
              [3, 4]])
c = np.array([[7], 
              [8]])

print("配列a:")
print(a)
print(f"形状: {a.shape}")  # 結果: (2, 2)

print("配列c:")
print(c)
print(f"形状: {c.shape}")  # 結果: (2, 1)

# 横方向に結合
result = np.concatenate((a, c), axis=1)
print("横方向結合の結果:")
print(result)
# 結果:
# [[1 2 7]
#  [3 4 8]]
print(f"結合後の形状: {result.shape}")  # 結果: (2, 3)

図解:

[1 2] + [7] = [1 2 7]
[3 4]   [8]   [3 4 8]
  →      →

重要な注意点:形状の一致

import numpy as np

# 形状が合わない例
a = np.array([[1, 2], [3, 4]])     # 形状: (2, 2)
d = np.array([[5, 6, 7]])          # 形状: (1, 3)

print(f"配列aの形状: {a.shape}")
print(f"配列dの形状: {d.shape}")

try:
    # 縦方向結合を試すとエラー
    result = np.concatenate((a, d), axis=0)
except ValueError as e:
    print(f"エラー: {e}")
    print("axis=0で結合するには、列数(axis=1のサイズ)が同じである必要があります")

try:
    # 横方向結合を試すとエラー
    result = np.concatenate((a, d), axis=1)
except ValueError as e:
    print(f"エラー: {e}")
    print("axis=1で結合するには、行数(axis=0のサイズ)が同じである必要があります")

複数の配列を一度に結合

import numpy as np

# 3つの配列を一度に結合
arr1 = np.array([[1, 2]])
arr2 = np.array([[3, 4]])
arr3 = np.array([[5, 6]])

# 複数配列の縦方向結合
result = np.concatenate((arr1, arr2, arr3), axis=0)
print("3つの配列を縦方向に結合:")
print(result)
# 結果:
# [[1 2]
#  [3 4]
#  [5 6]]

まとめ

  • concatenate()最も汎用的な結合方法
  • 軸(axis)と形状をきちんと揃える必要がある

np.vstack() / np.hstack():軸を意識せず簡単に使える

vstack():縦に積む(vertical stack)

import numpy as np

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

# vstackを使用(axis=0のconcatenateと同じ)
result_vstack = np.vstack((a, b))
print("vstackの結果:")
print(result_vstack)
# 結果:
# [[1 2]
#  [3 4]
#  [5 6]]

# concatenateと同じ結果
result_concat = np.concatenate((a, b), axis=0)
print("concatenate(axis=0)と同じ:", np.array_equal(result_vstack, result_concat))

hstack():横に積む(horizontal stack)

import numpy as np

a = np.array([[1, 2], [3, 4]])
c = np.array([[7], [8]])

# hstackを使用(axis=1のconcatenateと同じ)
result_hstack = np.hstack((a, c))
print("hstackの結果:")
print(result_hstack)
# 結果:
# [[1 2 7]
#  [3 4 8]]

# concatenateと同じ結果
result_concat = np.concatenate((a, c), axis=1)
print("concatenate(axis=1)と同じ:", np.array_equal(result_hstack, result_concat))

1次元配列での使用例

import numpy as np

# 1次元配列の場合
vec1 = np.array([1, 2, 3])
vec2 = np.array([4, 5, 6])

print("1次元配列:")
print(f"vec1: {vec1}, 形状: {vec1.shape}")
print(f"vec2: {vec2}, 形状: {vec2.shape}")

# vstackで縦に積む
vertical = np.vstack((vec1, vec2))
print("vstack結果:")
print(vertical)
# 結果:
# [[1 2 3]
#  [4 5 6]]
print(f"形状: {vertical.shape}")  # 結果: (2, 3)

# hstackで横に連結
horizontal = np.hstack((vec1, vec2))
print("hstack結果:")
print(horizontal)  # 結果: [1 2 3 4 5 6]
print(f"形状: {horizontal.shape}")  # 結果: (6,)

関数の特徴と制限

関数結合方向条件使いやすさ
vstack()axis=0(縦)列数が同じであること
hstack()axis=1(横)行数が同じであること
concatenate()指定可能指定軸以外の形状が同じ中(柔軟性高)

まとめ

  • vstack/hstack簡単に使えるショートカット関数
  • よく使う縦・横結合を直感的に表現

np.stack():新たな次元で結合したいとき

基本概念

stack()は、同じ形状の配列を新しい次元で結合する関数です。

import numpy as np

# 同じ形状の2つの配列
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

print("配列a:")
print(a)
print(f"形状: {a.shape}")  # 結果: (2, 2)

print("配列b:")
print(b)
print(f"形状: {b.shape}")  # 結果: (2, 2)

axis=0での結合(新しい軸を先頭に追加)

# 新しい次元で結合
result = np.stack((a, b), axis=0)
print("stack(axis=0)の結果:")
print(result)
# 結果:
# [[[1 2]
#   [3 4]]
#  [[5 6]
#   [7 8]]]
print(f"結合後の形状: {result.shape}")  # 結果: (2, 2, 2)

# アクセス方法
print(f"1つ目の配列: \n{result[0]}")  # aと同じ
print(f"2つ目の配列: \n{result[1]}")  # bと同じ

axis=1での結合(中間に新しい軸を追加)

result_axis1 = np.stack((a, b), axis=1)
print("stack(axis=1)の結果:")
print(result_axis1)
# 結果:
# [[[1 2]
#   [5 6]]
#  [[3 4]
#   [7 8]]]
print(f"結合後の形状: {result_axis1.shape}")  # 結果: (2, 2, 2)

axis=-1での結合(末尾に新しい軸を追加)

result_axis_last = np.stack((a, b), axis=-1)
print("stack(axis=-1)の結果:")
print(result_axis_last)
# 結果:
# [[[1 5]
#   [2 6]]
#  [[3 7]
#   [4 8]]]
print(f"結合後の形状: {result_axis_last.shape}")  # 結果: (2, 2, 2)

実用例1:画像データのバッチ化

import numpy as np

# 3枚の28×28画像
image1 = np.random.rand(28, 28)
image2 = np.random.rand(28, 28)
image3 = np.random.rand(28, 28)

print(f"各画像の形状: {image1.shape}")  # 結果: (28, 28)

# バッチとして結合
batch_images = np.stack((image1, image2, image3), axis=0)
print(f"バッチ化後の形状: {batch_images.shape}")  # 結果: (3, 28, 28)

# 機械学習で使いやすい形式
print("バッチの各画像にアクセス:")
print(f"1枚目の形状: {batch_images[0].shape}")
print(f"2枚目の形状: {batch_images[1].shape}")
print(f"3枚目の形状: {batch_images[2].shape}")

実用例2:時系列データのチャンネル追加

import numpy as np

# 3つの異なるセンサーの時系列データ
sensor1 = np.random.randn(100)  # 100時点のデータ
sensor2 = np.random.randn(100)
sensor3 = np.random.randn(100)

print(f"各センサーデータの形状: {sensor1.shape}")  # 結果: (100,)

# チャンネル次元で結合
multi_channel = np.stack((sensor1, sensor2, sensor3), axis=1)
print(f"マルチチャンネルデータの形状: {multi_channel.shape}")  # 結果: (100, 3)

# 時系列分析で使いやすい形式(時刻×チャンネル)
print("各時刻のデータ例:")
print(f"時刻0: {multi_channel[0]}")  # 3つのセンサーの値
print(f"時刻1: {multi_channel[1]}")

注意:形状が完全一致している必要がある

import numpy as np

# 形状が異なる配列
arr1 = np.array([[1, 2], [3, 4]])      # 形状: (2, 2)
arr2 = np.array([[5, 6, 7], [8, 9, 10]])  # 形状: (2, 3)

try:
    result = np.stack((arr1, arr2), axis=0)
except ValueError as e:
    print(f"エラー: {e}")
    print("stackは完全に同じ形状の配列でないと使用できません")

まとめ

  • stack()は「次元を増やして結合」したいときに使う
  • 全ての配列の形状が完全一致している必要がある
  • バッチ処理やマルチチャンネルデータの作成に便利

形が合わないときの対処法

問題:形状の不一致

import numpy as np

# 形状が合わない配列の例
x = np.array([1, 2, 3])        # 形状: (3,)
y = np.array([[4], [5], [6]])  # 形状: (3, 1)

print(f"配列x: {x}, 形状: {x.shape}")
print(f"配列y: \n{y}\n形状: {y.shape}")

try:
    result = np.hstack((x, y))
except ValueError as e:
    print(f"エラー: {e}")
    print("形状が合わないため結合できません")

解決方法1:reshapeやnewaxisで形を揃える

import numpy as np

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

# 方法1: reshapeを使用
x_reshaped = x.reshape(-1, 1)  # (3,) → (3, 1)
print(f"reshapeしたx: \n{x_reshaped}\n形状: {x_reshaped.shape}")

result1 = np.hstack((x_reshaped, y))
print(f"結合結果1: \n{result1}")

# 方法2: newaxisを使用
x_newaxis = x[:, np.newaxis]  # (3,) → (3, 1)
print(f"newaxisを使ったx: \n{x_newaxis}\n形状: {x_newaxis.shape}")

result2 = np.hstack((x_newaxis, y))
print(f"結合結果2: \n{result2}")

# 結果は同じ
print(f"結果が同じ: {np.array_equal(result1, result2)}")

解決方法2:broadcast可能な形状に調整

import numpy as np

# 異なるサイズの配列
a = np.array([1, 2, 3])     # 形状: (3,)
b = np.array([4, 5])        # 形状: (2,)

print(f"配列a: {a}")
print(f"配列b: {b}")

# tileを使って同じ長さに揃える
# bを3つに拡張
b_extended = np.tile(b, (2,))[:3]  # [4, 5, 4]の最初の3要素
print(f"拡張したb: {b_extended}")

# 結合
result = np.vstack((a, b_extended))
print(f"結合結果: \n{result}")

解決方法3:パディング(ゼロ埋め)

import numpy as np

# 長さが異なる配列
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5])

# 短い方にゼロを追加
max_length = max(len(arr1), len(arr2))
arr1_padded = np.pad(arr1, (0, max_length - len(arr1)), constant_values=0)
arr2_padded = np.pad(arr2, (0, max_length - len(arr2)), constant_values=0)

print(f"パディング後のarr1: {arr1_padded}")
print(f"パディング後のarr2: {arr2_padded}")

# 結合
result = np.vstack((arr1_padded, arr2_padded))
print(f"結合結果: \n{result}")

実用的な形状調整関数

import numpy as np

def smart_concatenate(arrays, axis=0, fill_value=0):
    """
    形状を自動調整して配列を結合する関数
    """
    if axis == 0:  # 縦方向結合
        # 最大列数に合わせる
        max_cols = max(arr.shape[-1] if arr.ndim > 1 else 1 for arr in arrays)
        adjusted_arrays = []
        
        for arr in arrays:
            if arr.ndim == 1:
                arr = arr.reshape(1, -1)
            
            if arr.shape[1] < max_cols:
                padding = max_cols - arr.shape[1]
                arr = np.pad(arr, ((0, 0), (0, padding)), constant_values=fill_value)
            
            adjusted_arrays.append(arr)
        
        return np.concatenate(adjusted_arrays, axis=0)
    
    elif axis == 1:  # 横方向結合
        # 最大行数に合わせる
        max_rows = max(arr.shape[0] if arr.ndim > 1 else len(arr) for arr in arrays)
        adjusted_arrays = []
        
        for arr in arrays:
            if arr.ndim == 1:
                arr = arr.reshape(-1, 1)
            
            if arr.shape[0] < max_rows:
                padding = max_rows - arr.shape[0]
                arr = np.pad(arr, ((0, padding), (0, 0)), constant_values=fill_value)
            
            adjusted_arrays.append(arr)
        
        return np.concatenate(adjusted_arrays, axis=1)

# 使用例
arr1 = np.array([1, 2, 3])
arr2 = np.array([[4, 5], [6, 7]])
arr3 = np.array([8])

result = smart_concatenate([arr1, arr2, arr3], axis=0, fill_value=0)
print("スマート結合の結果:")
print(result)

その他の便利な結合関数

np.r_[]とnp.c_[]:簡潔な記法

import numpy as np

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

# r_[]:行方向(縦)の結合
result_r = np.r_[a, b]
print(f"r_[]での結合: {result_r}")  # 結果: [1 2 3 4 5 6]

# c_[]:列方向の結合(2次元化)
result_c = np.c_[a, b]
print(f"c_[]での結合: \n{result_c}")
# 結果:
# [[1 4]
#  [2 5]
#  [3 6]]

np.append():要素やサブ配列の追加

import numpy as np

# 基本的な使用
arr = np.array([1, 2, 3])
result = np.append(arr, [4, 5])
print(f"append結果: {result}")  # 結果: [1 2 3 4 5]

# 2次元配列での使用
matrix = np.array([[1, 2], [3, 4]])
new_row = np.array([[5, 6]])

# 行を追加
result_row = np.append(matrix, new_row, axis=0)
print(f"行追加: \n{result_row}")

# 列を追加
new_col = np.array([[7], [8]])
result_col = np.append(matrix, new_col, axis=1)
print(f"列追加: \n{result_col}")

実用的な使用例

例1:データ分析での結合

import numpy as np

# 異なる実験条件のデータ
condition_A = np.random.randn(50, 3)  # 50サンプル、3特徴量
condition_B = np.random.randn(75, 3)  # 75サンプル、3特徴量
condition_C = np.random.randn(25, 3)  # 25サンプル、3特徴量

print(f"条件A: {condition_A.shape}")
print(f"条件B: {condition_B.shape}")
print(f"条件C: {condition_C.shape}")

# 全データを結合
all_data = np.vstack((condition_A, condition_B, condition_C))
print(f"結合後のデータ: {all_data.shape}")  # 結果: (150, 3)

# ラベルも作成
labels_A = np.full(50, 0)  # 条件Aは0
labels_B = np.full(75, 1)  # 条件Bは1
labels_C = np.full(25, 2)  # 条件Cは2

all_labels = np.hstack((labels_A, labels_B, labels_C))
print(f"ラベル: {all_labels.shape}")  # 結果: (150,)

# データとラベルを横に結合
dataset = np.c_[all_data, all_labels]
print(f"最終データセット: {dataset.shape}")  # 結果: (150, 4)

例2:画像処理での結合

import numpy as np

# 異なるサイズの画像を処理
image1 = np.random.rand(64, 64)     # 64×64
image2 = np.random.rand(64, 128)    # 64×128
image3 = np.random.rand(64, 32)     # 64×32

print(f"画像1: {image1.shape}")
print(f"画像2: {image2.shape}")
print(f"画像3: {image3.shape}")

# 横に並べて表示用に結合
combined_width = image1.shape[1] + image2.shape[1] + image3.shape[1]
combined_image = np.zeros((64, combined_width))

# 順番に配置
start_col = 0
combined_image[:, start_col:start_col + image1.shape[1]] = image1
start_col += image1.shape[1]

combined_image[:, start_col:start_col + image2.shape[1]] = image2
start_col += image2.shape[1]

combined_image[:, start_col:start_col + image3.shape[1]] = image3

print(f"結合画像: {combined_image.shape}")  # 結果: (64, 224)

例3:時系列データの結合

import numpy as np

# 異なる期間の時系列データ
daily_data = np.random.randn(365, 4)    # 1年間の日次データ(4変数)
hourly_data = np.random.randn(24, 4)    # 1日分の時間データ(4変数)

print(f"日次データ: {daily_data.shape}")
print(f"時間データ: {hourly_data.shape}")

# 日次データに時間データを追加
extended_data = np.vstack((daily_data, hourly_data))
print(f"拡張データ: {extended_data.shape}")  # 結果: (389, 4)

# 新しい特徴量を追加
temperature = np.random.randn(389, 1)
humidity = np.random.randn(389, 1)

# 横方向に特徴量を追加
full_dataset = np.hstack((extended_data, temperature, humidity))
print(f"完全データセット: {full_dataset.shape}")  # 結果: (389, 6)

結合関数のまとめ表

関数特徴注意点よく使う場面
concatenate()最も汎用的、軸指定可能axis指定・形状を揃える柔軟な結合が必要
vstack()縦方向結合、直感的列数を揃えるデータの追加
hstack()横方向結合、直感的行数を揃える特徴量の追加
stack()新次元で結合全体形状が完全一致バッチ化、チャンネル結合
append()要素追加、配列追加新しい配列を返す動的なデータ追加
r_[]行方向の簡潔記法1次元のみ簡単な連結
c_[]列方向の簡潔記法自動的に2次元化列ベクトルの結合

まとめ

結合したい形おすすめ関数備考
単純な上下連結vstack() または concatenate(axis=0)列数を揃える
横方向に連結hstack() または concatenate(axis=1)行数を揃える
次元を増やしたいstack()完全同一形状が必要
柔軟な結合concatenate()axisで方向指定
簡単な連結r_[] または c_[]記法が簡潔

コメント

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