静粛に、只今統計勉強中

仕事でデータ分析をすることになったバリバリ文系アラフィフのおっさんが、独学で統計の勉強を始めました。

Excel VBAで『マンガでわかる統計学 回帰分析編』のロジスティック回帰分析の手順をマクロにしてみた3

前回までの2回でロジスティック回帰分析のマクロのソースコードについて説明しました。今回は、ソースコード全文を掲載して、使い方と実行結果について説明したいと思います。

 

ソースコード


'ロジスティック回帰分析を行なう
Sub Logistic_Regression()

    Application.ScreenUpdating = False

    Dim a, b(1 To 2), c, i, j, k, L, n, x
    Dim eq, n1, n2
    Dim buf1, buf2, buf3
    Dim Rng As Range, y As Range, haty As Range

    c = Selection.Columns.Count - 1         '説明変数の数
    n = Selection.Rows.Count - 1            '観測数
    ReDim a(1 To 2, 1 To c)                 '回帰係数のアドレス取得用
    Set Rng = Selection(1)                  '基準セル

    With Rng
        Set y = .Offset(1, c).Resize(n)     '目的変数
        Columns(.Column + c).Insert         'ダミーの列を追加
        .Offset(1, c).Resize(n).Value = 1   'ダミー列に「1」を入力
        x = .Offset(1).Resize(n, c + 1)     '説明変数+ダミー
        Columns(.Column + c).Delete         'ダミーの列を削除

        .Offset(, c + 1).ColumnWidth = 1
        .Offset(, c + 3).ColumnWidth = 1
        .Offset(, c + 5).ColumnWidth = 2
        Columns(.Column).Copy Columns(.Column + c + 2)
        Columns(.Column).Copy Columns(.Column + c + 4)
        
        .Offset(1, c + 6).Value = "尤度関数"                '尤度関数のラベル
        .Offset(1, c + 7).FormulaR1C1 = "=PRODUCT(C[-3])"   '尤度関数の数式
        .Offset(2, c + 6).Value = "対数尤度関数"            '対数尤度関数のラベル
        With .Offset(2, c + 7)
            .FormulaR1C1 = "=LN(R[-1]C)"                    '対数尤度関数の数式
            L = .Address                                    '対数尤度関数のアドレス
        End With
        With .Offset(1, c + 6).Resize(2, 2)                 '罫線
            .Borders.LineStyle = True
            .Borders(xlInsideHorizontal).LineStyle = False
        End With
        
        .Offset(4, c + 7).Value = "回帰係数"
        .Offset(4, c + 8).Value = "オッズ比"
        .Offset(4, c + 9).Value = "Wald検定"
        For i = 1 To c
            .Offset(i + 4, c + 6).Value = .Offset(, i - 1).Value            '説明変数のラベル
            a(1, i) = .Offset(i + 4, c + 7).Address(ReferenceStyle:=xlR1C1) '回帰係数のアドレス
            a(2, i) = .Offset(i + 4, c + 7).Address                         '回帰係数のアドレス
            '回帰式の組み立て
            eq = eq & a(1, i) & "*" & .Offset(c + i + 11, c + 7).Address(ReferenceStyle:=xlR1C1) & "+"
        Next
        .Offset(c + 5, c + 6).Value = "定数"                        '定数項のラベル
        With .Offset(4, c + 6).Resize(c + 2, 4)                     '罫線
            .Borders.LineStyle = True
            .Borders(xlInsideHorizontal).LineStyle = False
            .Resize(1).Borders.LineStyle = True
        End With
        b(1) = .Offset(c + 5, c + 7).Address(ReferenceStyle:=xlR1C1)    '定数項のアドレス
        b(2) = .Offset(c + 5, c + 7).Address                            '定数項のアドレス
        eq = eq & b(1)                                                  '回帰式の組み立て

        .Offset(c + 7, c + 6).Value = "寄与率"
        .Offset(c + 8, c + 6).Value = "判別的中率"
        .Offset(c + 9, c + 6).Value = "尤度比検定"
        With .Offset(c + 7, c + 6).Resize(3, 2)             '罫線
            .Borders.LineStyle = True
            .Borders(xlInsideHorizontal).LineStyle = False
        End With
        
        .Offset(c + 11, c + 6).Value = "予測"
        For i = 1 To c
            .Offset(c + i + 11, c + 6).Value = .Offset(, i - 1).Value   '説明変数のラベル
        Next
        .Offset(c * 2 + 12, c + 6).Value = "確率"
        With .Offset(c * 2 + 12, c + 7)
            .FormulaR1C1 = "=1/(1+EXP(-(" & eq & ")))"                  '予測用の回帰式
            .NumberFormatLocal = "0.0%"
        End With
        With .Offset(c + 12, c + 6).Resize(c, 2)                        '罫線
            .Borders.LineStyle = True
            .Borders(xlInsideHorizontal).LineStyle = False
            With .Offset(c).Resize(1).Borders
                .LineStyle = True
                .Weight = xlMedium
            End With
            .Resize(c + 1).Borders(xlInsideVertical).Weight = xlThin
        End With
        
        .Offset(, c + 6).Resize(, 4).EntireColumn.AutoFit

        .Offset(, c + 2).Value = "予測値"
        eq = ""                                             '回帰式をリセット
        For i = 1 To c
            eq = eq & a(1, i) & "*RC[-" & c - i + 3 & "]+"  '回帰式の組み立て
        Next
        eq = eq & b(1)                                      '回帰式の組み立て
        Set haty = .Offset(1, c + 2).Resize(n)              '予測値のセル範囲
        haty.FormulaR1C1 = "=1/(1+EXP(-(" & eq & ")))"      '回帰式を代入
        
        .Offset(, c + 4).Value = "(尤度関数の計算過程)"
        .Offset(1, c + 4).Resize(n).FormulaR1C1 = "=IF(RC[-4]=1,RC[-2],1-RC[-2])"
    End With

    'ソルバーで対数尤度関数の最大値を求める
    SolverOk SetCell:=L, MaxMinVal:=1, ValueOf:=0, ByChange:=a(2, 1) & ":" & b(2), _
        Engine:=1, EngineDesc:="GRG Nonlinear"
    SolverSolve userfinish:=True

    With Application.WorksheetFunction
        n1 = .CountIf(y, 1) '事象が起きた回数
        n2 = .CountIf(y, 0) '事象が起きなかった回数
        k = n1 * .Ln(n1) + n2 * .Ln(n2) - (n) * .Ln(n)
        '寄与率
        Rng.Offset(c + 7, c + 7).Value = 1 - Range(L).Value / k
        '判別的中率
        Rng.Offset(c + 8, c + 7).Value = (.CountIfs(y, 1, haty, ">=0.5") + .CountIfs(y, 0, haty, "<0.5")) / n
        '尤度比検定
        Rng.Offset(c + 9, c + 7).Value = .ChiSq_Dist_RT(2 * (Range(L).Value - k), c)
        'オッズ比
        For i = 1 To c
            Rng.Offset(i + 4, c + 8).Value = Exp(Rng.Offset(i + 4, c + 7).Value)
        Next
        'Wald検定
        ReDim buf1(1 To c + 1, 1 To n)
        For j = 1 To n
            buf1(c + 1, j) = haty(j) * (1 - haty(j))
            For i = 1 To c
                buf1(i, j) = x(j, i) * buf1(c + 1, j)
            Next
        Next
        buf2 = .MMult(buf1, x)
        buf3 = .MInverse(buf2)
        For i = 1 To c + 1
            Rng.Offset(i + 4, c + 9).Value = .ChiSq_Dist_RT(Rng.Offset(i + 4, c + 7) ^ 2 / buf3(i, i), 1)
        Next
    End With

    ActiveWindow.DisplayGridlines = False
    Application.Calculation = xlAutomatic
    Application.ScreenUpdating = True

End Sub

 

使い方と実行結果

使い方は、毎度おなじみ、ソースコード全行をVBEで標準モジュールにコピペしたら、データ範囲を選択して、[開発]→[マクロ]→[Logistic_Regression]を選択→【実行】をクリックするだけ、です。
f:id:cyclo-commuter:20180215090144p:plain

使用上の注意が4つあります。

  1. 必ず表にラベル行を作っておいてください。
  2. 必ず説明変数をに、目的変数を一番に配置してください。
  3. 必ずラベル行からセル範囲を選択してください。
  4. あらかじめソルバーを使える状態にしておいてください。
    f:id:cyclo-commuter:20180214141431j:plain

クリック後1~数秒で以下のように結果が出力されます。 

f:id:cyclo-commuter:20180214142018p:plain

出力について一つずつ確認してみましょう。

 

f:id:cyclo-commuter:20180214143832p:plain
回帰係数と定数です。この値を使って予測値と予測の確率を計算しています。
試しに、日曜日の最高気温23℃でノルンスペシャルが売れる確率を計算してみると、
f:id:cyclo-commuter:20180214144305p:plain
44.1%と出ました。日曜日であっても、最高気温23℃では売れないようですね。

 

f:id:cyclo-commuter:20180214144617p:plain
オッズ比について、『マンガでわかる統計学 回帰分析編』には計算の仕方は書かれていたのですが、値の意味の説明はありませんでした。
なので、例によってグーグル先生に頼ってみたら、 

『オッズ比』について &#8211; 原因と結果の関係性,傾向を知る方法funmatu.wordpress.com

この 2サイトの説明がとても分かりやすかったです。ただし、順序尺度と言っても

説明変数のデータ単位が全て同じ場合はオッズ比は寄与順位に適用できます。
データ単位が異なる場合、重回帰分析同様、回帰係数の単純比較はできません。
(重回帰の場合は標準回帰係数で寄与順位をします。)
Wald統計量は(回帰係数÷標準誤差)の2乗で検定統計量です。
標準誤算で割ることによって基準化され、寄与順位に適用できます。

ロジスティック回帰 :: 株式会社アイスタット|統計分析研究所

だそうですので、そのままじゃ使えない。正直オッズ比、扱いづらいです。

 

f:id:cyclo-commuter:20180214152420p:plain
それに引き換え、マルチに使えるのがWald検定のp値。

  1. 回帰係数を個別に検定できます(お好みの有意水準でどうぞ)。
  2. オッズ比も個別に検定できます(お好みの有意水準でどうぞ)。
  3. Wald統計量の代わりに寄与順位に適用できます(値が小さいほうが上位)。

 

f:id:cyclo-commuter:20180214153901p:plain

(寄与率は)精度が高いほど1に近づき、そうじゃないときほど0に近づくのよ。
ロジスティック回帰式の寄与率は大きくなりづらい傾向があるといわれているから、参考程度にしておいたほうがいいかもね。
『マンガでわかる統計学 回帰分析編』171ページ

との由。で、もう一つの方法として誤判別率についての説明があるのですが、私の方はこれを裏返して判別的中率としてみました。

尤度比検定は、「回帰係数を包括的に検討する検定」(175ページ)だそうです。こちらも、お好みの有意水準でどうぞ。

 

以上で説明終了です。
WEBで検索していると、まだまだアレコレ足せそうですが、今回はここまで。