林嶔 (Lin, Chin)
Lesson 12 簡易網頁App撰寫
– 前端:使用HTML/CSS/Java Script等技術製作網頁介面(與一般網頁無異)
– 後端:利用預先設定的模組與使用者進行互動,並回傳結果給前端
– 優勢:跨平台、更新方便
– 劣勢:速度較慢、需要穩定的連線
– 應該還記得怎樣安裝套件吧!
library(shiny)
# Define UI for application that plots random distributions
shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with a slider input for number of observations
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
))
library(shiny)
# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
# Expression that generates a plot of the distribution. The expression is
# wrapped in a call to renderPlot to indicate that:
#
# 1) It is 'reactive' and therefore should be automatically re-executed
# when inputs change 2) Its output type is a plot
output$distPlot = renderPlot({
# generate an rnorm distribution and plot it
dist = rnorm(input$obs)
hist(dist)
})
})
– 如果你想複製貼上點不同的東西,可以在shiny-examples內找到更多例子。
前面已經說過,使用shiny(R package)所撰寫的App,他的基本構造是一個包含ui.R(主管使用者介面)以及server.R(主管伺服器端的處理)的資料夾。
Shiny app的基本運作流程為:
使用者自ui.R中的給定一個參數。
這個參數傳到server.R裡面,使用反應函數進行計算。
反應完成後,再回傳至ui.R輸出反應結果。
library(shiny)
# Define UI for application that plots random distributions
shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with a slider input for number of observations
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
))
headerPanel()用來定義網頁標題
sidebarPanel()用來定義的控制選單內含哪些可控參數,本例中只有一個滑動輸入元件sliderInput(),元件為obs
mainPanle()則是用來定義輸出區域的輸出結果,本例中只有一個圖片輸出元件plotOutput(),元件為distPlot
library(shiny)
# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
# Expression that generates a plot of the distribution. The expression is
# wrapped in a call to renderPlot to indicate that:
#
# 1) It is 'reactive' and therefore should be automatically re-executed
# when inputs change 2) Its output type is a plot
output$distPlot = renderPlot({
# generate an rnorm distribution and plot it
dist = rnorm(input$obs)
hist(dist)
})
})
其中是以shinyServer()這個函數為開頭,裡面包含著一個函數function()要求你指定input及output,由於我們只要做一個反應函數,由於我們想要畫一張圖,所以使用renderPlot()函數,在裡面我們指定它畫圖的過程,並且將結果儲存在output裡面的distPlot。
這個畫圖的過程很簡單,就是先指定一個數字(由input$obs提供,也就是剛剛在ui.R中使用者所給定的參數),然後要求R使用rnorm()隨機產生n個平均數為0,標準差為1的數列,而這個數列儲存在dist元件內。接著在以hist(dist)畫出這個數列的直方圖。
因此,在renderPlot()函數內,我們根據使用者指定的參數(input$obs)產生了一張直方圖,而這張直方圖將會儲存在output裡面的distPlot。
接著這個物件distPlot就會回到ui.R中,而根據我們在ui.R中所下的指令,他將會使用plotOutput()函數使這張圖形呈現在輸出區上。
Note:所有的輸入元件都存取在input這個List內;而所有的輸出元件都存取在output這個List內。
至此為止,我們已經學會了webApp的基本寫法。然而這時候若需要進一步的增加它的應用面,我們需要的是學習更多R裡面的函數,讓我們的App可以擁有更強大的功能。
請在R內練習下列這段程式碼的使用,並試圖改變裡面的obs,M,S,Coler的數字,了解他的功能
在剛剛的練習中我們已經清楚了在R裡面,改變參數可以改變圖形的常態分布,接著我們可以開始增加這些參數至我們剛剛寫的App內。
除了已經存在的obs之外,我們將繼續增加M,S,Coler在我們的控制區域之內。
ui.R
library(shiny)
# Define UI for application that plots random distributions
shinyUI(pageWithSidebar(
# Application title
headerPanel("Hello Shiny!"),
# Sidebar with inputs for number of observations, mean, SD, and coler.
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500),
numericInput("M", "Mean of this normal distribution:", min = -200, max = 200, value = 100),
numericInput("S", "SD of this normal distribution:", min = 0, max = 50, value = 10),
radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
))
library(shiny)
# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
# Expression that generates a plot of the distribution. The expression is
# wrapped in a call to renderPlot to indicate that:
#
# 1) It is 'reactive' and therefore should be automatically re-executed
# when inputs change 2) Its output type is a plot
output$distPlot = renderPlot({
# generate an rnorm distribution and plot it
dist = rnorm(input$obs,mean=input$M,sd=input$S)
hist(dist,col=input$Color)
})
})
我們先把R做為一個計算機來使用,並且使用函數功能做一些較複雜的運算。
下面這個範例是我們前面的課程所做到的費波納奇數列,
在R裡面我們學會函數運算之後,接著我們會希望其他人也可以輕易的使用我們剛剛設定好的函數,因此我們會希望將他寫成像這樣子的App,供大家使用。
ui.R
library(shiny)
# Define UI for application that calculate the needed sample size
shinyUI(pageWithSidebar(
# Application title
headerPanel("My App"),
# Sidebar with numeric inputs
sidebarPanel(
numericInput("a", "The first value of sequence:", min = -100, max = 100, value = 1),
numericInput("b", "The second value of sequence:", min = -100, max = 100, value = 1),
numericInput("last.seq", "The length of sequence:", min = 3, max = 30, value = 20)
),
# Show the outcome
mainPanel(
verbatimTextOutput("Seq")
)
))
library(shiny)
# Your function
Fibonacci = function (a, b, last.seq) {
if (last.seq < 3) {
cat("last.seq必須大於等於3。")
} else {
x = c(a, b)
for (i in 3:last.seq) {
x[i] = x[i-1] + x[i-2]
}
x
}
}
# Define server logic required to calculate the needed sample size
shinyServer(function(input, output) {
output$Seq = renderPrint({
Fibonacci(input$a, input$b, input$last.seq)
})
})
prime_number = function (max.x) {
x = 2:max.x
answer.x = rep(TRUE, max.x-1)
indexes = 1:(max.x/2-1) * 2
answer.x[indexes + 1] = FALSE
for (i in indexes) {
pos = which(answer.x[1:((i-1)/3)])
for (j in pos) {
if (x[i] %% x[j] == 0) {
answer.x[i] = FALSE
break
}
}
}
x[answer.x]
}
prime_number(max.x = 100)
prime_number(max.x = 200)
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("My App"),
sidebarPanel(
numericInput("max.x", "The search range of prime number:", min = 2, max = 500, value = 100)
),
mainPanel(
verbatimTextOutput("Seq")
)
))
library(shiny)
prime_number = function (max.x) {
x = 2:max.x
answer.x = rep(TRUE, max.x-1)
indexes = 1:(max.x/2-1) * 2
answer.x[indexes + 1] = FALSE
for (i in indexes) {
pos = which(answer.x[1:((i-1)/3)])
for (j in pos) {
if (x[i] %% x[j] == 0) {
answer.x[i] = FALSE
break
}
}
}
x[answer.x]
}
shinyServer(function(input, output) {
output$Seq = renderPrint({
prime_number(input$max.x)
})
})
– conditionalPanel()可以讓我們的控制bar僅在特定條件下出現
– 常態分布必須要有兩個參數(平均數&標準差)決定,而t分布則必須要由一個參數決定(自由度),因此我們就必須設計當選擇常態分不時,控制區要出現的是平均數&標準差;若選擇了t分布,控制區要出現的則是自由度
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Generating distribution"),
sidebarPanel(
selectInput("method", "Choose distribution:", choices=c("Normal"="norm", "Student t"="st")),
helpText("Setting parameter(s) for distribution model"),
conditionalPanel(condition="input.method=='norm'",
numericInput(inputId="mu", label="mean", value=0),
numericInput(inputId="sd", label="standard deviation", value=1, min=0)
),
conditionalPanel(condition="input.method=='st'",
numericInput(inputId="df", label="Df", value=10, min=1)
),
sliderInput(inputId="obs",
label="Number of observations:",
min = 1, max = 1000, value = 500)
),
mainPanel(
plotOutput("distPlot")
)
))
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("My App"),
sidebarPanel(
selectInput("method", "Choose a method:", choices = c("Fibonacci" = "1", "Prime Number" = "2")),
conditionalPanel(condition="input.method=='1'",
numericInput("a", "The first value of sequence:", min = -100, max = 100, value = 1),
numericInput("b", "The second value of sequence:", min = -100, max = 100, value = 1),
numericInput("last.seq", "The length of sequence:", min = 3, max = 30, value = 20)
),
conditionalPanel(condition="input.method=='2'",
numericInput("max.x", "The search range of prime number:", min = 2, max = 500, value = 100)
)
),
mainPanel(
verbatimTextOutput("Seq")
)
))
library(shiny)
Fibonacci = function (a, b, last.seq) {
if (last.seq < 3) {
cat("last.seq必須大於等於3。")
} else {
x = c(a, b)
for (i in 3:last.seq) {
x[i] = x[i-1] + x[i-2]
}
x
}
}
prime_number = function (max.x) {
x = 2:max.x
answer.x = rep(TRUE, max.x-1)
indexes = 1:(max.x/2-1) * 2
answer.x[indexes + 1] = FALSE
for (i in indexes) {
pos = which(answer.x[1:((i-1)/3)])
for (j in pos) {
if (x[i] %% x[j] == 0) {
answer.x[i] = FALSE
break
}
}
}
x[answer.x]
}
shinyServer(function(input, output) {
output$Seq = renderPrint({
if (input$method == "1") {
Fibonacci(input$a, input$b, input$last.seq)
} else {
prime_number(input$max.x)
}
})
})