データ行列に対する計算で役立ちそうな関数
VBAを使用して簡単な計算プログラムを作る際に便利だと思われるものをいくつか紹介しておきます。通常は配列を宣言すると、配列の要素番号は0から始まりますが、以下のものはすべて配列の要素番号が1からであることを前提に書いています。
ちなみに、私はワークシート上からデータを読み込むときにユーザフォームのRefEditを使うのが好きですが、Application.InputBoxを使用しても良いです。ただし、1行のみのデータ範囲、1列のみのデータ範囲を指定する場合は両者の読み込み方に違いがあるので注意してください。
dim myRange as string
dim dat as variant
dat = range(myRange)
としたときはdatには2次配列(行列)としてデータが格納されています。一方で、
dim myRange as range
dim dat as variant
Set myRange = Application.Inputbox()
dat = myRange
とした場合、datには1次配列(ベクトル)としてデータが格納されます。つまり、n行m列という概念(区別)がないのです。
ホント、Excelってのは面倒ですね。。。
それから、引数として渡された1次配列(ベクトル)や2次配列(行列)は勝手に要素の最小値が0にされているのかもしれないので注意!! これが起こると、コードを走らせたときに"インデックスの範囲が有効ではありません"とエラーが生じる。
■ Variant型の変数に代入されているデータ行列の行合計と列合計を求める
Function dimsum(dat As Variant, x As Integer) As Variant Dim i As Integer, j As Integer Dim nrow As Integer, ncol As Integer Dim rowsum() As Variant, colsum() As Variant Dim mySum As Double nrow = UBound(dat, 1) ' データ行列の行数を取得 ncol = UBound(dat, 2) ' データ行列の列数を取得 ReDim rowsum(nrow) ' 各行の合計を格納する配列 ReDim colsum(ncol) ' 各列の合計を格納する配列 If x = 1 Then For i = 1 To nrow 'x = 1なら行合計 mySum = 0 For j = 1 To ncol mySum = mySum + dat(i, j) Next rowsum(i) = mySum Next dimsum = rowsum ' 行合計が格納された配列を返す ElseIf x = 2 Then 'x = 2なら列合計 For i = 1 To ncol mySum = 0 For j = 1 To nrow mySum = mySum + dat(j, i) Next colsum(i) = mySum Next dimsum = colsum ' 列合計が格納された配列を返す Else MsgBox "引数の指定が間違っています[x = 行:1 or 列:2]" End If End Function
〜使用方法〜
例えば、RefEdit1つとコマンドボタン1つをつけた簡単なユーザーフォームを作成し、コマンドボタンに次のようにプログラムを書けばよい。結果の表示にはこのページの下にあるvprint()を利用すると簡単。
Private Sub CommandButton1_Click() Dim myRange As String Dim dat As Variant Dim tameshi As Variant myRange = RefEdit1.Text dat = Range(myRange) tameshi = dimsum(dat, 1) 'この場合は行合計を求める vprint (tameshi) ' vprint()を利用してワークシート上に表示 Unload UserForm1 End Sub
■ Variant型の変数に代入されているデータ行列の行平均と列平均を求める
Function dimmean(dat As Variant, x As Integer) As Variant Dim i As Integer, j As Integer Dim nrow As Integer, ncol As Integer Dim rowmean() As Variant, colmean() As Variant Dim mySum As Double, myMean As Double Dim count As Integer Dim FT As String nrow = UBound(dat, 1) ' データ行列の行数を取得 ncol = UBound(dat, 2) ' データ行列の列数を取得 ReDim rowmean(nrow) ReDim colmean(ncol) If x = 1 Then For i = 1 To nrow 'x = 1なら行平均 mySum = 0 count = 0 For j = 1 To ncol mySum = mySum + dat(i, j) FT = IsEmpty(dat(i, j)) If FT = "True" Then count = count + 1 '欠損値のカウント End If Next myMean = mySum / (ncol - count) rowmean(i) = myMean '列jの平均値を配列へ格納 Next dimmean = rowmean ElseIf x = 2 Then 'x = 2なら列平均 For i = 1 To ncol mySum = 0 count = 0 For j = 1 To nrow mySum = mySum + dat(j, i) FT = IsEmpty(dat(j, i)) If FT = "True" Then count = count + 1 '欠損値のカウント End If Next myMean = mySum / (nrow - count) colmean(i) = myMean ' 行jの平均を配列へ格納 Next dimmean = colmean Else MsgBox "引数の指定が間違っています[x = 行:1 or 列:2]" End If End Function
〜使用方法〜
行合計を求める関数dimsum()と同じ。
■ 1次配列(ベクトル)を2次配列(行列)へ変換する
Function mconv(vector As Variant, nrow As Integer, ncol As Integer, byRow As String) As Variant Dim i As Integer, j As Integer, k As Integer Dim matrix() As Variant ReDim matrix(1 To nrow, 1 To ncol) 'nrow行、ncol列の2次配列を作成 If byRow = "T" Then ' 引数のbyRowがTなら行単位で変換 k = 0 For i = 1 To nrow For j = 1 To ncol matrix(i, j) = vector(k) k = k + 1 Next Next ElseIf byRow = "F" Then ' 引数のbyRowがFなら列単位で変換 k = 0 For i = 1 To ncol For j = 1 To nrow matrix(j, i) = vector(k) k = k + 1 Next Next Else MsgBox "引数の指定が誤っています[byRow = T or F]" End If mconv = matrix ' 行列に変換されたものを返す End Function
〜使用方法〜
結果の表示にはこのページの下にあるdprint()を使用するとラク。
Sub sample() Dim vec() As Variant Dim mat As Variant ReDim vec(1 To 6) vec = Array(1, 2, 3, 4, 5, 6) mat = mconv(vec, 2, 3, "T") dprint(mat) End Sub
■ 2次配列(行列)から1次配列へと変換
Function vconv(matrix As Variant) As Variant Dim nrow As Integer, ncol As Integer Dim myVector() As Variant Dim i As Integer, j As Integer, k As Integer nrow = UBound(matrix, 1) ncol = UBound(matrix, 2) ReDim myVector(nrow * ncol) k = 0 For i = 1 To ncol For j = 1 To nrow k = k + 1 myVector(k) = matrix(j, i) Debug.Print myVector(k) Next Next vconv = myVector End Function
〜使用方法〜
最初にいったようなユーザーフォームを作成して次のようにコードを書く。
Private Sub CommandButton1_Click() Dim myRange As String Dim dat As Variant Dim tameshi As Variant myRange = RefEdit1.Text dat = Range(myRange) tameshi = vconv(dat) vprint(tameshi) Unload UserForm1 End Sub
■ 1次配列(ベクトル)の中身をワークシート上に表示する
Function vprint(vector As Variant) Dim length As Integer, i As Integer Dim res As Range length = UBound(vector, 1) Set res = Application.InputBox(prompt:="データを表示させるセルを選択してください", Title:="データ表示", Type:=8) For i = 1 To length res.Cells(1, i) = vector(i) Next End Function
〜使用方法〜
単に引数に1次配列を指定するだけ
■ 2次配列(行列)の中身をワークシート上に表示する
Function dprint(dmatrix As Variant) Dim nrow As Integer, ncol As Integer Dim i As Integer, j As Integer Dim res As Range nrow = UBound(dmatrix, 1) ncol = UBound(dmatrix, 2) Set res = Application.InputBox(prompt:="データを表示させるセルを選択してください", Title:="データ表示", Type:=8) For i = 1 To nrow For j = 1 To ncol res.Cells(i, j) = dmatrix(i, j) Next Next End Function
〜使用方法〜
単に引数に2次配列を指定するだけ
追記
以上のものは完全なものではない、、、というか、データの取得方法によって左右されるので、参考程度にして実際に使用する際には細かいトコは自分で勝手に変更してください(私自身も細かい所まで試していない)。というか、こんなことをするくらいならRを使ってください。