Creating Interactive Classification Model Web Tool via R Shiny — Iris Dataset

Purpose: This session is to show you how to build a web-based tool to let user select classification approaches for the Iris dataset. We are going to include SVM, K-mean and Hierarchical Clustering in this version and will add more in the future.

Dataset Introduction: Link

User Interface:

  1. The default setting for dropdown is “SVM” and it is showing how the model classify the item based on petal length and petal width. The below table is to show the accuracy of the classification. If the real column does not equal predict column, it means the model misclassify those individual items.

2. You can select the “hclust” for hierarchy clustering or “Kmean” for K-mean clustering method.

Tool Structure:
R Shiny includes two main parts of codes, UI.R and Server.R.

  1. UI: The code of UI control the design and layout of tool with the input and output hit on the Server.R.
navbarPage("IRIS",
tabPanel("classification",
sidebarPanel(
selectInput("method", "Method:",
choices=c("svm","hclust","Kmean"))
),
mainPanel(
plotOutput("plot1"),
hr(),
helpText("Accuracy:"),
tableOutput("view1")
)
)
)

2. Server: The code of server is actually running the machine learning behind the scene and return the results (chart, table, etc) back to the interface.

2.1 seleceData1: Restructure the data by adding columns of setosa/virginica/ versicolor so that it can be used for all models (We will include more models like neuralnet in the future).

selectedData1 <- reactive({
iris.dataset <- iris
iris.dataset$setosa <- iris.dataset$Species=="setosa"
iris.dataset$virginica = iris.dataset$Species == "virginica"
iris.dataset$versicolor = iris.dataset$Species == "versicolor"
iris.dataset
})

2.2 selectdata2: Build the model based on the input passed from the interface. This will return the result of file, which will be used in plot and accuracy confusion matrix.

selectedData2 <- reactive({
if ( input$method == "svm") {
fit <- svm(formula = Species ~ Sepal.Length+Sepal.Width+Petal.Length+Petal.Width, data = selectedData1())
}
else if ( input$method == "hclust") {
fit <- hclust(dist(selectedData1(), method="euclidean") , method="ward.D2")

}
else if ( input$method == "Kmean") {
fit <- kmeans(selectedData1()[, -5] , centers=3)
}
fit
})

2.3 output$plot1: This is the output plot presented in user interface (See mainPanel in UI part). It uses the result of model (fit in selectdata2) to create the plot.

output$plot1 <- renderPlot({
fit <- selectedData2()
iris <- selectedData1()
if ( input$method == "svm") {
plot(fit, iris, Petal.Width ~ Petal.Length, slice = list(Sepal.Width = 3, Sepal.Length = 4))
}
else if ( input$method == "hclust") {
par(mfrow=c(1,2))

plot( fit, xlab="euclidean")
} else if ( input$method == "Kmean") {
fviz_cluster(fit,
data = iris[, -5] ,
geom = c("point"),
frame.type = "norm")
}
})

2.4 output$view1: This is the output table presented in user interface (See mainPanel in UI part). It uses the result of model (fit in selectdata2) to create the accuracy confusion matrix.

output$view1 <- renderTable({
fit <- selectedData2()
iris <- selectedData1()
if ( input$method == "svm") {
comp = predict(fit, iris)
confus.matrix <- table(real=iris$Species, predict=comp )
} else if ( input$method == "hclust") {
fit.update <- fit
fit.update <- cutree(fit.update, k=3)
fit.raw <- fit.update
confus.matrix.raw <- table(fit.raw, iris$Species)
for(i in 1:nrow(iris)){
if(confus.matrix.raw[1,fit.raw[i]]/50 >0.5) {fit.update[i] <- "setosa"}
if(confus.matrix.raw[2,fit.raw[i]]/50 >0.5) {fit.update[i] <- "versicolor"}
if(confus.matrix.raw[3,fit.raw[i]]/50 >0.5) {fit.update[i] <- "virginica"}
}
confus.matrix <- table(fit.update, iris$Species)
} else if ( input$method == "Kmean") {
fit.raw <- fit
confus.matrix.raw <- table(fit.raw$cluster, iris$Species)
for(i in 1:nrow(iris)){
if(confus.matrix.raw[1,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "setosa"}
if(confus.matrix.raw[2,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "versicolor"}
if(confus.matrix.raw[3,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "virginica"}
}
confus.matrix <- table(fit$cluster, iris$Species)
}
confus.matrix
})

Full code:

library(shiny)
library(neuralnet)
library(e1071)
library(cluster)
library(factoextra)
shinyServer(function(input, output) {

selectedData1 <- reactive({
iris.dataset <- iris
iris.dataset$setosa <- iris.dataset$Species=="setosa"
iris.dataset$virginica = iris.dataset$Species == "virginica"
iris.dataset$versicolor = iris.dataset$Species == "versicolor"
iris.dataset
})

selectedData2 <- reactive({
if ( input$method == "svm") {
fit <- svm(formula = Species ~ Sepal.Length+Sepal.Width+Petal.Length+Petal.Width, data = selectedData1())
}
else if ( input$method == "hclust") {
fit <- hclust(dist(selectedData1(), method="euclidean") , method="ward.D2")

}
else if ( input$method == "Kmean") {
fit <- kmeans(selectedData1()[, -5] , centers=3)
}
fit
})

output$plot1 <- renderPlot({
fit <- selectedData2()
iris <- selectedData1()
if ( input$method == "svm") {
plot(fit, iris, Petal.Width ~ Petal.Length, slice = list(Sepal.Width = 3, Sepal.Length = 4))
}
else if ( input$method == "hclust") {
par(mfrow=c(1,2))

plot( fit, xlab="euclidean")
} else if ( input$method == "Kmean") {
fviz_cluster(fit,
data = iris[, -5] ,
geom = c("point"),
frame.type = "norm")
}
})
output$view1 <- renderTable({
fit <- selectedData2()
iris <- selectedData1()
if ( input$method == "svm") {
comp = predict(fit, iris)
confus.matrix <- table(real=iris$Species, predict=comp )
} else if ( input$method == "hclust") {
fit.update <- fit
fit.update <- cutree(fit.update, k=3)
fit.raw <- fit.update
confus.matrix.raw <- table(fit.raw, iris$Species)
for(i in 1:nrow(iris)){
if(confus.matrix.raw[1,fit.raw[i]]/50 >0.5) {fit.update[i] <- "setosa"}
if(confus.matrix.raw[2,fit.raw[i]]/50 >0.5) {fit.update[i] <- "versicolor"}
if(confus.matrix.raw[3,fit.raw[i]]/50 >0.5) {fit.update[i] <- "virginica"}
}
confus.matrix <- table(fit.update, iris$Species)
} else if ( input$method == "Kmean") {
fit.raw <- fit
confus.matrix.raw <- table(fit.raw$cluster, iris$Species)
for(i in 1:nrow(iris)){
if(confus.matrix.raw[1,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "setosa"}
if(confus.matrix.raw[2,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "versicolor"}
if(confus.matrix.raw[3,fit.raw$cluster[i]]/50 >0.5) {fit$cluster[i] <- "virginica"}
}
confus.matrix <- table(fit$cluster, iris$Species)
}
confus.matrix
})
})

Here is the LINK of the tool! Enjoy it :)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store