MATLAB 配列の基本と要素の追加方法【初心者向けガイド】
はじめに:MATLABと配列の重要性
MATLAB(Matrix Laboratory)は、数値計算、データ解析、アルゴリズム開発、モデル作成、シミュレーション、グラフィックス表示など、幅広い技術計算タスクに特化した高性能なプログラミング言語およびインタラクティブ環境です。その名前が示す通り、「行列(Matrix)」がMATLABの基本的なデータ構造であり、その強力な演算機能の基盤となっています。
MATLABにおいて、データはほとんどの場合「配列」として扱われます。スカラー(単一の数値)も、実際には1×1の配列として内部で処理されます。ベクトル(1次元の数値リスト)や行列(2次元の数値グリッド)はもちろんのこと、画像、音声、時系列データ、さらには異なるデータ型を格納するセル配列や構造体配列に至るまで、MATLABで扱うあらゆる情報は配列の形式で表現されます。
配列を理解し、効率的に操作することは、MATLABを使いこなす上で最も重要なスキルのひとつです。データの格納、アクセス、加工、演算のすべてが配列操作に基づいているため、配列の概念をマスターすることは、MATLABプログラミングの効率性とパフォーマンスを決定づけます。
この記事では、MATLAB配列の基本概念から、その作成方法、柔軟なインデックス操作、そして最も重要な「配列への要素の追加」について、初心者の方にも分かりやすく、しかし詳細に解説していきます。さらに、パフォーマンスを意識した効率的なコードの書き方や、発展的な配列型についても触れ、MATLABでのデータハンドリングの基礎を盤石にすることを目指します。
第1章:MATLAB配列の基本概念
MATLABにおける配列は、同じ型のデータ要素の集まりであり、行と列の形式で整理されます。MATLABは、この配列に対する豊富な組み込み関数と強力な演算機能を提供します。
1.1 配列とは何か? スカラー、ベクトル、行列、多次元配列
配列は、データを構造的に保持するための基本的なデータ構造です。MATLABでは、配列の「次元」と「サイズ」が重要になります。
-
スカラー (Scalar)
- 定義:1つの値のみを持つ配列です。数学的な意味でのスカラーと同じです。
- サイズ:1行1列 (1×1)。
- 例:
a = 10;
- MATLAB内部では、
a
も1×1の行列として扱われます。これにより、スカラーと配列の演算がシームレスに行えます。
-
ベクトル (Vector)
- 定義:1行または1列に複数の値を持つ配列です。数学的な意味でのベクトルと同じです。
- サイズ:
- 行ベクトル (Row Vector):1行N列 (1xN)。例:
v = [1 2 3];
- 列ベクトル (Column Vector):N行1列 (Nx1)。例:
v = [1; 2; 3];
またはv = [1 2 3]';
(転置)
- 行ベクトル (Row Vector):1行N列 (1xN)。例:
- 例:
matlab
rowVec = [10, 20, 30, 40, 50]; % 1x5の行ベクトル
colVec = [1; 2; 3; 4; 5]; % 5x1の列ベクトル
-
行列 (Matrix)
- 定義:複数の行と列を持つ2次元の配列です。
- サイズ:M行N列 (MxN)。
- 例:
matlab
M = [1 2 3; 4 5 6; 7 8 9]; % 3x3の行列
% または
M = [1, 2, 3; ... % ... は行の継続を示す
4, 5, 6; ...
7, 8, 9]; - 要素はカンマ
,
またはスペースで区切られ、行はセミコロン
;
で区切られます。
-
多次元配列 (Multidimensional Array)
- 定義:2次元以上の配列です。3次元配列は、複数の行列を奥行き方向に重ねたものと考えることができます(例えば、RGB画像は縦、横、色の3次元配列)。
- サイズ:M行N列Pページ (MxNxP) など。
- 例:
matlab
% 3x3の2つのページを持つ3次元配列
D = rand(3, 3, 2); % 3行3列、2ページの乱数配列
% D(:,:,1) は1ページ目の3x3行列
% D(:,:,2) は2ページ目の3x3行列 - より高次元の配列も作成可能です。例えば、
rand(2,3,4,5)
は4次元配列になります。
1.2 MATLABにおける主要なデータ型(配列との関連)
MATLABの配列は、格納するデータの種類によって様々なデータ型を持ちます。データ型は、メモリの使用量や演算の挙動に影響します。
-
数値型 (Numeric Types)
- MATLABのデフォルトの数値型は
double
(倍精度浮動小数点数)です。ほとんどの計算はこの型で行われます。 - 浮動小数点数型:
double
:倍精度(約15-17桁の精度)。最も一般的。single
:単精度(約7桁の精度)。メモリを節約したい場合や、高速な計算が求められる場合に。
- 整数型:
int8
,int16
,int32
,int64
:それぞれ8, 16, 32, 64ビットの符号付き整数。uint8
,uint16
,uint32
,uint64
:それぞれ8, 16, 32, 64ビットの符号なし整数。- メモリ使用量を抑えたい場合や、特定のハードウェアと連携する場合に有用です。例えば、画像データはしばしば
uint8
で表現されます(0-255のピクセル値)。
- 例:
matlab
a_double = 3.14159; % double (デフォルト)
b_single = single(3.14159); % single
c_int8 = int8(120); % int8
d_uint8 = uint8(250); % uint8
- MATLABのデフォルトの数値型は
-
文字型 (Character Array –
char
)- 文字や文字列を格納します。MATLABでは、文字列は文字の配列として扱われます。
- 例:
str = 'Hello, world!';
- これは1×13の
char
配列です。 - 複数の文字列を格納する場合は、同じ長さにするか、後述のセル配列やstring型を使用します。
-
文字列型 (String Array –
string
)- R2016b以降で導入された新しい文字列データ型です。
char
配列よりも直感的に文字列を扱え、異なる長さの文字列をまとめて格納できます。 - 例:
strArray = ["apple", "banana", "cherry"];
- ダブルクォーテーション
"
で囲んで定義します。
- R2016b以降で導入された新しい文字列データ型です。
-
論理型 (Logical Array –
logical
)- 真(
true
または1
)と偽(false
または0
)のブール値を格納します。条件分岐や論理インデックスに使用されます。 - 例:
isEven = mod(num, 2) == 0;
true
は内部的に1
、false
は内部的に0
として扱われますが、算術演算に使用すると1
または0
として振る舞います。
- 真(
-
セル配列 (Cell Array –
cell
)- 異なるデータ型やサイズのデータを格納できる配列です。各要素は「セル」と呼ばれ、異なる種類のデータ(数値、文字列、別の配列、構造体など)を保持できます。
- 波括弧
{}
を使用して作成し、アクセスします。 - 例:
myCell = {10, 'Hello', [1 2 3], true};
myCell{1}
で1番目の要素にアクセスします。
-
構造体配列 (Structure Array –
struct
)- 名前付きの「フィールド」を持つデータ構造です。各フィールドは異なる型のデータを保持でき、データベースのレコードのように、関連するデータをグループ化するのに役立ちます。
- ドット
.
演算子を使用してフィールドにアクセスします。 - 例:
matlab
student.name = 'Taro';
student.age = 20;
student.grades = [85 90 78]; - 複数の構造体を配列としてまとめることもできます。
-
テーブル配列 (Table Array –
table
)- R2013b以降で導入されたデータ型で、スプレッドシートやデータベースのテーブルのように、列名を持つデータセットを格納するのに適しています。各列は異なるデータ型を持つことができます。
- 例:
matlab
Name = ["Alice"; "Bob"; "Charlie"];
Age = [25; 30; 22];
Score = [80; 92; 75];
T = table(Name, Age, Score); - データ解析において非常に強力なツールです。
これらのデータ型を理解し、適切に使い分けることで、MATLABでのデータ管理がより効率的かつ明確になります。
第2章:配列の作成方法
MATLABで配列を作成する方法はいくつかあります。目的やデータの性質に応じて最適な方法を選択することが重要です。
2.1 直接入力(手動で値を指定)
最も基本的な方法で、スカラー、ベクトル、行列を直接キーボードから入力します。
-
スカラー:単一の数値を代入します。
matlab
scalar_val = 100;
% scalar_valは1x1のdouble型配列 -
行ベクトル:要素をスペースまたはカンマで区切ります。
matab
row_vec = [1 2 3 4 5];
% または
row_vec_comma = [6, 7, 8, 9, 10];
% どちらも1x5のdouble型配列 -
列ベクトル:要素をセミコロンで区切ります。または行ベクトルを転置します。
matlab
col_vec_semicolon = [1; 2; 3; 4; 5];
% または
col_vec_transpose = [6 7 8 9 10]'; % アポストロフィ ' は転置演算子
% どちらも5x1のdouble型配列 -
行列:行はセミコロンで区切り、同じ行内の要素はスペースまたはカンマで区切ります。
matlab
myMatrix = [1 2 3; % 1行目
4 5 6; % 2行目
7 8 9]; % 3行目
% 3x3のdouble型配列
MATLABコマンドウィンドウで入力する際は、...
(3つのドット) を使用して、コマンドを次の行に継続することができます。
matlab
longMatrix = [1 2 3 4 5 ...
6 7 8 9 10];
% longMatrix は 2x5 の行列
2.2 組み込み関数による生成
MATLABは、特定のパターンを持つ配列を効率的に生成するための多くの組み込み関数を提供しています。
-
zeros(rows, cols, ...)
:すべての要素がゼロの配列を作成します。
matlab
Z1 = zeros(3); % 3x3のゼロ行列
Z2 = zeros(2, 4); % 2x4のゼロ行列
Z3 = zeros(2, 3, 2); % 2x3x2のゼロの3次元配列
Z4 = zeros(size(myMatrix)); % myMatrixと同じサイズのゼロ行列 -
ones(rows, cols, ...)
:すべての要素が1の配列を作成します。
matlab
O1 = ones(3); % 3x3の1の行列
O2 = ones(1, 5); % 1x5の1の行ベクトル -
eye(dim)
またはeye(rows, cols)
:単位行列を作成します。対角要素が1で、それ以外が0の正方行列です。
matlab
E1 = eye(4); % 4x4の単位行列
E2 = eye(2, 3); % 2x3の長方形単位行列 (左上から対角要素が1) -
rand(rows, cols, ...)
:0から1の間の一様分布の乱数を持つ配列を作成します。
matlab
R1 = rand(5); % 5x5の一様乱数行列
R2 = rand(1, 10); % 1x10の一様乱数行ベクトル -
randn(rows, cols, ...)
:標準正規分布(平均0、標準偏差1)に従う乱数を持つ配列を作成します。
matlab
RN1 = randn(3, 3); % 3x3の正規分布乱数行列 -
linspace(start, end, num)
:指定された開始値と終了値の間で、指定された数の等間隔な点を生成する行ベクトルを作成します。
matlab
% 0から10までを11個の等間隔な点で分割
L1 = linspace(0, 10, 11); % [0 1 2 3 4 5 6 7 8 9 10]
% デフォルトは100点
L2 = linspace(0, 1); % 0から1までを100個の等間隔な点で分割 -
logspace(start_exp, end_exp, num)
:指定された指数(10のべき乗)の範囲で、指定された数の等比数列を生成する行ベクトルを作成します。
matlab
% 10^0から10^3までを4個の等比数列で分割
LG1 = logspace(0, 3, 4); % [1 10 100 1000] -
コロン演算子 (
:
):シーケンスや範囲を生成する非常に強力な演算子です。start:end
:開始値から終了値まで、1ずつ増えるシーケンス。start:step:end
:開始値から終了値まで、指定されたステップで増えるシーケンス。
matlab
Seq1 = 1:5; % [1 2 3 4 5]
Seq2 = 1:2:10; % [1 3 5 7 9] (ステップ2)
Seq3 = 10:-1:1; % [10 9 8 7 6 5 4 3 2 1] (逆順)
コロン演算子は、後述のインデックス操作でも非常に重要になります。
2.3 既存の配列からの派生
既存の配列を利用して新しい配列を作成することも一般的です。
-
結合(Concatenation):複数の配列を結合して新しい配列を作成します。
- 水平結合 (
[A B]
,[A, B]
):同じ行数の配列を横に連結します。
matlab
A = [1 2];
B = [3 4];
C_horz = [A B]; % [1 2 3 4]
% または
C_horz_comma = [A, B]; % 同じ結果 - 垂直結合 (
[A; B]
):同じ列数の配列を縦に連結します。
matlab
X = [1; 2];
Y = [3; 4];
Z_vert = [X; Y]; % [1; 2; 3; 4] - 次元が異なる配列や、サイズが合わない配列を結合しようとするとエラーになります。
- 水平結合の場合、行数が一致しないとエラー。
- 垂直結合の場合、列数が一致しないとエラー。
- 水平結合 (
-
繰り返し (
repmat(A, rows_repeat, cols_repeat)
):配列を指定された回数だけ繰り返して新しい配列を作成します。
matlab
SmallMatrix = [1 2; 3 4];
RepeatedMatrix = repmat(SmallMatrix, 2, 3);
% RepeatedMatrix は以下のように出力されます:
% 1 2 1 2 1 2
% 3 4 3 4 3 4
% 1 2 1 2 1 2
% 3 4 3 4 3 4 -
型変換 (
cast
関数や型名関数):あるデータ型の配列を別のデータ型に変換します。
“`matlab
D_double = [1.5 2.5 3.5];
I_int8 = int8(D_double); % [2 3 4] (小数点以下は丸められる)U_uint8 = uint8(250);
F_single = single(U_uint8); % 250 (single型)
“`
変換時に値が失われる(精度が落ちる、オーバーフロー/アンダーフロー、小数点以下の切り捨てなど)可能性があるため注意が必要です。
第3章:配列のインデックス操作(アクセス)
MATLABで配列の特定の部分にアクセスしたり、変更したりすることを「インデックス操作」と呼びます。これはMATLABプログラミングの根幹をなす非常に重要な概念です。
3.1 線形インデックスと部分参照
MATLABは、配列のすべての要素を1次元の列ベクトルとして連続的に格納します(列優先順序)。この格納順序に基づいたインデックスを「線形インデックス」と呼びます。
A(k)
:配列A
のk
番目の要素にアクセスします。
matlab
M = [1 2 3; 4 5 6; 7 8 9];
% Mは内部的に [1; 4; 7; 2; 5; 8; 3; 6; 9] のように格納されている
disp(M(1)); % 1 (1行1列目)
disp(M(2)); % 4 (2行1列目)
disp(M(3)); % 7 (3行1列目)
disp(M(4)); % 2 (1行2列目)
disp(M(5)); % 5 (2行2列目)
線形インデックスは、多次元配列でも使用できます。例えば、D = rand(3,3,2);
の場合、D(1)
はD(1,1,1)
に、D(10)
はD(1,2,2)
に相当します。
3.2 添字インデックスと部分参照
最も一般的なインデックス操作で、要素の行と列(およびそれ以上の次元)を指定してアクセスします。
-
2次元配列 (
A(row, col)
):
matlab
M = [11 12 13;
21 22 23;
31 32 33];
disp(M(2, 3)); % 2行3列目の要素: 23
disp(M(1, 1)); % 1行1列目の要素: 11 -
多次元配列 (
A(dim1, dim2, dim3, ...)
:
“`matlab
D = rand(2, 3, 2); % 2行3列2ページの3次元配列
% D(:,:,1) =
% 0.8147 0.9134 0.2785
% 0.9058 0.6324 0.5469
%
% D(:,:,2) =
% 0.9575 0.1576 0.7060
% 0.9649 0.9706 0.0318disp(D(1, 2, 1)); % 1ページ目の1行2列目の要素
disp(D(2, 1, 2)); % 2ページ目の2行1列目の要素
“` -
範囲指定 (
:
コロン演算子):特定の行や列全体、または部分的な範囲を選択できます。:
単独:その次元のすべての要素を選択します。start:end
:開始インデックスから終了インデックスまでの範囲を選択します。start:step:end
:開始インデックスから終了インデックスまで、指定されたステップで選択します。
“`matlab
M = [11 12 13;
21 22 23;
31 32 33];
disp(M(2, :)); % 2行目のすべての要素: [21 22 23]
disp(M(:, 3)); % 3列目のすべての要素: [13; 23; 33] (列ベクトル)
disp(M(1:2, 2:3)); % 1-2行目、2-3列目の部分行列:
% 12 13
% 22 23
disp(M([1 3], :)); % 1行目と3行目を選択:
% 11 12 13
% 31 32 33
“`
3.3 論理インデックスと部分参照
条件に基づいて要素を選択する非常に強力な方法です。配列と同じサイズの論理配列(true
/false
または1
/0
)を作成し、true
に対応する要素のみを選択します。
“`matlab
data = [10 5 20 15 8 25];
% dataの中から10より大きい要素を選択
mask = data > 10; % 論理配列: [false false true true false true]
disp(mask);
selected_data = data(mask); % data(data > 10) とも書ける
disp(selected_data); % [20 15 25]
% 条件を満たす要素に新しい値を代入することもできる
data(data > 10) = 99;
disp(data); % [10 5 99 99 8 99]
“`
論理インデックスは、データフィルタリングや条件付き操作に非常に役立ちます。
3.4 end
キーワード
end
キーワードは、特定の次元の最後のインデックスを表します。配列のサイズが未知の場合や、コードを柔軟に書きたい場合に便利です。
“`matlab
A = [1 2 3; 4 5 6; 7 8 9];
disp(A(end, :)); % 最終行のすべての要素: [7 8 9]
disp(A(:, end)); % 最終列のすべての要素: [3; 6; 9]
disp(A(end-1, end-1)); % 最後から2番目の行と列の要素 (5)
% end を使って、特定の範囲の要素にアクセス
disp(A(1:end-1, :)); % 最終行を除くすべての行
“`
3.5 空配列の利用(要素の削除)
MATLABでは、要素を削除する際に空配列 []
を使用します。
“`matlab
V = [1 2 3 4 5];
V(3) = []; % 3番目の要素を削除
disp(V); % [1 2 4 5]
M = [11 12 13;
21 22 23;
31 32 33];
M(2, 🙂 = []; % 2行目を削除
disp(M);
% 11 12 13
% 31 32 33
M(:, [1 3]) = []; % 1列目と3列目を削除
disp(M);
% 12
% 32
“`
要素や行/列を削除すると、残りの要素がシフトして新しい配列が作成されます。
第4章:配列の形状変更と次元操作
配列の形状を変更したり、次元を操作したりする関数は、データの前処理や特定のアルゴリズムにデータを適合させる際に重要です。
4.1 転置(Transpose)
配列の行と列を入れ替えます。
- 非共役転置 (
A.'
):数値配列の場合、単に行と列を入れ替えます。 - 共役転置 (
A'
):複素数配列の場合、行と列を入れ替え、さらに要素の複素共役を取ります。実数配列の場合は、A.'
と同じ結果になります。
“`matlab
A = [1 2 3; 4 5 6];
A_transpose = A.’; % 非共役転置
disp(A_transpose);
% 1 4
% 2 5
% 3 6
C = [1+2i, 3+4i];
C_conjugate_transpose = C’; % 共役転置
disp(C_conjugate_transpose);
% 1-2i
% 3-4i
C_non_conjugate_transpose = C.’; % 非共役転置
disp(C_non_conjugate_transpose);
% 1+2i
% 3+4i
“`
通常、実数配列を扱う場合はどちらを使っても同じですが、複素数配列を扱う場合は注意が必要です。
4.2 reshape(A, new_rows, new_cols, ...)
配列の要素数を変えずに、新しい形状に再配置します。要素は線形インデックス(列優先順序)に従って新しい形状に埋められます。
matlab
Original = 1:9; % [1 2 3 4 5 6 7 8 9]
Reshaped = reshape(Original, 3, 3);
disp(Reshaped);
% 1 4 7
% 2 5 8
% 3 6 9
reshape
を使用する場合、新しい形状の全要素数(new_rows * new_cols * ...
)が元の配列の全要素数と一致する必要があります。
多次元配列の例:
matlab
Original_3D = reshape(1:27, 3, 3, 3); % 3x3x3の配列
Reshaped_2D = reshape(Original_3D, 9, 3); % 9x3の2次元配列に
disp(Reshaped_2D);
4.3 permute(A, order)
配列の次元の順番を並べ替えます。order
は新しい次元の順番を指定するベクトルです。
“`matlab
% 2x3x4 の配列を作成
A = reshape(1:(234), 2, 3, 4);
% A のサイズは [2 3 4]
% A(1,1,1) = 1, A(2,1,1) = 2, A(1,2,1) = 3, …
% 2次元と1次元を入れ替える (サイズは [3 2 4] になる)
B = permute(A, [2 1 3]);
disp(size(B)); % [3 2 4]
disp(B(1,1,1)); % 元のA(1,1,1)がB(1,1,1)に相当。値は1。
disp(B(2,1,1)); % 元のA(1,2,1)がB(2,1,1)に相当。値は3。
``
permute`は、例えば画像の幅と高さの次元を入れ替えたり、特定の次元でデータを処理するために配列の向きを変えたい場合に非常に有用です。
4.4 squeeze(A)
シングルトン次元(サイズが1の次元)を削除します。
“`matlab
A = rand(1, 5, 1, 2, 1); % 1x5x1x2x1 の5次元配列
disp(size(A)); % [1 5 1 2 1]
B = squeeze(A);
disp(size(B)); % [5 2] (1だった次元が削除された)
“`
これにより、不要な次元が取り除かれ、配列がより扱いやすくなります。
4.5 cat(dim, A, B, ...)
指定した次元に沿って複数の配列を連結します。水平結合や垂直結合は、cat
関数の特殊なケースとして扱えます。
cat(1, A, B)
は垂直結合[A; B]
と同じ。cat(2, A, B)
は水平結合[A B]
と同じ。cat(3, A, B)
は3次元方向に配列を結合します。
“`matlab
M1 = [1 2; 3 4];
M2 = [5 6; 7 8];
% 3次元方向に結合 (M1, M2を奥行き方向に重ねる)
C = cat(3, M1, M2);
disp(size(C)); % [2 2 2]
disp(C(:,:,1)); % M1
disp(C(:,:,2)); % M2
``
cat`は、多次元配列の構築において非常に柔軟な手段を提供します。
第5章:配列の要素の追加・削除・変更
配列への要素の追加は、MATLABで最も注意が必要な操作の一つです。特に大規模な配列を扱う場合、効率的な追加方法を知っているかどうかがパフォーマンスに大きく影響します。
5.1 基本的な追加方法
MATLABでは、既存の配列に要素を追加する方法がいくつかあります。
-
既存のインデックス外への代入:
配列の現在のサイズを超えたインデックスに値を代入すると、MATLABは自動的に配列を拡張し、新しいインデックスまでの間に存在する要素にはゼロを埋めます。
“`matlab
V = [1 2 3];
V(5) = 10; % 4番目の要素は自動的に0が埋められる
disp(V); % [1 2 3 0 10]M = [1 2; 3 4];
M(3, 1) = 5; % 3行目は自動的に拡張され、M(3,2)は0
disp(M);
% 1 2
% 3 4
% 5 0M(1, 4) = 100; % 4列目が自動的に拡張され、M(2,4), M(3,4)は0
disp(M);
% 1 2 0 100
% 3 4 0 0
% 5 0 0 0
“`
これは非常に便利ですが、後述するパフォーマンスの問題を引き起こす可能性があります。 -
水平結合による追加 (
A = [A, new_elements];
):
既存の配列の末尾に、新しい要素や別の配列を水平方向(列方向)に結合します。
“`matlab
myVector = [1 2 3];
myVector = [myVector, 4, 5]; % 単一の要素を追加
disp(myVector); % [1 2 3 4 5]anotherVector = [6 7];
myVector = [myVector, anotherVector]; % 別の配列を追加
disp(myVector); % [1 2 3 4 5 6 7]myMatrix = [1 2; 3 4];
newColumn = [5; 6];
myMatrix = [myMatrix, newColumn]; % 列を追加
disp(myMatrix);
% 1 2 5
% 3 4 6
“`
水平結合する場合、既存の配列と追加する要素/配列の行数が一致する必要があります。 -
垂直結合による追加 (
A = [A; new_rows];
):
既存の配列の末尾に、新しい行や別の配列を垂直方向(行方向)に結合します。
“`matlab
myVector = [1; 2; 3]; % 列ベクトルとして開始
myVector = [myVector; 4; 5]; % 単一の要素を追加
disp(myVector); % [1; 2; 3; 4; 5]anotherVector = [6; 7];
myVector = [myVector; anotherVector]; % 別の配列を追加
disp(myVector); % [1; 2; 3; 4; 5; 6; 7]myMatrix = [1 2; 3 4];
newRow = [5 6];
myMatrix = [myMatrix; newRow]; % 行を追加
disp(myMatrix);
% 1 2
% 3 4
% 5 6
“`
垂直結合する場合、既存の配列と追加する要素/配列の列数が一致する必要があります。 -
多次元配列への追加:
cat
関数を使用するか、インデックス指定で拡張します。
matlab
D = rand(2,2,1); % 2x2の1ページ
new_page = ones(2,2);
D = cat(3, D, new_page); % 3次元方向に新しいページを追加
disp(size(D)); % [2 2 2]
disp(D(:,:,2)); % new_pageの内容
5.2 事前割り当て(Pre-allocation)の重要性
これまでの要素追加方法(特にインデックス外への代入や結合をループ内で繰り返す場合)は、小規模な配列では問題になりませんが、大規模な配列や多数の要素を追加する場合、極めてパフォーマンスが低下します。これは、MATLABが配列を拡張するたびに、以下のプロセスを内部的に行っているためです。
- 既存の配列よりも大きな新しいメモリ領域を確保する。
- 既存の配列のすべての要素を新しいメモリ領域にコピーする。
- 古いメモリ領域を解放する。
- 新しい要素を新しいメモリ領域に追加する。
この「再割り当てとコピー」の処理は、配列が大きくなるにつれて指数関数的にコストが増大します。特にループ内で頻繁に配列が拡張されると、このオーバーヘッドが積み重なり、プログラムの実行時間が劇的に長くなります。
事前割り当てとは、配列にデータを格納する前に、あらかじめ最終的に必要となるであろう最大のサイズでメモリを確保しておくことです。これにより、MATLABは一度だけメモリを割り当て、その後の要素追加時に再割り当てのオーバーヘッドを避けることができます。
-
事前割り当ての推奨方法:
要素がすべて既知の型(例:double
)であれば、zeros()
やones()
、またはnan()
(Not a Number)を使って必要なサイズの配列を初期化します。
“`matlab
% 事前割り当てなしの場合 (非常に遅い)
tic;
myNumbers_slow = [];
for i = 1:100000
myNumbers_slow = [myNumbers_slow, i];
end
toc; % 例: Elapsed time is 0.725927 seconds. (環境による)% 事前割り当てありの場合 (高速)
tic;
numElements = 100000;
myNumbers_fast = zeros(1, numElements); % 1×100000のゼロ配列を事前割り当て
for i = 1:numElements
myNumbers_fast(i) = i; % 要素を上書き
end
toc; % 例: Elapsed time is 0.003923 seconds. (環境による)
``
cell(rows, cols)
上記の例では、事前割り当ての有無で実行時間が桁違いに異なることが分かります。
セル配列の場合は、構造体配列の場合は
struct()`とフィールド名を指定して事前に初期化します。“`matlab
% セル配列の事前割り当て
numItems = 5;
myCellArray_preallocated = cell(1, numItems); % 1×5の空のセル配列を事前割り当て
for i = 1:numItems
myCellArray_preallocated{i} = [‘Item ‘ num2str(i)];
end% 構造体配列の事前割り当て
numStudents = 100;
% 最初の要素を初期化することで、フィールドが定義される
students(numStudents).name = ”; % 最後の要素を初期化してメモリを確保
students(numStudents).id = 0;for i = 1:numStudents
students(i).name = sprintf(‘Student%d’, i);
students(i).id = i;
end
“`
ベストプラクティス: ループで配列を構築する場合は、必ず事前に配列のサイズを見積もり、メモリを割り当てるようにしてください。これにより、コードの実行速度が劇的に向上し、より大規模なデータセットも効率的に処理できるようになります。
5.3 要素の削除
要素の削除は、その要素に空配列 []
を代入することで行います。MATLABは、空配列が代入されたインデックスの要素を削除し、残りの要素を詰めます。
-
単一要素の削除:
matlab
V = [10 20 30 40 50];
V(3) = []; % 3番目の要素 (30) を削除
disp(V); % [10 20 40 50] -
複数の要素の削除:
matlab
V = [1 2 3 4 5 6 7 8 9];
V([2 4 6]) = []; % 2, 4, 6番目の要素を削除
disp(V); % [1 3 5 7 8 9] -
行/列の削除:
“`matlab
M = [11 12 13;
21 22 23;
31 32 33;
41 42 43];M(2, 🙂 = []; % 2行目を削除
disp(M);
% 11 12 13
% 31 32 33
% 41 42 43M(:, [1 3]) = []; % 1列目と3列目を削除
disp(M);
% 12
% 32
% 42
“`
多次元配列の場合も同様に、特定の次元でスライスを削除できます。 -
論理インデックスによる削除:
matlab
data = [10 5 20 15 8 25];
data(data < 10) = []; % 10より小さい要素を削除
disp(data); % [10 20 15 25]
この方法は、特定の条件を満たす要素を一括で削除するのに非常に便利です。
第6章:よくある落とし穴とベストプラクティス
MATLABの配列操作は強力ですが、いくつかの一般的な落とし穴があり、それらを避けるためのベストプラクティスがあります。
6.1 事前割り当ての徹底
最大の落とし穴であり、最も重要なベストプラクティスです。
第5章で詳しく述べたように、ループ内で配列を動的に拡張することは、パフォーマンスに壊滅的な影響を与えます。数千、数万回の反復がある場合、プログラムが数分から数時間かかる原因となることがあります。
-
推奨されるパターン:
“`matlab
% 最終的なサイズがわかる場合
N = 100000;
resultArray = zeros(1, N); % または ones, nan, cell, struct など
for i = 1:N
% 計算
resultArray(i) = i * 2;
end% 最終的なサイズが事前にわからない場合
% ある程度のサイズで事前に割り当てておき、必要に応じて拡張する(拡張回数を減らす)
initial_capacity = 1000;
data = zeros(1, initial_capacity);
current_size = 0;for i = 1:some_large_number
new_value = i; % 例えば、条件によっては追加しない場合もある
if new_value < 5000 % 例示のための条件
current_size = current_size + 1;
if current_size > initial_capacity
% 容量が足りなくなったら、現在のサイズの2倍に拡張
data = [data, zeros(1, initial_capacity)];
initial_capacity = initial_capacity * 2;
end
data(current_size) = new_value;
end
end
data = data(1:current_size); % 不要なゼロを削除
“`
後者のパターンは、事前割り当てがないよりはるかにマシですが、可能な限り最終サイズを予測し、一度で割り当てるのが理想です。
6.2 インデックスの範囲外エラー
これはMATLABでよく遭遇するエラーの一つです。配列の範囲外のインデックスにアクセスしようとすると発生します。
matlab
A = [1 2 3];
% A(4) = 10; % これはAを拡張するのでエラーにならない (しかし、パフォーマンス注意)
% disp(A(5)); % これはエラーになります: "Index exceeds array dimensions."
インデックスが現在の配列のサイズを超えて要素を取得しようとした場合に発生します。特にループ処理で、インデックスの境界条件を誤ると発生しやすいため、size()
関数やlength()
関数、end
キーワードを適切に利用して、配列の範囲内に収まるように注意しましょう。
6.3 スカラーと配列の演算(ブロードキャスト)
MATLABは、スカラーと配列の間の演算を自動的に拡張(ブロードキャスト)します。これは便利ですが、意図しない結果につながることもあります。
“`matlab
A = [1 2; 3 4];
B = A + 10; % 各要素に10が加算される
disp(B);
% 11 12
% 13 14
C = A . 2; % 各要素に2が乗算される (要素ごとの乗算)
disp(C);
% 2 4
% 6 8
``
これは直感的ですが、行列の乗算と要素ごとの乗算
.*`の違いを常に意識することが重要です。
6.4 異なるデータ型の結合と型変換の意識
異なるデータ型の配列を結合しようとすると、MATLABは自動的に型変換を試みますが、これにより予期せぬ情報の損失(特に精度や値の範囲)が発生する可能性があります。
“`matlab
A_double = [1.1 2.2];
B_int8 = int8([3 4]);
C = [A_double, B_int8]; % B_int8がdoubleに変換される
disp(C); % [1.1 2.2 3.0 4.0]
disp(class(C)); % double
D = [B_int8, A_double]; % A_doubleがint8に変換される
disp(D); % [3 4 1 2] (小数点以下が切り捨てられる!)
disp(class(D)); % int8
``
double()
結合の順序によって、結果の型と値が変わる可能性があることに注意が必要です。明示的な型変換関数(例:,
int8()`)を使用して、変換の挙動を制御することをお勧めします。
6.5 コピーと参照(MATLABは値渡しが基本)
MATLABは基本的に「値渡し」のセマンティクスを採用しています。つまり、配列を関数に渡したり、別の変数に代入したりすると、通常はデータのコピーが作成されます。これにより、関数内で元の配列が変更されることを心配する必要がなくなりますが、大規模な配列の場合、メモリ使用量と実行時間に影響を与える可能性があります。
“`matlab
A = rand(1000, 1000); % 大規模な配列
B = A; % Aの内容がBにコピーされる (メモリを2倍消費する可能性がある)
function modifyArray(arr)
arr(1,1) = 999; % arrのコピーを変更
end
modifyArray(A); % Aは変更されない
disp(A(1,1)); % 元のA(1,1)の値
“`
ただし、MATLABの内部では、可能な限りコピーを避ける「コピーオンライト (Copy-on-Write)」最適化が行われます。これは、変数に代入されたり関数に渡されたりした配列が、実際に変更されるまでコピーされないというものです。これにより、不必要なメモリコピーが削減され、パフォーマンスが向上します。しかし、コードを書く上では、基本的にはコピーが発生すると考えておいた方が安全です。
第7章:発展的な配列操作
MATLABには、数値行列だけでなく、より複雑なデータ構造を扱うための特別な配列型が用意されています。これらは、異なる種類のデータを効率的に管理するために不可欠です。
7.1 セル配列 (Cell Array)
前述の通り、セル配列は異なるデータ型やサイズのデータを格納できる汎用的なコンテナです。各要素が「セル」と呼ばれ、任意のMATLABデータを保持できます。
-
作成方法:波括弧
{}
を使用して作成します。
“`matlab
% 直接入力
myCell = {1, ‘hello’, [10 20; 30 40], true};% cell 関数で初期化 (事前割り当て)
emptyCell = cell(2, 3); % 2×3の空のセル配列
“` -
要素へのアクセス:
- セルコンテンツインデックス(
{}
):セルの内容にアクセスします。最も一般的です。
matlab
disp(myCell{1}); % 1 (数値)
disp(myCell{2}); % 'hello' (文字列)
disp(myCell{3}); % [10 20; 30 40] (行列) - セルアレイインデックス(
()
):セル配列自体の一部(サブセル配列)を取得します。この場合、返されるのはセル配列です。
matlab
subCell = myCell(1:2);
disp(subCell); % {1} {'hello'}
disp(class(subCell)); % cell
- セルコンテンツインデックス(
-
要素の追加・変更:
インデックス指定または結合によって要素を追加・変更できます。
“`matlab
myCell = {‘Apple’, ‘Banana’};
myCell{3} = ‘Cherry’; % 3番目のセルを追加 (拡張)
disp(myCell); % {‘Apple’} {‘Banana’} {‘Cherry’}% 結合による追加
newCell = {‘Date’, ‘Elderberry’};
myCell = [myCell, newCell]; % 水平結合
disp(myCell); % {‘Apple’} {‘Banana’} {‘Cherry’} {‘Date’} {‘Elderberry’}% 2次元セル配列
matrixCell = cell(2,2);
matrixCell{1,1} = ‘A1’;
matrixCell{2,2} = [1 2 3];
disp(matrixCell);
% {‘A1’} {[]}
% {[]} {[1 2 3]}
“`
セル配列も、頻繁な拡張を避けるために事前割り当てが推奨されます。
7.2 構造体配列 (Structure Array)
構造体は、名前付きのフィールド(プロパティ)を持つデータ構造です。データベースのレコードやオブジェクトのプロパティのように、関連する異なる型のデータを一箇所にまとめるのに適しています。
-
作成方法:ドット
.
演算子を使用してフィールドに値を代入します。
“`matlab
% 単一の構造体
person.name = ‘Alice’;
person.age = 30;
person.city = ‘Tokyo’;% 構造体配列 (複数のperson構造体をまとめる)
students(1).name = ‘Bob’;
students(1).age = 22;
students(1).major = ‘Computer Science’;students(2).name = ‘Carol’;
students(2).age = 21;
students(2).major = ‘Physics’;% struct 関数での作成
s = struct(‘Field1’, {1,2,3}, ‘Field2’, {‘A’,’B’,’C’});
“` -
フィールドへのアクセス:ドット
.
演算子とフィールド名を使用します。
matlab
disp(person.name); % 'Alice'
disp(students(1).major); % 'Computer Science' -
要素(構造体)の追加・変更:
インデックス指定で新しい構造体を追加したり、既存の構造体のフィールド値を変更したりできます。
“`matlab
person.age = 31; % フィールドの値を変更% 構造体配列への追加
students(3).name = ‘David’; % 新しい構造体を追加(自動拡張)
students(3).age = 23;
students(3).major = ‘Mathematics’;
“`
構造体配列も、動的な拡張はパフォーマンスに影響するため、事前割り当てが推奨されます。特に、全てのフィールドが同じ型で同じサイズになる場合は、事前に最後の要素を定義してメモリを確保するのが効率的です。
7.3 テーブル配列 (Table Array)
R2013bで導入されたテーブル配列は、データベースのテーブルやスプレッドシートのように、名前付きの列を持つデータ構造です。各列は異なるデータ型を持つことができ、データの整理、フィルタリング、可視化に非常に便利です。
-
作成方法:
table()
関数を使用し、列となる変数を渡します。
“`matlab
Names = [“Alice”; “Bob”; “Charlie”];
Ages = [25; 30; 22];
Scores = [80; 92; 75];T = table(Names, Ages, Scores);
disp(T);
% Names Ages Scores
% __ __ ______
% “Alice” 25 80
% “Bob” 30 92
% “Charlie” 22 75% 列名を指定することもできる
T.Properties.VariableNames = {‘StudentName’, ‘StudentAge’, ‘TestScore’};
disp(T);
% StudentName StudentAge TestScore
% __ _ ___
% “Alice” 25 80
% “Bob” 30 92
% “Charlie” 22 75
“` -
要素へのアクセス:
- ドット
.
演算子と列名:特定の列全体にアクセス。 - 添字インデックス:行と列の両方を指定。
matlab
disp(T.StudentAge); % [25; 30; 22] (列ベクトル)
disp(T(2, :)); % 2行目のすべて: Bob, 30, 92
disp(T.StudentName(3)); % "Charlie"
- ドット
-
行/列の追加・変更:
新しい列を追加するには、ドット構文で列名とデータを代入します。
新しい行を追加するには、垂直結合[T; new_row]
を使用します。
“`matlab
% 新しい列の追加
Grades = [“A”; “B”; “C”];
T.Grade = Grades;
disp(T);% 新しい行の追加
newPerson = {“David”, 28, 88, “B”};
T = [T; newPerson]; % 新しい行を追加
disp(T);
“`
テーブルは、データ解析や可視化のための強力なデータコンテナとして、非常に推奨されます。
7.4 時系列配列 (Timetable Array)
R2016bで導入されたTimetableは、時間情報を持つデータ(時系列データ)を扱うための特殊なテーブル配列です。各行がタイムスタンプと関連付けられており、時間ベースの操作(リサンプリング、同期など)が容易に行えます。
-
作成方法:
“`matlab
Time = datetime(2023,1,1, [1:3]’, 0, 0); % タイムスタンプ
Sensor1 = [10; 12; 11];
Sensor2 = [5; 6; 4];TT = timetable(Time, Sensor1, Sensor2);
disp(TT);
% Time Sensor1 Sensor2
% ____ _ _____
% 01-Jan-2023 01:00:00 10 5
% 01-Jan-2023 02:00:00 12 6
% 01-Jan-2023 03:00:00 11 4
“` -
時間ベースの操作:
特定の時間範囲のデータを抽出したり、異なるタイムテーブルを結合・同期したりするのに便利です。
matlab
% 特定の時間範囲のデータを抽出
subsetTT = TT(timerange('01-Jan-2023 01:30:00', '01-Jan-2023 02:30:00'), :);
disp(subsetTT);
% Time Sensor1 Sensor2
% ____________________ _______ _______
% 01-Jan-2023 02:00:00 12 6
時系列データの解析を行う場合、Timetableは非常に有用なツールです。
まとめ
この記事では、MATLABにおける配列の基本から、その効率的な操作方法、そしていくつかの高度なデータ構造について網羅的に解説してきました。
主要なポイントの要約:
- MATLABの基盤は配列:スカラー、ベクトル、行列、多次元配列など、MATLABのすべてのデータは配列として扱われます。
- 多様なデータ型:数値型(
double
,int8
など)、文字型(char
,string
)、論理型(logical
)に加え、異なるデータを格納できるセル配列、構造体配列、表形式のテーブル配列、時系列データのTimetableなどがあります。目的に応じて適切なデータ型を選ぶことが重要です。 - 配列の作成方法:直接入力、
zeros
,ones
,rand
,linspace
, コロン演算子などの組み込み関数、そして既存配列の結合や繰り返し(repmat
)など、多様な方法があります。 - 柔軟なインデックス操作:線形インデックス、添字インデックス、論理インデックス、
end
キーワードを使いこなすことで、配列の特定の部分に効率的にアクセスし、操作できます。 - 配列の形状変更:
transpose
,reshape
,permute
,squeeze
,cat
などの関数を使って、配列の次元や形状を柔軟に変更できます。 - 要素の追加と事前割り当ての重要性:配列への要素追加は、インデックス外への代入や結合で行えますが、ループ内で繰り返すとパフォーマンスが著しく低下します。これを避けるために、
zeros
,cell
,struct
などで事前にメモリを確保する「事前割り当て」が必須です。 - よくある落とし穴とベストプラクティス:事前割り当ての徹底、インデックス範囲外エラーの回避、データ型の意識、ブロードキャストの理解などが、効率的で堅牢なMATLABコードを書く上で重要です。
MATLABプログラミングにおいて、配列はあなたのツールキットの中心となるでしょう。この記事で学んだ配列の基本概念と操作方法を習得することで、データ解析、アルゴリズム開発、シミュレーションなど、あらゆるMATLABタスクをより効率的かつ強力に進めることができるようになります。
今後の学習として、これらの基本操作を組み合わせたより複雑なアルゴリズムの実装、行列演算の応用、データ可視化、そしてMATLABのパフォーマンス最適化の deeper dive を試してみてください。MATLABのドキュメントは非常に充実しており、多くのサンプルコードが提供されています。継続的な学習と実践を通じて、あなたのMATLABスキルは着実に向上していくはずです。