【VBA編】データの標準化
前回はそれぞれのシートにCSVデータを読み込む処理を作成しました。
今回は読み込んだデータを標準化する処理を書いていきます。
標準化
「標準化」とは、データを「平均が0、分散が1になるように変換する」ことです。
標準化を行うことで、学習の効率がよくなるそうなので、今回使用するデータについても標準化することにします。
グラフで見るとこんな感じ。
がくの長さを x 軸、がくの幅を y 軸にとってデータをプロットしています。
青い点が標準化前のデータ(元のデータ)、オレンジの点が標準化後のデータです。
標準化後のデータは原点 O の周辺に集まっているのがわかると思います。
標準化の計算
標準化は個々のデータについておこなっていくのですが、Excelの関数では、
=STANDARDIZE(x, 平均, 標準偏差)
を使って求めることができます。
なお、引数の x は 元の値ですが、
平均は「=AVERAGE(データ)」
標準偏差は「=STDEV.P(データ)」
でそれぞれ求めることができます。
標準化後の値を z とすると、数式では以下のようにあらわされます。
(今回のコードではワークシート関数を使うのでこの式は利用しません。)
では、標準化のコードを書いていきましょう。
必要なシートの追加
訓練データとテストデータそれぞれの標準化後のデータを格納するシートを追加します。
標準化後のデータを入力データとして利用するので、それぞれのシート名には「(入力)」とつけています。
オブジェクト名はそれぞれ「ws_Train_Data_Input」、「ws_Test_Data_Input」としました。
定数の宣言
標準モジュール「G」に以下の列挙型の定数を宣言しておきます。
「ラベル列」というのは、データの正解(値)が格納されている列です。
今回であれば「アヤメの種類」が格納されている列です。
'[G - 標準モジュール] Public Enum RW DATA_TITLE = 1 'データのタイトル行 DATA_START = 2 'データの開始行 End Enum Public Enum CL DATA_LABEL = 1 'データのラベル列 DATA_START = 2 'データの開始列 End Enum
標準化のコード
「mdlReadData」に以下のコードを書きましょう。
引数として、元データのあるシート、書き込み先のシートを取っています。
元データのあるシートは、「ws_Train_Data」もしくは「ws_Test_Data」
書き込み先のシートは、「ws_Train_Data_Input」もしくは「ws_Test_Data_Input」
が呼び出し元でセットされます。
'[mdlReadData - 標準モジュール] 'ワークシートのデータを標準化して書き込み先のワークシートに書き込む '[引数] <- aWsData : Worksheet / 元データがあるシート, aWsStandardized : Worksheet / 標準化したデータを書き込む先のシート '[戻り値] -> なし Private Sub standardizeData(ByRef aWsData As Worksheet, ByRef aWsStandardized As Worksheet) Dim r As Long Dim c As Long Dim i As Long Dim eRow As Long Dim eCol As Long Dim mean As Double '平均値 Dim std As Double '標準偏差 aWsStandardized.Cells.Clear With aWsData '最終行と最終列を取得する eRow = .Cells(Rows.Count, 1).End(xlUp).Row eCol = .Cells(1, Columns.Count).End(xlToLeft).Column '列ごとに処理 For c = CL.DATA_START To eCol '平均値と標準偏差を求める mean = WorksheetFunction.Average(.Range(.Cells(RW.DATA_START, c), .Cells(eRow, c)).Value) std = WorksheetFunction.StDev_P(.Range(.Cells(RW.DATA_START, c), .Cells(eRow, c)).Value) '標準化を行う For r = RW.DATA_START To eRow aWsStandardized.Cells(r, c).Value = WorksheetFunction.Standardize(CDbl(.Cells(r, c).Value), mean, std) Next Next 'タイトル行とラベル列をコピー貼り付け .Rows(RW.DATA_TITLE).Copy Destination:=aWsStandardized.Rows(RW.DATA_TITLE) .Columns(CL.DATA_LABEL).Copy Destination:=aWsStandardized.Columns(CL.DATA_LABEL) End With End Sub
1列目はラベルなので、2列目から処理を開始します。(CL.DATA_START は 2です。)
列ごとにワークシート関数で平均「mean」と標準偏差「std」を求めています。
平均と標準偏差が求まったら、元データの値を「=STANDARDIZE(x, 平均, 標準偏差)」のワークシート関数を使って標準化し、標準化後の値を書き込み先のシートに書き込みます。
すべての列について上記処理を行い、最後にタイトル行とラベル列を書き込み先シートにコピーして終了です。
標準化処理の呼び出し
では、上記の標準化処理を呼び出してデータを標準化しましょう。
前回作成した「Click_データ読み込み()」プロシージャにコードを追加します。
追加するのは、
[ Call standardizeData(ws_Train_Data, ws_Train_Data_Input) ]
[ Call standardizeData(ws_Test_Data, ws_Test_Data_Input) ]
の2行です。
それ以外の変更点はありません(コメントを除く)。
'[mdlReadData - 標準モジュール] 'データの読み込み Public Sub Click_データ読み込み() Dim filePath As String Application.ScreenUpdating = False '訓練データの読み込み filePath = Range(G.RNG_TRAIN_DATA_PATH).Value Call readData(ws_Train_Data, filePath) '訓練データを標準化する Call standardizeData(ws_Train_Data, ws_Train_Data_Input) 'テストデータの読み込み filePath = Range(G.RNG_TEST_DATA_PATH).Value Call readData(ws_Test_Data, filePath) 'テストデータを標準化する Call standardizeData(ws_Test_Data, ws_Test_Data_Input) Application.ScreenUpdating = True MsgBox "データを読み込みました。", vbOKOnly + vbInformation End Sub
処理の確認
では、メインシートの「データ読み込み」ボタンをクリックしてデータを読み込んでみましょう。
「訓練データ(入力)」シート、「テストデータ(入力)」シートにそれぞれ標準化後の値が書き込まれていると思います。
【訓練データ(入力)】シート(ws_Train_Data_Input)
【テストデータ(入力)】シート(ws_Test_Data_Input)