t_kahi’s blog

KNIMEやCellProfiler、創薬に関する記事と,日々のメモです

【KNIME】線形判別分析 (Linear Discriminant Analysis) をKNIME Workflowで行う

こんにちは,@PKです.

今日は判別分析の一種である線形判別分析(Linear Discriminant Analysis)のKNIME Workflowについて紹介したいと思います.

線形判別分析(Linear Discriminant Analysis) とは

判別分析??という方もいらっしゃると思うので,少しだけ紹介します.

判別分析(はんべつぶんせき、英: discriminant analysis)は、事前に与えられているデータが異なるグループに分かれる場合、新しいデータが得られた際に、どちらのグループに入るのかを判別するための基準(判別関数)を得るための正規分布を前提とした分類の手法。
判別分析 - Wikipedia

判別分析は,グループの情報が与えられたときに,それらをうまく分離する分析手法のことで,今回の線形判別分析は,超平面・直線による判別を指すようです. (*線形判別分析は等分散性が必要)

線形判別分析(Linear Discriminant Analysis, LDA)~多クラスにも応用できる線形クラス分類~ データ解析・マイニングとR言語

今回はこの線形判別分析をKNIME Workflowで行う手法について,具体例を出しながら紹介していきたいと思います.

KNIME Workflowの概要

今回ご紹介するWorkflowは以下の通りです.
f:id:t_kahi:20190704215845p:plain

サンプルデータとしてirisを使って,種ごとの判別分析を行います.

#R Table
knime.out <- iris 
head(iris)
> knime.out <- iris 
+ head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

KINMEの線形判別分析を行うノード「Linear Discrimination Analysis」を使った判別分析についてと,RノードでRのlda関数を用いた線形判別分析の2種類をご紹介します.

「Linear Discrimination Analysis」による線形判別分析

KNIME Workflowには線形判別分析を行う「Linear Discrimination Analysis」というノードがあります. nodepit.com

この「Linear Discrimination Analysis」を使った判別分析のKNIME Workflowも公開されており,誰でも使うことができます.
How to use the Linear Discriminant Analysis – KNIME Hub

しかし,現状では「Linear Discrimination Analysis」に学習→予測の機能が付いていません
new LDA node - KNIME Extensions - KNIME Community Forum
上記フォーラムでも

The LDA node currently only does dimensionality reduction, no classification. Think of it as PCA that takes into account the class information.

となっているため,あくまでもクラス情報を加味したPCAとして使うことしかできないようです.
せっかくなので判別分析をしてみましょう.

f:id:t_kahi:20190704220947p:plain

予測ができないので分ける必要はないのですが,「Partitioning」でそれぞれの種ごとに分けて(左下図),その後「Linear Discriminant Analysis」の設定へと進みます(右下図) f:id:t_kahi:20190705110615p:plain

「Linear Discriminant Analysis」の設定では次元数や判別分析を実行するカラム,グループのカラムを指定して実行します.
以下に実行結果の表の一部を示しています.
f:id:t_kahi:20190705111009p:plain

右側のAxis0, Axis1が判別分析の判別係数を表しています.
この後は,得られたデータを可視化するための簡単な処理が続きます.
まずは可視化に不必要なからむを「Column Filter」で除いて,「Color Manager」で種ごとに色を付けます.
f:id:t_kahi:20190705111239p:plain
「COlumn Resorter」でカラムの順序を変えて(Scatter Plot表示の時に少しだけ設定を楽にするため),実行をすると右下図のような色付けされた表が出力されます.
f:id:t_kahi:20190705111307p:plain あとはこのデータを「Scatter Plot」で表示すれば完成です.
f:id:t_kahi:20190705111406p:plain
"versicolor","virginica"は距離が近いのでご判別される可能性があります.
とはいえKNIMEの「Linear Discrimination Analysis」では肝心の予測ができないので,Rノードを使ったWorkflowの説明をしたいと思います.

Rノードによる線形判別分析

KNIMEのノードでは学習→予想ができないことが分かったので,ここからが本番です.
以下にRノードWorkflowをお示しします.
f:id:t_kahi:20190706091046p:plain

今回使った主なRノードは「R Learner」と「R Predictor」です.
nodepit.com
nodepit.com

「R Learner」ではRスクリプト+モデルを作成することができます.
それを「R Predictor」に受け渡して,テストデータで実際の判別分析を行ってみます.

「R Learner」のRスクリプトは以下の通りです. MASSパッケージのlda関数を使って線形判別分析を行います.

#R Learner 
library(MASS) 

train <- knime.in

iris.lda<- lda(Species~ .,train)# 線形判別分析の実行
iris.lda#結果の確認
table(train[,5],predict(iris.lda)$class)#学習データの判別結果確認

knime.model <- iris.lda

実行した際に判別ができているかどうかを確認しておきます.
以下の通り,判別分析関数と,"setosa","versicolor","virginica"がそれぞれきれいに分類されていることが最後のテーブルで確認できます.

# R Learner console
Call:
lda(Species ~ ., data = train)

Prior probabilities of groups:
    setosa versicolor  virginica 
 0.3333333  0.3333333  0.3333333 

Group means:
           Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa            5.064       3.448        1.492       0.252
versicolor        6.024       2.780        4.252       1.320
virginica         6.596       3.008        5.560       2.080

Coefficients of linear discriminants:
                    LD1        LD2
Sepal.Length  0.8764488 -0.3374557
Sepal.Width   1.4133696  2.1524568
Petal.Length -1.8823954 -1.1211685
Petal.Width  -3.3802799  3.4671372

Proportion of trace:
   LD1    LD2 
0.9869 0.0131 
            
             setosa versicolor virginica
  setosa         25          0         0
  versicolor      0         25         0
  virginica       0          0        25

上記結果から,うまく3つの種を判別できているようです.
判別分析の判別係数の結果を先ほどの「Linear Discrimination Analysis」と同じように表示させたいときはモデルのデータを可視化するために「R View (workspace)」を使います.
LDA関数の結果をプロットするのは以下を参考にしました.
r - Linear discriminant analysis plot using ggplot2 - Stack Overflow 「R View」のスクリプトは以下の通りです.

#R VIew (workspace)  
library(ggplot2)
iris.lda <- knime.model
irisProjection <- cbind(scale(as.matrix(iris[,-5]),scale=FALSE) %*% iris.lda$scaling,iris[,5,drop=FALSE])
irisProjection$row_num = 1:nrow(irisProjection)
p <- ggplot(data=irisProjection, aes(x=LD1,y=LD2,col=Species)) + 
geom_point() + geom_text(aes(label = row_num))
p

f:id:t_kahi:20190706093824p:plain

ここまでで,学習データはうまくいっていることが確認できたので,「R Peredictor」で実際のテストデータを判別していきます.
先ほど「Partitioning」で分けた半分のテストデータを判別できるか検証していきます.
f:id:t_kahi:20190706225513p:plain

「R Predictor」のスクリプトは以下の通りです.

# R Predictor
iris.lda <- knime.model
test <- knime.in
Species <- knime.in$Species
iris.test<-predict(iris.lda,test)
table(test[,5],iris.test$class)#判別結果の確認
knime.out <- data.frame(iris.test$x,iris.test$class,Species)

モデルの読み込みは"knime.model"だけで良いので意外と簡単で便利です!
判別結果を確認します.

             setosa versicolor virginica
  setosa         25          0         0
  versicolor      0         23         2
  virginica       0          3        22

versicolorとvirginicaは先ほどの学習結果からも距離が近いので誤判別が起きています.
テストデータの判別関数値の散布図を示します.
この時,色で判別テスト結果を表示し,ラベルで元の種情報を表示します.
ラベル情報の付加は以下の記事を参考にしました.
ggplot2 パッケージによる可視化の際のラベルの重なりを防ぐ|Colorless Green Ideas

「R View」の結果は以下の通りです.

library(ggplot2)
library(ggrepel)
data <- knime.in
ggplot(data, aes(x = LD1, y = LD2, color = iris.test.class, label = knime.in$Species)) +
  geom_point()+
  geom_text_repel()

赤字で示した部分が誤判別をしている部分です.
f:id:t_kahi:20190706231651p:plain

まとめ

線形判別分析をKNIME Workflowで行いました.
残念ながらKNIMEのノードでは学習→予測を行うことができないので,Rノードを使うのが良いかもしれません.
この線形判別分析は、各グループの母分散が等しいという仮定(等分散性)に基づいているので,分散が異なる場合は違う判別分析を試したほうがいいでしょう.
Rでは様々な判別分析手法があるらしく,他手法も含めて今後比較していきたいです.
Rで判別分析いろいろ(11種類) - Qiita

最後にWorkflow全体の動画を示します.
youtu.be

おまけ

実は「R Learner」とか「R Predictor」など使わなくても「R Snippet」で完結できます.
「R Source」→「R Snippet 」→「R View」なので,もうKNIMEでやる意味が一切ないですね…
f:id:t_kahi:20190706232132p:plain

# R Snippet
library(MASS)

data.iris <- knime.in

nr <- nrow(data.iris)
row.train <- sample(nr, floor(0.5 * nr))
train <- data.iris[row.train,]
test <- data.iris[-row.train,]

iris.lda<- lda(Species~ .,train)# 線形判別分析の実行
iris.lda#結果の確認
table(train[,5],predict(iris.lda)$class)#学習データの判別結果確認

Species <- test$Species
iris.test<-predict(iris.lda,test)
table(test[,5],iris.test$class)

knime.out <- data.frame(iris.test$x,iris.test$class, Species)

結果は「R View」で確認します(結果は先ほどと全く同じなので割愛します)