【HTML】別HTMLを埋め込む(iframe)

作成したHTMLテーブルを別のHTMLファイルに埋め込んで表示させたい!と思ったので、その方法をメモ。

HTMLの埋め込みは、iframe タグで実装できる。
<iframe src='パス/ファイル名.html'></iframe>


試しに前回記事で作ったHTMLテーブルを埋め込んでみる。
【VBA & HTML】ExcelのテーブルをHTML形式に変換する - こちにぃるの日記

HTMLは以下。

<!DOCTYPE html>
<html lang='ja'>
<head>
  <meta charset='utf8'>
  <title>Top</title>
  <link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
  <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>
  <style type='text/css'>  
    #header{
      background-color: #CBFFD3;
      position: fixed;
      top: 0px;
      left: 0px;
      width: 100%;
      height: 75px;
      border: 5px solid #fff;
    }
    #main_body{
      padding:75px 0 75px;
      top: 75px;
    }
    .side {
      background-color:#FFFFEE;
      width:10%;
      overflow-y: scroll;
      border: 5px solid #fff;
    }
    .fixed{
      position:fixed;
      top:75px;
      width: 300px;
    }
    .content {
      width:90%;
      margin-left:20px;
    }
    article {
      display:flex;
    }
  </style>
  <script type='text/javascript'>
      $(function($) {
        var tab = $('.sidebar'),
        offset = tab.offset();
        $(window).scroll(function () {
          if($(window).scrollTop() > offset.top) {
            tab.addClass('fixed');
          } else {
            tab.removeClass('fixed');
          }
        });
      });
  </script>
</head>
<body>

  <div id='header' style='font-size:30px;'>タイトルXXX</div>
  
  <article id='main_body'>
  
    <div class='side'>
      <div class='sidebar'>
        <p>目次</p>
        <ol>
          <li><a href='#Table1'>テーブル1</a></li>
          <li><a href='#Table2'>テーブル2</a></li>
          <li><a href='#Table3'>テーブル3</a></li>
        </ol>
      </div> 
    </div> 
    
    <div class='content'>
      <hr/>
      <div id='Table1'>■テーブル1</div>
      <iframe width='90%' height='500px' src='./SampleHTMLtable.html' frameborder='0'></iframe>
      <br/>
      
      <hr/>
      <div id='Table2'>■テーブル2</div>
      <iframe width='40%' height='200px' src='./SampleHTMLtable.html' frameborder='0'></iframe>
      <br/>
      
      <hr/>
      <div id='Table3'>■テーブル3</div>
      <iframe width='70%' height='500px' src='./SampleHTMLtable.html' frameborder='0'></iframe>
      <br/>
      
    </div>
    
  </article>
  
</body>
</html>


うまくHTMLテーブルを表示できた。 f:id:cochineal19:20210829160959p:plain

サイドメニューの設定は以下サイト様を参考にさせていただきました。
直帰率改善に!スクロールしてもサイドバーを固定して記事一覧を表示する方法 | イズクル

【VBA & HTML】ExcelのテーブルをHTML形式に変換する

Excelを開いて表を見るのは面倒だったりする。
Excelの起動を待ったり、別作業でExcelを使っていたりするとき)

そんなときブラウザで表形式データが見れたら便利だ、というモチベーションでExcelで作成したテーブルをHTMLに変換するプログラムを作ってみた。

Excel の準備


こんな感じでExcelを準備。セル色:グレーに設定情報、白に実データが入ります。
f:id:cochineal19:20210829152628p:plain
* 3行目:HTMLタイトル入力。
* 5行目:Table対象にする列に「1」を設定し、該当列のみHTMLに出力。
* 6行目:aタグ用にURLを入力した列の番号を指定。例だと7列目にPDFのURLを指定。
* 7行目:imgタグ用にURLを入力した列の番号を指定。例だと8列目にjpegファイルのURLを指定。
* 8行目:当該列の列幅を指定。
* 9行目:ヘッダー名を入力。
* 10行目以降:実データを入力。

VBAコード


このエクセルにHTML生成用のVBAコードを仕込む。

Option Explicit

Sub CreateHTMLTable()
'---------------------------------------
' 設定
'---------------------------------------
    On Error GoTo ERROR1
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False
    
    Dim myWB As Workbook, myWS As Worksheet
    Dim strow, enrow, stcol, encol, t_row, a_row, i_row, w_row, h_row, r, c As Long
    Dim outfile As String
    Dim adoSt As Object

    Set adoSt = CreateObject("ADODB.Stream")
    Const adTypeBinary = 1
    Const adTypeText = 2
    Const adCR = 13
    Const adLF = 10
    Const adCRLF = -1
    Const adWriteChar = 0
    Const adWriteLine = 1
    Const adSaveCreateNotExist = 1
    Const adSaveCreateOverWrite = 2
    
    Set myWB = ThisWorkbook
    Set myWS = myWB.Sheets(ActiveSheet.Name)
    
    t_row = 5
    a_row = 6
    i_row = 7
    w_row = 8
    h_row = 9
    
    strow = 10
    enrow = myWS.UsedRange.Find("*", , xlFormulas, , xlByRows, xlPrevious).Row
    
    stcol = 2
    encol = myWS.UsedRange.Find("*", , xlFormulas, , xlByColumns, xlPrevious).Column
   
'---------------------------------------
' HTMLテーブル生成
'---------------------------------------
    outfile = Replace(myWS.Cells(3, 2), "\", "_")
    outfile = Replace(outfile, "/", "_")
    outfile = Replace(outfile, ":", "_")
    outfile = Replace(outfile, "*", "_")
    outfile = Replace(outfile, "?", "_")
    outfile = Replace(outfile, """", "_")
    outfile = Replace(outfile, "<", "_")
    outfile = Replace(outfile, ">", "_")
    outfile = Replace(outfile, "|", "_")
    outfile = Replace(outfile, " ", "_")
    outfile = Replace(outfile, " ", "_")
    

    With adoSt
        .Charset = "UTF-8"
        .Type = adTypeText
        .LineSeparator = adLF
        .Open
        
        .WriteText "<!DOCTYPE html>", adWriteLine
        .WriteText "<html lang='ja'>", adWriteLine
        
        .WriteText "<head>", adWriteLine
        .WriteText "  <meta charset='utf8'>", adWriteLine
        .WriteText "  <title>SampleHTMLtable</title>", adWriteLine
        
        .WriteText "  <!-- Bootstrap -->", adWriteLine
        .WriteText "  <link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>", adWriteLine
        .WriteText "  ", adWriteLine
        .WriteText "  <!-- jQuery -->", adWriteLine
        .WriteText "  <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>", adWriteLine
        .WriteText "  ", adWriteLine
        .WriteText "  <!-- Datatables -->", adWriteLine
        .WriteText "  <link rel='stylesheet' href='https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.css'/> ", adWriteLine
        .WriteText "  <script src='https://cdn.datatables.net/t/bs-3.3.6/jqc-1.12.0,dt-1.10.11/datatables.min.js'></script>", adWriteLine
        .WriteText "  <script type='text/javascript'>", adWriteLine
        .WriteText "      jQuery(function($){", adWriteLine
        .WriteText "        $.extend( $.fn.dataTable.defaults, {", adWriteLine
        .WriteText "          language: {", adWriteLine
        .WriteText "          url: 'http://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Japanese.json'", adWriteLine
        .WriteText "          } ", adWriteLine
        .WriteText "        }); ", adWriteLine
        .WriteText "        $('#TableLayout1').DataTable({", adWriteLine
        .WriteText "          scrollX: true", adWriteLine
        .WriteText "          ,scrollY: '75vh'", adWriteLine
        .WriteText "          ,displayLength: 10", adWriteLine
        .WriteText "        });", adWriteLine
        .WriteText "      });", adWriteLine
        .WriteText "  </script>", adWriteLine
        .WriteText "</head>", adWriteLine
        
        .WriteText "<body>", adWriteLine
        .WriteText "  <table id='TableLayout1' border='1'>", adWriteLine
        
        .WriteText "    <thead style='background-color: #64F9C1;'>", adWriteLine
        .WriteText "      <tr>", adWriteLine
            For c = stcol To encol
                If myWS.Cells(t_row, c) = 1 Then
                    .WriteText "        <th width='" & myWS.Cells(w_row, c) & "px'>" & EscapeTxt(myWS.Cells(h_row, c)) & "</th>", adWriteLine
                End If
            Next c
        .WriteText "      </tr>", adWriteLine
        .WriteText "    </thead>", adWriteLine
        .WriteText "    <tbody>", adWriteLine
            
        For r = strow To enrow
            .WriteText "      <tr>", adWriteLine
                For c = stcol To encol
                    If myWS.Cells(t_row, c) = 1 Then
                        
                        If myWS.Cells(a_row, c) <> "" Then
                        
                            If myWS.Cells(r, myWS.Cells(a_row, c)) <> "" Then
                                .WriteText "        <td><a target='_blank' href='" & myWS.Cells(r, myWS.Cells(a_row, c)) & "'>" & EscapeTxt(myWS.Cells(r, c)) & "</a></td>", adWriteLine
                            Else
                                .WriteText "        <td>" & EscapeTxt(myWS.Cells(r, c)) & "</td>", adWriteLine
                            End If
                            
                        ElseIf myWS.Cells(i_row, c) <> "" Then
                        
                            If myWS.Cells(r, myWS.Cells(i_row, c)) <> "" Then
                                .WriteText "        <td><img src='" & myWS.Cells(r, myWS.Cells(i_row, c)) & "' title='" & EscapeTxt(myWS.Cells(r, c)) & "' width='" & myWS.Cells(w_row, c) & "px'></td>", adWriteLine
                            Else
                                .WriteText "        <td>" & EscapeTxt(myWS.Cells(r, c)) & "</td>", adWriteLine
                            End If
                            
                        Else
                        
                            .WriteText "        <td>" & EscapeTxt(myWS.Cells(r, c)) & "</td>", adWriteLine
                            
                        End If
                        
                    End If
                Next c
            .WriteText "      </tr>", adWriteLine
        Next r
        
        .WriteText "    </tbody>", adWriteLine
        .WriteText "  </table>", adWriteLine
        .WriteText "</body>", adWriteLine
        .WriteText "</html>", adWriteLine
        
        .SaveToFile ThisWorkbook.Path & "\" & outfile & ".html", adSaveCreateOverWrite
        .Close
    End With
   
    myWS.Cells(1, 1).Select
    MsgBox "出力しました"
    
    GoTo END1

'---------------------------------------
' 後始末
'---------------------------------------
ERROR1:
    MsgBox Err.Number & ":" & Err.Description
    
END1:
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False
    Set adoSt = Nothing
    Set myWB = Nothing
    Set myWS = Nothing

End Sub

Function EscapeTxt(InTxt As String) As String

    EscapeTxt = Replace(InTxt, "&", "&amp;")
    EscapeTxt = Replace(EscapeTxt, "<", "&lt;")
    EscapeTxt = Replace(EscapeTxt, ">", "&gt;")
    EscapeTxt = Replace(EscapeTxt, "'", "&#39;")
    EscapeTxt = Replace(EscapeTxt, """", "&quot;")
    EscapeTxt = Replace(EscapeTxt, " ", "&nbsp;")
    EscapeTxt = Replace(EscapeTxt, vbNewLine, "<br/>")
    EscapeTxt = Replace(EscapeTxt, vbCrLf, "<br/>")
    EscapeTxt = Replace(EscapeTxt, vbCr, "<br/>")
    EscapeTxt = Replace(EscapeTxt, vbLf, "<br/>")
    
End Function


HTMLは、Stream オブジェクト( ADODB.Stream )を使って UTF 形式で出力している。
Stream オブジェクト (ADO) - ActiveX Data Objects (ADO) | Microsoft Docs
Stream オブジェクトのプロパティ、メソッド、およびイベント - ActiveX Data Objects (ADO) | Microsoft Docs

また、HTMLテーブルにソート・フィルター機能等を付けたかったので DataTables を使用している。
DataTables を初めて使ったが、実装は簡単で見栄えも良い。表の幅とか高さとかは使う環境で微調整が必要。
DataTables | Table plug-in for jQuery
Options

HTMLファイルを生成


表に値を入れて、HTMLファイルを生成してみるとこんな感じ。
f:id:cochineal19:20210829154816p:plain

PDFのリンクも開け、画像も表示され、最低限の機能は揃えられたかな?

一元管理ができそうで、何より何より。


今回作ったHTMLテーブルを別のHTMLファイルに埋め込む方法は以下の記事で。
【HTML】別HTMLを埋め込む(iframe) - こちにぃるの日記

【VBA】パラメタを変えてスクリプトを大量生成

久々にVBA

R、PythonSAS ... なんでも良いが、
共有ロジックを外部ファンクションや外部マクロにしておいて、 パラメタを変えてぐるぐる回すことがある。
例えば、疾患名を変えて患者数を推移図にするなど。

そんなとき、スクリプトをいちいち作っていると面倒だし、ミスにもつながる。

だからVBAで一括生成してしまおうというお話。


こんな感じでエクセルに情報を埋めて、
f:id:cochineal19:20210828145754p:plain:w400

VBAコードを仕込んで実行すると、

Sub OutputScript()
'---------------------------------------
' 設定
'---------------------------------------
    On Error GoTo ERROR1
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False
    
    Dim myWB As Workbook, myWS As Worksheet
    Dim strow, enrow, encol, fnumber, i As Long
    Dim outpath As String
    
    Set myWB = ThisWorkbook
    Set myWS = myWB.Sheets(ActiveSheet.Name)
    
    strow = 7
    enrow = myWS.UsedRange.Find("*", , xlFormulas, , xlByRows, xlPrevious).Row
    
    outpath = myWS.Cells(3, 3)
    If outpath = "" Then outpath = myWB.Path
    
'---------------------------------------
' テキスト生成
'---------------------------------------
    For i = strow To enrow
    
        fnumber = FreeFile
        
        Open outpath & "\" & myWS.Cells(i, 2) For Output As #fnumber
        
        Print #fnumber, "#==============================================================================="
        Print #fnumber, "# ファイル名 : " & myWS.Cells(i, 2)
        Print #fnumber, "# 作  成  日 : " & Format(Now(), "yyyy/mm/dd")
        Print #fnumber, "# 作  成  者 : " & myWS.Cells(4, 3)
        Print #fnumber, "#==============================================================================="
        Print #fnumber, "#-------------------------------------------------------------------------------"
        Print #fnumber, "# 設定"
        Print #fnumber, "#-------------------------------------------------------------------------------"
        Print #fnumber, "library(tidyverse)"
        Print #fnumber, "setwd(パスを入力)"
        Print #fnumber, ""
        Print #fnumber, "#-------------------"
        Print #fnumber, "# ロード"
        Print #fnumber, "#-------------------"
        Print #fnumber, "Source('./myFunctionX.r')"
        Print #fnumber, ""
        Print #fnumber, "#-------------------"
        Print #fnumber, "# 実行"
        Print #fnumber, "#-------------------"
        Print #fnumber, "myFunctionX(PARAM1='" & myWS.Cells(i, 3) & "', PARAM2=" & myWS.Cells(i, 4) & ")"
        Print #fnumber, ""
        Print #fnumber, "#-------------------------------------------------------------------------------"
        Print #fnumber, "# End of File"
        Print #fnumber, "#-------------------------------------------------------------------------------"
        
        Close #fnumber

    Next i
    
    myWS.Cells(1, 1).Select
    MsgBox "出力しました"
    
    GoTo END1

'---------------------------------------
' 後始末
'---------------------------------------
ERROR1:
    MsgBox Err.Number & ":" & Err.Description
    
END1:
    Application.DisplayAlerts = False
    Application.ScreenUpdating = False
    Set myWB = Nothing
    Set myWS = Nothing

End Sub


一気にスクリプトが生成される(今回はRスクリプト)。
f:id:cochineal19:20210828145957p:plain:w350

スクリプトの中身はこんな感じ。外部ファンクションへの指定パラメタをスクリプト毎に変えて生成している。
f:id:cochineal19:20210828150239p:plain:w550
f:id:cochineal19:20210828150319p:plain:w550

これで少し楽ができる。めでたし、めでたし。

【Python】Kerasでロジスティック回帰

Kerasの勉強用にロジスティック回帰と同じ構造を作って実行してみる。

【目次】


基本の整理


ニューラルネットワークは入力層ー中間層ー出力層からなり、図示するとこんな感じ。

f:id:cochineal19:20210725104049p:plain:w350

このうち、ロジスティック回帰は中間層がない単純な形。

f:id:cochineal19:20210725104305p:plain:w220

活性化関数にはシグモイド関数が使われる。

\quad y= \dfrac{1}{1+exp \left( - w^{T} X \right)}


また、損失関数には交差エントロピー誤差が使われる。

\quad E=-\sum \{ t_{i} \cdot log\ y_{i} + \left(1-t_{i} \right) \cdot log \left( 1-y_{i} \right) \}


使用データ


Kaggleチュートリアルタイタニックデータを使う。

import numpy as np 
import pandas as pd
from scipy import stats
from sklearn import model_selection
 
#-- import
train = pd.read_csv("../input/titanic/train.csv")
test = pd.read_csv("../input/titanic/test.csv")

###--- 欠損処理
train["Age"] = train["Age"].fillna(train["Age"].median()) #--中央値で補完
train["Embarked"] = train["Embarked"].fillna(train["Embarked"].mode().iloc(0)) #-- 最頻値で補完 
test["Age"] = test["Age"].fillna(test["Age"].median()) #--中央値で補完
test["Fare"] = test["Fare"].fillna(test["Fare"].median()) #--中央値で補完
 
###--- カテゴリの数値化
train["Sex"] = train["Sex"].map({'male':0, 'female':1})
train = pd.get_dummies(train, columns=['Embarked'])
test["Sex"] = test["Sex"].map({'male':0, 'female':1})
test = pd.get_dummies(test, columns=['Embarked'])
 
###--- データ分割
Xval = ["Pclass", "Sex", "Age", "Fare", "Embarked_C", "Embarked_Q", "Embarked_S"]
df_y = train["Survived"].values
df_X = train[Xval].values
train_X, valid_X, train_y, valid_y = model_selection.train_test_split(df_X, df_y, random_state=19)
test_X = test[Xval].values


Keras での試し書き(モデル作成)


先ず下準備として乱数シードを固定する。
また、データを標準化した方が精度が良かったので標準化。

import pandas as pd
import numpy as np
import tensorflow as tf
from keras.layers import Activation, Dense, Dropout, BatchNormalization
from keras.models import Sequential
from keras import optimizers, callbacks
from keras.utils.np_utils import to_categorical
from sklearn.preprocessing import StandardScaler
 
#-- 乱数シード
np.random.seed(seed=19)
tf.random.set_seed(19)
 
#-- 標準化
sc = StandardScaler()
train_X_std = sc.fit_transform(train_X)
valid_X_std = sc.transform(valid_X)


モデルを作成する。
Sequential()インスタンス化し、add メゾッドでモデルを定義する。
units がユニット数。ロジスティック回帰だと層が一つなのでここが出力層の定義になる。
input_shape が入力層で特徴量の数。
activation が活性化関数。ロジスティック回帰だとシグモイド関数

#-- モデル
model = Sequential()
model.add(Dense(units=1
                ,input_shape=(len(Xval),)
                ,activation="sigmoid"))


作成モデルをコンパイルする。

#-- コンパイル
model.compile(optimizer=optimizers.SGD(lr=0.1)
              ,loss='binary_crossentropy'
              ,metrics=["accuracy"])


コンパイルしたモデルを最適化する。
callbacks.EarlyStopping で Early Stopping を実装できる。 callbacks.ModelCheckpoint('best_model.h5', save_best_only=True) でベストモデルを保存できる(後にモデルの読み込み方法を記載)。

#-- Fitting
callbacks = [
    callbacks.ModelCheckpoint('best_model.h5', save_best_only=True)
    ,callbacks.EarlyStopping(patience=10)
]
 
history = model.fit(train_X_std, train_y
                    ,batch_size=64
                    ,epochs=1000
                    ,verbose=1
                    ,callbacks=callbacks
                    ,validation_data=(valid_X_std, valid_y))


Keras での試し書き(バリデーションデータで評価)


作成モデルで予測する。

#-- 予測
pred_y = model.predict(valid_X_std)
pred_y = np.where(pred_y < 0.5, 0, 1)


重み係数を見たい場合、次で取得できる。

model.layers[0].get_weights()


今回の作成モデルを評価してみる。
ついでにsklearnで作ったロジスティック回帰を並べてみる。

from sklearn.linear_model import LogisticRegression
 
model2 = LogisticRegression(solver='liblinear', random_state=19)
model2.fit(train_X_std, train_y)
pred_y2 = model2.predict(valid_X_std)
pred_y2 = np.where(pred_y2 < 0.5, 0, 1)
from sklearn.metrics import classification_report, confusion_matrix
 
print("-- keras --")
print(confusion_matrix(valid_y, pred_y))
print(classification_report(valid_y, pred_y))
print()
print("-- sklearn.linear_model.LogisticRegression --")
print(confusion_matrix(valid_y, pred_y2))
print(classification_report(valid_y, pred_y2))

f:id:cochineal19:20210725114727p:plain:w400

アルゴリズムが違うだろうが、だいたい同じ?

Keras での試し書き(テストデータで予測)


別セッションで作成モデルを使用したい場合、最初からPGMを再実行するのは手間。
モデルを保存してあれば load_model(パス/ファイル名) で読み込んで再利用できる。

from keras.models import load_model
model3 = load_model('./best_model.h5')
pred_y3 = model3.predict(test_X)
pred_y3 = np.where(pred_y3 < 0.5, 0, 1)


参考


Home - Keras Documentation
Kerasによる2クラスロジスティック回帰 - 人工知能に関する断創録

本ブログで参考になりそうなもの
【統計】ロジスティック回帰分析 - こちにぃるの日記

【SAS】treeboostプロシジャ_Gradient Boosting Tree(勾配ブースティング木)

今回は Gradient Boosting Tree(勾配ブースティング木)を試してみる。

【目次】


決定木を学習器としたアンサンブル学習であり、ブースティングと言われる手法。
※XGBoost ではないのであしからず。

Pythonでの実装コード


Pythonでは scikit learn で実装できる。
sklearn.ensemble.GradientBoostingClassifier — scikit-learn 0.24.2 documentation

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report, confusion_matrix
 
mod1 = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0, max_depth=6, random_state=19)
mod1.fit(train_X, train_y)
pred_y = mod1.predict(valid_X)
 
print(confusion_matrix(valid_y, pred_y))
print(classification_report(valid_y, pred_y))



SASでの実装コード


SASでは treeboost プロシジャで実装できる。
任意の検証データを用いたい場合、assess validata = バリデーションデータ で指定する。

特徴量は input 特徴量 / level=尺度 で名義尺度(nominal)、順序尺度(ordinal)、間隔尺度(interval)を指定する。 何も指定しない場合、文字型は名義尺度、数値型は間隔尺度と見なされる。
本プロシジャでは Huber型 M-推定 を行うようで極端な外れ値の影響を受けにくい。

オプションはたくさんあるので、詳細は プロシジャガイド を参照。

proc treeboost data=train(where=(selected=0))
                iterations = 1000 /* pythonではn_estimators */
                maxdepth = 6      /* pythonではmax_depth */
                seed = 19
                ; 
 
    target 目的変数 / level=binary;
    input 名義尺度の特徴量 / level=nominal;
    input 順序尺度の特徴量 / level=ordinal order=ascending;
    input 間隔尺度の特徴量 / level=interval;
    
    assess validata = valid_dataset;
    subseries best;
    
    code file="gradboost.sas";
    save fit=_gb_fit importance=_gb_importance;
quit;
run;


SASでお試し(対象データ、データ加工)


今回もKaggleチュートリアルタイタニックデータを用いる。

データ加工は以前と同じなのでコードをたたんでいる。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

★コードを見る場合ここをクリック★

/*---------------------------------------------------
    欠損値処理、変数作成
---------------------------------------------------*/
*-- 数値型:欠損値を中央値に置き換え;
proc stdize data=train out=train method=median reponly;
    var age;
run;

*-- カテゴリ型:欠損値を最頻値に置き換え;
data train;
    set train;
    Embarked = coalescec(Embarked,"S");
run;

/*---------------------------------------------------
    データ分割
---------------------------------------------------*/
proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;
data train(drop=selected) test(drop=selected);
    set train;
    if selected=1 then output test; else output train;
run;

proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;

proc format;
    value rollf 0="train-data" 1="validation-data" 2="test-data";
run;
data alldata;
   set train test(in=in1);
   format selected rollf.; *-- フォーマット適用;
   if in1 then selected=2; *-- テストデータに番号振る;
run;


SASでお試し(モデル作成)


Gradient Boosting Tree(勾配ブースティング木)の実行。

proc treeboost data=train(where=(selected=0))
                iterations = 1000 /* pythonではn_estimators */
                maxdepth = 6      /* pythonではmax_depth */
                seed = 19
                ; 
 
    target Survived / level=binary;
    input Sex Embarked / level=nominal;
    input Pclass SibSp Parch / level=ordinal order=ascending;
    input Age Fare / level=interval;
    
    assess validata = train(where=(selected=1));
    subseries best;
    
    code file="gradboost.sas";
    save fit=_gb_fit importance=_gb_importance;
quit;
run;


変数重要度を図示する。

title "Feature Importance of validation dataset";
proc sgplot data=_gb_importance;
    hbar name /response=vimportance  groupdisplay=cluster categoryorder=respdesc;
run;

f:id:cochineal19:20210725010400p:plain:w400

作成モデルで予測する。

%macro _Pred(cd=);
    data _Pred_&cd.;
        set alldata;
        %inc &cd.;
        if P_Survived1 > 0.5 then U_Survived = 1; else U_Survived = 0;
    run;
%mend _Pred;
%_Pred(cd=gradboost);


SASでお試し(モデル評価)


評価指標を自作し、スコアを算出する。

%macro _Scores(inds=, tlabel=Survived, pred=U_Survived, group=selected);
    proc sql;
        create table &inds._Scores as
            select
                 %if &group.^="" %then %do; &group. , %end;
                 count(*) as N
                ,sum(case when &tlabel.=1 and &pred.=1 then 1 else 0 end) as TP
                ,sum(case when &tlabel.=1 and &pred.=0 then 1 else 0 end) as FN
                ,sum(case when &tlabel.=0 and &pred.=1 then 1 else 0 end) as FP
                ,sum(case when &tlabel.=0 and &pred.=0 then 1 else 0 end) as TN
                ,(calculated TP + calculated TN) / count(*)      as Accuracy    label="正診率(Accuracy)"
                ,calculated TP / (calculated TP + calculated FN) as Recall      label="検出率(Recall) 、類語:感度"
                ,calculated TN / (calculated FP + calculated TN) as Specificity label="特異度(Specificity)"
                ,calculated TP / (calculated TP + calculated FP) as Precision   label="適合率(Precision)"
                ,2 * calculated Recall * calculated Precision / (calculated Recall + calculated Precision) as f1_score label="F1スコア"
            from &inds.
             %if &group.^="" %then %do; group by &group. %end;
        ;
    quit;
    
    title "&inds.";
    proc print data=&inds._Scores; run;
    title "";
%mend _Scores;
%_Scores(inds=_Pred_gradboost);

f:id:cochineal19:20210725010328p:plain

今回の f1スコアは 0.754 くらい。
この前、ランダムフォレストが 0.709 くらいだったのでだいぶ良い予測性能。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

※毎度のことハイパーパラメータチューニングはしていないのであしからず。

参考


https://support.sas.com/documentation/solutions/emtmsas/93/emprcref.pdf

【SAS】hpsvmプロシジャ_サポートベクターマシーン

今回はサポートベクターマシーンを試してみる。


Pythonでの実装コード


Pythonで簡単に実装できる。

#-- 線形SVM
from sklearn.svm import LinearSVC
mod1 = LinearSVC(C=1, random_state=999)
mod1.fit(X_train, y_train)
y_pred1 = mod1.predict(X_test)
 
#-- 非線形SVM
from sklearn.svm import SVC
klist=['poly','rbf','sigmoid']
for k in klist:
    mod1 = SVC(C=1, kernel=k, random_state=999)
    mod1.fit(X_train, y_train)
    y_pred1 = mod1.predict(X_test)


以前、次の記事でも触れている。
【機械学習_Python】サポートベクターマシーン - こちにぃるの日記

SASでの実装コード


ものすごくシンプルな記載。
ここにカーネルやらペナルティやらクロスバリデーションやらを設定できる。

proc hpsvm data = 訓練データ;
    target 目的変数 / order = 順序 level = 変数のタイプ;
    input 特徴量1, ... , 特徴量p;
    code file="hpsvm_fit.sas"; /* 将来データ用に予測アルゴリズム出力 */
run;
 
data _Pred_&cd.;
    set alldata;
    %inc hpsvm_fit; *-- 新しいデータで予測;
run;


SASでお試し(対象データ、データ加工)


今回もKaggleチュートリアルタイタニックデータを用いる。

データ加工は以前と同じなのでコードをたたんでいる。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

★コードを見る場合ここをクリック★

/*---------------------------------------------------
    欠損値処理、変数作成
---------------------------------------------------*/
*-- 数値型:欠損値を中央値に置き換え;
proc stdize data=train out=train method=median reponly;
    var age;
run;

*-- カテゴリ型:欠損値を最頻値に置き換え;
data train;
    set train;
    Embarked = coalescec(Embarked,"S");
run;

/*---------------------------------------------------
    データ分割
---------------------------------------------------*/
proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;
data train(drop=selected) test(drop=selected);
    set train;
    if selected=1 then output test; else output train;
run;

proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;

proc format;
    value rollf 0="train-data" 1="validation-data" 2="test-data";
run;
data alldata;
   set train test(in=in1);
   format selected rollf.; *-- フォーマット適用;
   if in1 then selected=2; *-- テストデータに番号振る;
run;


SASでお試し(モデル作成)


線形SVMの実行。
kernel linear で線形SVM(デフォルトなので書かなくても良い)。
partition で訓練データとバリデーションデータを指定。
select cv=validateset でバリデーションデータを指定。クロスバリデーションで penalty のハイパーパラメータCを決定する。例では0.1~1の間で最適値を探してくれる。

proc hpsvm data=train;
    partition roleVar=selected(train='0' valid='1');
    performance details;
    
    target Survived / order=desc level=nominal;
    input Pclass Sex Age Fare Embarked SibSp Parch;
    
    kernel linear;
    penalty C=0.1 to 1;
    select cv=validateset seed=19;
    
    code file="hpsvm_linear_cv.sas";
run;


線形SVM(10-fold cross validation)の実行。
select cv=random fold=10 で10-fold クロスバリデーション。

proc hpsvm data=train;
    performance details;
    
    target Survived / order=desc level=nominal;
    input Pclass Sex Age Fare Embarked SibSp Parch;
    
    kernel linear;
    penalty C=0.1 to 1;
    select cv=random fold=10 seed=19;
    
    code file="hpsvm_linear_10fcv.sas";
run;


多項式カーネルでの非線形SVM(10-fold cross validation)の実行。
kernel polynom多項式カーネルを指定。
その他、rbf(Gaussカーネル)、sigmoid(シグモイドカーネル)がある。

proc hpsvm data=train;
    performance details;
    
    target Survived / order=desc level=nominal;
    input Pclass Sex Age Fare Embarked SibSp Parch;
    
    kernel polynom / degree=2;
    penalty C=0.1 to 1;
    select cv=random fold=10 seed=19;
    
    code file="hpsvm_polynom_10fcv.sas";
run;


作成モデルで予測する。

%macro _Pred(cd=);
    data _Pred_&cd.;
        set alldata;
        %inc hpsvm_&cd.;
        if P_Survived1 > 0.5 then U_Survived = 1; else U_Survived = 0;
    run;
%mend _Pred;
%_Pred(cd=linear_cv);
%_Pred(cd=linear_10fcv);
%_Pred(cd=polynom_10fcv);


SASでお試し(モデル評価)


評価指標を自作し、スコアを算出する。

%macro _Scores(inds=, tlabel=Survived, pred=U_Survived, group=selected);
    proc sql;
        create table &inds._Scores as
            select
                 %if &group.^="" %then %do; &group. , %end;
                 count(*) as N
                ,sum(case when &tlabel.=1 and &pred.=1 then 1 else 0 end) as TP
                ,sum(case when &tlabel.=1 and &pred.=0 then 1 else 0 end) as FN
                ,sum(case when &tlabel.=0 and &pred.=1 then 1 else 0 end) as FP
                ,sum(case when &tlabel.=0 and &pred.=0 then 1 else 0 end) as TN
                ,(calculated TP + calculated TN) / count(*)      as Accuracy    /*正診率(Accuracy)*/
                ,calculated TP / (calculated TP + calculated FN) as Recall      /*検出率(Recall) 、類語:感度*/
                ,calculated TN / (calculated FP + calculated TN) as Specificity /*特異度(Specificity)*/
                ,calculated TP / (calculated TP + calculated FP) as Precision   /*適合率(Precision)*/
                ,2 * calculated Recall * calculated Precision / (calculated Recall + calculated Precision) as f1_score /*F1スコア*/
            from &inds.
             %if &group.^="" %then %do; group by &group. %end;
        ;
    quit;
    
    title "&inds.";
    proc print data=&inds._Scores; run;
    title "";
%mend _Scores;
%_Scores(inds=_Pred_linear_cv);
%_Scores(inds=_Pred_linear_10fcv);
%_Scores(inds=_Pred_polynom_10fcv);


f:id:cochineal19:20210713230951p:plain

今回のいずれのモデルもハイパーパラメータ Cは 0.1 が最適値だったよう。
f1スコアは多項式カーネル0.70 くらい。

参考


SAS Help Center

【SAS】hpforestプロシジャ_ランダムフォレスト

今回はランダムフォレストを試してみる。


Pythonでの実装コード


Pythonでは非常に簡単に実装できる。

from sklearn.ensemble import RandomForestClassifier
mod1 = RandomForestClassifier(n_estimators = 100
                              ,criterion = "gini"
                              ,max_depth = i_integer
                              ,max_features = "auto"
                              ,bootstrap = True
                              ,random_state = 1)


クロスバリデーションも簡単(例は決定木とランダムフォレストをグリッドサーチで実装)。

import pandas as pd
from sklearn import model_selection
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
 
modfnc1 = [DecisionTreeClassifier(), RandomForestClassifier()]
prm1 = [{"max_depth": stats.randint(1,6)
         ,"criterion":["gini","entropy"]
         ,"random_state":[1]}
        ,{"n_estimators": stats.randint(10,201)
         ,"max_depth": stats.randint(1,6)
         ,"criterion":["gini","entropy"]
         ,"max_features":["auto", "sqrt", "log2"]
         ,"random_state":[1]}]
for modfnc1, prm1 in zip(modfnc1, prm1):
    clf = model_selection.RandomizedSearchCV(modfnc1, prm1)
    clf.fit(X_train, y_train)
    y_pred1 = clf.predict(X_test)
    print(clf.best_estimator_)
    print(confusion_matrix(y_test, y_pred1))
    print(classification_report(y_test, y_pred1))
    print()


SASでの実装コード


ホールドアウトでのモデル作成は次の通り。
任意の訓練データと検証データを用いたい場合、optionに scoreprole = valid を付け、partition statement で指定する。

proc hpforest data = 訓練データ seed = 19 scoreprole = valid
    maxtrees = 100 /* pythonではn_estimators */
    maxdepth = 6   /* pythonではmax_depth */
    ;
 
    partition roleVar=selected(train='0' valid='1');
    performance details;
 
    target 目的変数 / level = 変数のタイプ;
    input 特徴量1, ... , 特徴量p;
 
    ods output FitStatistics      = _Res_FitStatistics;      /* 予測値の出力 */
    ods output VariableImportance = _Res_VariableImportance; /* 変数重要度(特徴量重要度)の出力 */
    save file = "hpforest_fit.bin"; /* 将来データ用に予測アルゴリズム出力 */
run;


SASに任せる場合は trainfraction=訓練データの割合 とすればプロシジャ内でデータ分割してくれる。

proc hpforest data = 訓練データ seed = 19 trainfraction=0.8
    maxtrees = 100 /* pythonではn_estimators */
    maxdepth = 6   /* pythonではmax_depth */
    ;
    ...
run;


なお、hpforest プロシジャでは save file = "出力ファイル名.bin" を付けることでモデルのアルゴリズムを出力できる。
この出力ファイルを hp4score に渡せば、新しいデータで予測ができる。

proc hp4score data = alldata;
    score file="hpforest_fit.bin" out=_Pred_RandomForest;
    id PassengerId selected;
run;


SASでお試し(対象データ、データ加工)


今回もKaggleチュートリアルタイタニックデータを用いる。

データ加工は以前と同じなのでコードをたたんでいる。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

★コードを見る場合ここをクリック★

/*---------------------------------------------------
    欠損値処理、変数作成
---------------------------------------------------*/
*-- 数値型:欠損値を中央値に置き換え;
proc stdize data=train out=train method=median reponly;
    var age;
run;

*-- カテゴリ型:欠損値を最頻値に置き換え;
data train;
    set train;
    Embarked = coalescec(Embarked,"S");
run;

/*---------------------------------------------------
    データ分割
---------------------------------------------------*/
proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;
data train(drop=selected) test(drop=selected);
    set train;
    if selected=1 then output test; else output train;
run;

proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;

proc format;
    value rollf 0="train-data" 1="validation-data" 2="test-data";
run;
data alldata;
   set train test(in=in1);
   format selected rollf.; *-- フォーマット適用;
   if in1 then selected=2; *-- テストデータに番号振る;
run;


SASでお試し(モデル作成)


ランダムフォレストの実行。

proc hpforest data=train seed=19 scoreprole=valid
    maxtrees    = 100 /* pythonではn_estimators */
    maxdepth    = 6   /* pythonではmax_depth */
    vars_to_try = 4 
    ;
 
    partition roleVar=selected(train='0' valid='1');
    performance details;
 
    target Survived / level = binary;
    input Pclass Sex Age Fare Embarked SibSp Parch;
 
    ods output FitStatistics      = _Res_FitStatistics;
    ods output VariableImportance = _Res_VariableImportance;
    save file="hpforest_fit.bin"; 
run;

f:id:cochineal19:20210712224857p:plain:w400

変数重要度を図示する。

title "Feature Importance";
proc sgplot data=_Res_VariableImportance;
    hbar Variable /response=Gini  groupdisplay=cluster categoryorder=respdesc;
run;

f:id:cochineal19:20210712224944p:plain:w400

作成モデルで予測する。

proc hp4score data = alldata;
    score file="hpforest_fit.bin" out=_Pred_RandomForest;
    id PassengerId selected;
run;
 
data _Pred_RandomForest;
    set _Pred_RandomForest;
    if P_Survived1 > 0.5 then U_Survived = 1; else U_Survived = 0;
run;


SASでお試し(モデル評価)


評価指標を自作し、スコアを算出する。

%macro _Scores(inds=, tlabel=Survived, pred=U_Survived, group=selected);
    proc sql;
        create table &inds._Scores as
            select
                 %if &group.^="" %then %do; &group. , %end;
                 count(*) as N
                ,sum(case when &tlabel.=1 and &pred.=1 then 1 else 0 end) as TP
                ,sum(case when &tlabel.=1 and &pred.=0 then 1 else 0 end) as FN
                ,sum(case when &tlabel.=0 and &pred.=1 then 1 else 0 end) as FP
                ,sum(case when &tlabel.=0 and &pred.=0 then 1 else 0 end) as TN
                ,(calculated TP + calculated TN) / count(*)      as Accuracy    /*正診率(Accuracy)*/
                ,calculated TP / (calculated TP + calculated FN) as Recall      /*検出率(Recall) 、類語:感度*/
                ,calculated TN / (calculated FP + calculated TN) as Specificity /*特異度(Specificity)*/
                ,calculated TP / (calculated TP + calculated FP) as Precision   /*適合率(Precision)*/
                ,2 * calculated Recall * calculated Precision / (calculated Recall + calculated Precision) as f1_score /*F1スコア*/
            from &inds.
             %if &group.^="" %then %do; group by &group. %end;
        ;
    quit;
    
    title "&inds.";
    proc print data=&inds._Scores; run;
    title "";
%mend _Scores;
%_Scores(inds=_Pred_RandomForest);

f:id:cochineal19:20210712225319p:plain

今回の f1スコアは 0.709 くらい。
この前 hpgenselect プロシジャで作成したモデル(Lasso ロジスティック回帰)よりは良い予測性能。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

ハイパーパラメータはあまりいじってないので、ここをいじれば性能は良くなるかもしれない。
なお、forest プロシジャではハイパーパラメータを自動調整してくれる autoturn statement があるようだが、hpforest プロシジャに同様の機能があるかは調べられていない。
SAS Help Center

参考


SAS Help Center
SAS Help Center
https://www.lexjansen.com/mwsug/2016/AA/MWSUG-2016-AA20.pdf
https://support.sas.com/content/dam/SAS/support/en/books/free-books/machine-learning-with-sas-special-collection.pdf

本ブログは個人メモです。 本ブログの内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。