データを大量に扱う場合,一つ一つのデータにそれぞれ変数を宣言するのは非常に大変です.このため,同じ種類のデータであれば全体を代表す名前と個々を区別する番号を用いた変数を宣言することができます.このタイプの変数を「配列」変数といいます.配列を使用すれば,同種類のデータを大量に扱うプログラムをより単純に記述できます.
配列を代表する名前を「配列名」,個々を区別する番号を「インデックス」,一つ一つの変数を「配列要素」と呼びます.
配列変数の型宣言は次のように記述します.
Dim 配列名(インデックスの範囲) As データ型
配列変数名を a ,インデックス最大値を 5 とした整数型の配列変数の宣言は次のようになります.
Dim a(5) As Integer
この宣言によって,a(0) から a(5) の「6つの変数」が用意されます.最初のインデックスは0から始まることに注意しましょう.
また,インデックスの最小値を指定して宣言することも可能です.
Dim a(1 To 5) As Integer
このように宣言した場合は,a(1) から a(5) の「5つの変数」が用意されます.
配列変数にデータを代入する場合はFor文を使用すると便利です.例えば,データが C1 から C10 セルに入力されている場合に,それらを配列変数に代入するには以下のように書きます.
Dim a(1 To 10) As Integer Dim i As Integer For i = 1 To 10 a(i) = Cells(i,3).Value Next i
次のようなデータについて,平均点を求めるプログラムを作成する.この時,各学生の得点は同種のデータなので配列変数を使用する.
学生番号 | 得点 |
---|---|
1 | 77 |
2 | 83 |
3 | 56 |
4 | 68 |
5 | 35 |
6 | 96 |
7 | 84 |
8 | 72 |
9 | 63 |
10 | 51 |
11 | 45 |
12 | 74 |
合計 | |
平均 |
Sub heikin() '変数宣言 Dim tokuten(1 To 12) As Integer '得点の配列変数 Dim ave As Double '平均点 Dim gokei As Integer '合計点 Dim i As Integer '学生番号 '得点入力 For i = 1 To 12 tokuten(i) = Cells(i+1,2).Value Next i 合計点初期値 gokei = 0 '合計点計算 For i = 1 To 12 gokei = gokei + tokuten(i) Next i '平均点計算 ave = gokei/12 '結果表示 Cells(14,2).Value = gokei Cells(15,2).Value = ave End Sub
インデックスを2つ使用した配列変数を使うこともできます.イメージとしては,Excelのセルのように行番号と列番号で指定できる2次元的な変数の集合と思ってください.
2次元の配列の型宣言は,次のように記述します.
Dim 配列名(インデックス1の範囲 , インデックス2の範囲) As データ型
配列名を a ,第1インデックスの最大値を3,第2インデックスの最大値を4とした整数型の配列の宣言は,
Dim a(3,4) As Integer
この宣言で a(0,0) から a(3,4) まで \((3+1)\times(4+1) = 20\)個の変数が用意されます.
また,インデックスの最小値を指定することもできます.
Dim a(1 To 3, 2 To 5) As Integer
この宣言で,a(1,2) から a(3,4) まで \(3 \times 3 = 9\)個の変数が用意されます.
2次元配列変数にデータを代入する場合はFor文を2重に使用します.例えば,データが A1 から C10 セルに入力されている場合に,それらを配列変数に代入するには以下のように書きます.
Dim a(1 To 3, 1 To 10) As Integer Dim i As Integer, j As Integer For i = 1 To 3 For j = 1 To 10 a(i,j) = Cells(i,j).Value Next j Next i
名前 | 国語 | 数学 | 英語 | 理科 | 社会 | 個人平均 |
---|---|---|---|---|---|---|
A | 71 | 95 | 76 | 89 | 72 | |
B | 61 | 86 | 74 | 60 | 80 | |
C | 76 | 81 | 80 | 76 | 73 | |
D | 63 | 51 | 71 | 60 | 79 | |
E | 66 | 75 | 88 | 55 | 65 | |
F | 78 | 54 | 63 | 84 | 51 | |
G | 88 | 78 | 69 | 69 | 93 | |
H | 76 | 68 | 55 | 60 | 78 | |
I | 78 | 61 | 79 | 51 | 88 | |
J | 79 | 63 | 82 | 75 | 83 | |
K | 82 | 87 | 78 | 72 | 55 | |
L | 49 | 71 | 77 | 87 | 61 | |
M | 68 | 81 | 68 | 93 | 75 | |
N | 83 | 87 | 60 | 81 | 62 | |
O | 56 | 88 | 58 | 72 | 72 | |
P | 86 | 72 | 68 | 98 | 62 | |
Q | 73 | 55 | 75 | 70 | 75 | |
R | 74 | 77 | 87 | 70 | 85 | |
S | 68 | 68 | 91 | 50 | 79 | |
T | 81 | 92 | 80 | 76 | 73 | |
U | 91 | 87 | 93 | 65 | 71 | |
V | 68 | 74 | 88 | 77 | 78 | |
W | 80 | 70 | 76 | 73 | 85 | |
X | 92 | 73 | 78 | 73 | 97 | |
Y | 69 | 67 | 79 | 85 | 83 | |
Z | 71 | 69 | 77 | 77 | 77 | |
科目平均 |
Sub heikin2() '変数宣言 Dim tokuten(1 To 26, 1 To 5) As Integer '得点の配列変数 Dim id_ave(1 To 26) As Double, sbj_ave(1 To 5) As Double '平均点 Dim id_gokei(1 To 26) As Integer, sbj_gokei(1 To 5) As Integer '合計点 Dim i As Integer, j As Integer '得点入力 For i = 1 To 26 For j = 1 To 5 tokuten(i,j) = Cells(i+1,j+1).Value Next j Next i '合計点初期値 For i = 1 To 26 id_gokei(i) = 0 Next i For i = 1 To 5 sbj_gokei(i) = 0 Next i '合計点計算 For i = 1 To 26 For j = 1 To 5 id_gokei(i) = id_gokei(i) + tokuten(i,j) Next j Next i For j = 1 To 5 For i = 1 To 26 sbj_gokei(j) = sbj_gokei(j) + tokuten(i,j) Next i Next j '平均点計算 For i = 1 To 26 id_ave(i) = id_gokei(i)/5 Next i For i = 1 To 5 sbj_ave(i) = sbj_gokei(i)/26 Next i '結果表示 For i = 1 To 26 Cells(i+1,7).Value = id_ave(i) Next i For i = 1 To 5 Cells(28,i+1).Value = sbj_ave(i) Next i End Sub
最初に説明したように,VBAでは単純な処理を行う単一の命令をステートメントといい,それらを組み合わせてある特定の作業させるようにしたものをプロシージャと呼びます.
一般的に,VBAにおいて何らかの作業を行うプロシージャをサブプロシージャといいます.しかし複雑な作業の場合に,作業内容を一つのプロシージャにまとめて記述すると,処理の流れや構造が分かりにくくなりエラーも生じ易くなります.したがって,複雑な作業を実行する場合は,作業の全体を幾つかの より単純な作業の組み合わせになるように書き表した「メインプロシージャ」とし,分割した個々の作業内容(「サブルーチン」という)を別のプロシージャ「サブプロシージャ」として記述しておき,メインのプロシージャではそれらを呼び出して使用するように記述する,このように記述すれば,作業全体の流れが分かり易くなり,間違いも起こりにくくなります.また,たとえエラーが発生した場合でも,どこで発生したのかを解析しやすくなるため,修正しやすくなります.
サブプロシージャを呼び出すときは次のように記述します.
呼び出し側:
Call サブプロシージャ名
サブルーチン側:
Sub サブプロシージャ名
サブプロシージャは「引数」を取ることができ,この引数を使用してメインプロシージャとの間で値をやり取することが可能になります.この場合の記述の仕方は次のようになります.
呼び出し側:こちらの引数を実引数と呼ぶ.
Call サブプロシージャ名(実引数1, 実引数2, …)
サブルーチン側:こちらの引数を仮引数と呼ぶ.引数名は実引数と異なってもよい.
Sub サブプロシージャ名(仮引数1 As データ型, 仮引数2 As データ型, …)
配列を引数とする場合は,仮引数の記述を次のようにする.
Sub サブプロシージャ名(仮引数1() As データ型, 仮引数2() As データ型, …)
身長と体重のからBMIを計算し結果を表示するプログラムを作ってみる.
身長cm | 体重kg | BMI | 結果 |
---|---|---|---|
(身長入力) | (体重入力) |
Sub taikei() Dim shincho As Single, taiju As Single Dim bmi As Single shincho = Cells(2,1).Value taiju = Cells(2,2).Value Call bmi_keisan(shincho,taiju,bmi) Cells(2,3).Value = bmi If bmi < 18.5 Then Call yase ElseIf bmi < 25 Then Call futsu Else Call futo End If End Sub Sub bmi_keisan(h As Single, w As Single, b As Single) b = w/(h/100)^2 End Sub Sub yase() Cells(2,4).Value = "低体重" End Sub Sub futsu() Cells(2,4).Value = "標準" End Sub Sub futo() Cells(2,4).Value = "肥満" End Sub
Excelのブックに新しいシートを追加し,VBEで新しい標準モジュールを挿入します.プロジェクトエクスプローラーに Sheet4 と Module4 が表示されるはずです.
Module4 のコードウィンドウに新しい Sub プロシージャを作成します.
以下のプログラムのどれかを作成しましょう.
次のデータを使用して「平均値,分散,標準偏差」を求めるプログラムを作ってみましょう. この時,メインプロシージャから「学生数,得点データ」を受け取り,「平均値,分散,標準偏差」を計算する1つのサブプロシージャを作成し,メインプロシージャではそれを呼び出すように作成してください.偏差,分散,標準偏差に関しては「解説」を参照.
結果は得点データを入力したセルの下のセルに表示するようにし,使用するセルはこのプログラムを登録するボタンの下に来る位置のセルにしてください.Sheet4にフォームコントロールのボタンを挿入し,プログラムを登録して名前を「ex_051a」に変えて下さい,ボタンはシートの左上あたりに配置してください.
名前 | 得点 |
---|---|
A | 71 |
B | 61 |
C | 76 |
D | 63 |
E | 66 |
F | 78 |
G | 88 |
H | 76 |
I | 78 |
J | 79 |
K | 82 |
L | 49 |
M | 68 |
N | 83 |
O | 56 |
P | 86 |
Q | 73 |
R | 74 |
S | 68 |
T | 81 |
U | 91 |
V | 68 |
W | 80 |
X | 92 |
Y | 69 |
Z | 71 |
平均 | |
分散 | |
標準偏差 |
次の2次元配列のデータを使用して科目毎の標準偏差を求めるプログラムを作ってみましょう. この時,メインプロシージャから「学生数,科目,得点データ」を受け取り,各科目の「標準偏差」を計算するサブプロシージャを作成し,メインプロシージャではそれを呼び出すように作成してください.標準偏差に関しては「解説」を参照.
結果は得点データを入力したセルの下のセルに表示するようにし,使用するセルはこのプログラムを登録するボタンの下に来る位置のセルにしてください.Sheet4にフォームコントロールのボタンを挿入し,プログラムを登録して名前を「ex_051b」に変えて下さい,ボタンはシートの左上あたりに配置してください.
名前 | 国語 | 数学 | 英語 | 理科 | 社会 |
---|---|---|---|---|---|
A | 71 | 95 | 76 | 89 | 72 |
B | 61 | 86 | 74 | 60 | 80 |
C | 76 | 81 | 80 | 76 | 73 |
D | 63 | 51 | 71 | 60 | 79 |
E | 66 | 75 | 88 | 55 | 65 |
F | 78 | 54 | 63 | 84 | 51 |
G | 88 | 78 | 69 | 69 | 93 |
H | 76 | 68 | 55 | 60 | 78 |
I | 78 | 61 | 79 | 51 | 88 |
J | 79 | 63 | 82 | 75 | 83 |
K | 82 | 87 | 78 | 72 | 55 |
L | 49 | 71 | 77 | 87 | 61 |
M | 68 | 81 | 68 | 93 | 75 |
N | 83 | 87 | 60 | 81 | 62 |
O | 56 | 88 | 58 | 72 | 72 |
P | 86 | 72 | 68 | 98 | 62 |
Q | 73 | 55 | 75 | 70 | 75 |
R | 74 | 77 | 87 | 70 | 85 |
S | 68 | 68 | 91 | 50 | 79 |
T | 81 | 92 | 80 | 76 | 73 |
U | 91 | 87 | 93 | 65 | 71 |
V | 68 | 74 | 88 | 77 | 78 |
W | 80 | 70 | 76 | 73 | 85 |
X | 92 | 73 | 78 | 73 | 97 |
Y | 69 | 67 | 79 | 85 | 83 |
Z | 71 | 69 | 77 | 77 | 77 |
標準偏差 |
データの様々な性質を調べる手法を「統計」という.具体的には様々な統計量を計算することによって成される.
最も基本的な統計量は「平均」であり,データ:\(x_i\quad i = 1, \cdots , n\)に対して次のように定義される. \[ \bar{x} = \frac{x_1 + x_2 + \cdots + x_n}{n} = \frac{1}{n}\sum_{i=1}^{n}x_i \] 各データの平均からのずれを「偏差」,偏差の2乗の平均が「分散:\(\sigma^2\)」と呼ばれ,データ全体が平均からどのくらいの広がりを持っているのかを表す. \[ \sigma^2 = \frac{(x_1-\bar{x})^2 + (x_2 - \bar{x})^2 + \cdots + (x_n - \bar{x})^2}{n} = \frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x}) ^2 \] 通常は分散の平方根を取った「標準偏差:\(\sigma\)」がデータの広がりを表す指標となる. \[ \sigma = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2} \]
ファンクションプロシージャもサブルーチンの一つだが,VBAに組み込まれた数学の関数のような形で機能する. 従って,ファンクションプロシージャは受け取ったデータを処理し,その結果をメインのプロシージャに返す様に記述する.
ファンクションプロシージャを呼び出すときは次のように記述する.
呼び出し側:こちらの引数を実引数と呼ぶ.結果を受け取る変数はファンクションプロシージャのデータ型と合わせる
変数名 = ファンクションプロシージャ名(実引数名)
サブルーチン側:こちらの引数を仮引数と呼ぶ.引数名は実引数と異なってもよい.返す結果のデータ型は指定しておく
Function ファンクションプロシージャ名(仮引数名 As データ型) As データ型 プロシージャ名 = 計算式(仮引数名を用いて記述) End Function
ユーザー定義関数:1変数関数
Sub kansu1() Dim x As Double, y As Double x = Rane("A1").Value y = func1(x) Range("B1").Value = y End Sub Function func1(z As Double) As Double func1 = z*Exp(-z^2)*Sin(3*z + 1) End Function
複数の引数を持つファンクションプロシージャを定義することもできる.
呼び出し側:
変数名 = プロシージャ名(実引数1, 実引数2, …)
サブルーチン側:
Function プロシージャ名(仮引数1 As データ型, 仮引数2 As データ型, …) As データ型 プロシージャ名 = 計算式(仮引数で記述) End Function
ユーザー定義関数:2変数関数
Sub kansu2() Dim x As Double, y As Double, z As Double x = Range("A1").Value y = Range("B1").Value z = func2(x,y) Range("C1").Value = z End Sub Function func2(x1 As Double, x2 As Double) As Double func2 = ( (x1^2+x2^2)^2-6*(x1^2+x2^2)-3 )*Exp(-(x1^2+x2^2-1)/2) End Function
「ex_051x」のサブプロシージャの下に新しい Sub プロシージャ「ex_052a または ex052b または ex052c」を作成します.
次のどれかの場合のプログラムを作成しましょう.
入出力は,セルに各生物種間の係数(解説参照)と最初の個体数,世代数,結果(指定した世代での個体数)の表で行う.表の構成は,作成するプログラムに応じて変えてください.
使用するセルはこのプログラムを登録するボタンの下に来る位置のセルにする.Sheet4にフォームコントロールのボタンを挿入し,プログラムを登録して名前を ex_052a または ex052b または ex052c に変える,ボタンはex_11の横に適当な間隔を開けて配置する.
\(A_{ij}\) | 自然増殖 | 植物 | 草食 | 肉食 |
---|---|---|---|---|
植物 | 数値(> 0) | 数値 (< 0) | 数値 (< 0) | 0 |
草食 | 0 | 数値 (> 0) | 数値 (< 0) | 数値 (< 0) |
肉食 | 0 | 0 | 数値 (> 0) | 数値 (< 0) |
世代数 | 0 | (世代数を入力) |
---|---|---|
植物 | (初期数値を入力) | |
草食 | (初期数値を入力) | |
肉食 | (初期数値を入力) |
植物は一定の割合で増殖し,その個体数と草食動物の数に比例した割合で減少する. 草食動物は,植物の数に比例した割合で増殖し,その個体数と肉食動物の数の両者に比例した割合で減少する. 肉食動物は,草食動物の数に比例した割合で増殖し,その個体数に比例した割合で減少する.
個体数の世代間変化は, \(x_n, y_n, z_n\)を\(n\)世代目の植物,草食動物,肉食動物の個体数とし,\(A_{ij}\)を各関係を表す係数として考えると以下のように表される.全てをかなり小さい値にしないとすぐ絶滅します.
\(A_{ij}\) | x | y | z |
---|---|---|---|
x | 植物による変化率(生存競争):\(A_{xx}\) < 0 | 草食による変化率(食われる):\(A_{xy}\) < 0 | 肉食による変化率(食われない):\(A_{xz}\) = 0 |
y | 植物による変化率(食べる):\(A_{yx}\) > 0 | 草食による変化率(生存競争):\(A_{yy}\) < 0 | 肉食による変化率(食われる):\(A_{yz}\) < 0 |
z | 植物による変化率(食べない):\(A_{zx}\) = 0 | 草食による変化率(食べる):\(A_{zy}\) > 0 | 肉食による変化率(生存競争):\(A_{zz}\) < 0 |
自然増殖:\(A_{x}\) > 0,\(A_y = 0\) ,\(A_z = 0\):勝手に増えるのは植物だけ.
次の世代における個体数は次のような式で決まる(係数が0の場合も書いてある).ただし,個体数が負になった場合は0にする. \begin{align} x_{n+1} &= x_n + A_{x}x_n + A_{xx}x_n^2 + A_{xy}x_ny_n + A_{xz}x_nz_n\\ y_{n+1} &= y_n + A_{y}y_n + A_{yx}y_nx_n + A_{yy}y_n^2 + A_{yz}y_nz_n\\ z_{n+1} &= z_n + A_{z}z_n + A_{zx}z_nx_n + A_{zy}z_ny_n + A_{zz}z_n^2\\ \end{align} ヒント:Int(number) とすると数値 number の整数部分を取り出せる.