Introduction about webApp writing by R (for CDC)

Chin Lin

Wednesday, November 18, 2015

基本介紹

  1. 只需要連上網路即可使用

  2. 透過互動清楚的展示你所想表達的事物

  3. 對於使用者來說非常簡單上手

R語言

安裝套件

F1

開始創造一個簡單的App - Step 1

F2

開始創造一個簡單的App - Step 2

F3

開始創造一個簡單的App - Step 3

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")
  )
))

F4

開始創造一個簡單的App - Step 4

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)
  })
  
})

F5

開始創造一個簡單的App - Step 5

F6

F7

練習-1

– 如果你想複製貼上點不同的東西,可以在shiny-examples內找到更多例子,

簡單介紹Shinyapps

  1. 使用者自ui.R中的給定一個參數。

  2. 這個參數傳到server.R裡面,使用反應函數進行計算。

  3. 反應完成後,再回傳至ui.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")
  )
))
  1. headerPanel()用來定義網頁標題

  2. sidebarPanel()用來定義的控制選單內含哪些可控參數,本例中只有一個滑動輸入元件sliderInput(),元件為obs

  3. mainPanle()則是用來定義輸出區域的輸出結果,本例中只有一個圖片輸出元件plotOutput(),元件為distPlot

F8

簡介server.R內的參數

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)
    })

})

Note:所有的輸入元件都存取在input這個List內;而所有的輸出元件都存取在output這個List內。

學習R裡面的基本函數

        obs = 500
        M = 170
        S = 10
        Coler = "blue"
        dist = rnorm(obs,mean=M,sd=S)
        hist(dist,col=Coler)

增加可控參數至剛剛的App中

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("Coler", "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$Coler)
    })

})

學習將自己的計算工具包裝成一個自訂函數

F9

Sig = 0.05  #two-sided significance level
p = 0.5     #The expected rate
X = 0.03    #Acceptable error range
Z = qnorm(1-Sig/2)   #The Z value based on the two-sided significance level 
n = p*(1-p)*Z^2/X^2  #Calculate the needed number of sample
n           #Print the outcome
Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

Samplesize(Sig = 0.05,p = 0.5, X = 0.03) #Test 1
Samplesize(Sig = 0.05,p = 0.2, X = 0.03) #Test 2
Samplesize(Sig = 0.05,p = 0.2, X = 0.05) #Test 3

創造一個新的App使我們能更方便的計算樣本數

library(shiny)

# Define UI for application that calculate the needed sample size 
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Sample size calculator"),

  # Sidebar with numeric inputs for significance level, expected rate, acceptable error range.
  sidebarPanel(
    numericInput("Sig", "Two-sided significance level:", min = 0, max = 1.000, value = 0.05),
    numericInput("p", "The expected rate:", min = 0, max = 1, value = 0.5),
    numericInput("X", "Acceptable error range:", min = 0, max = 1, value = 0.03)
  ),

  # Show the outcome
  mainPanel(
    h3(textOutput("Size"))
  )
))
library(shiny)

# Your function
Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

# Define server logic required to calculate the needed sample size
shinyServer(function(input, output) {

    output$Size = renderText({
      N = Samplesize(Sig = input$Sig,p = input$p, X = input$X)      
      paste("We need",round(N),"samples for this sampling.")
    })

})

練習-2

– 原始參數共有5個,分別是:

  1. alpha: 雙尾顯著水準(請在運算前把它轉換為適當的Z value)

  2. power: 單尾檢力(請在運算前把它轉換為適當的Z value)

  3. OR: 臨床上有意義之最小OR

  4. r: Case組人數是Control組人數的幾倍

  5. p0: 預期Control組之暴露率

F10

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

Samplesize_OR(alpha=0.05,power=0.8,OR=1.5,r=1,p0=0.2,corr=FALSE)
Samplesize_OR(alpha=0.05,power=0.8,OR=1.5,r=1,p0=0.2,corr=TRUE)

練習-2 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Sample size for Case Control study"),
  
  sidebarPanel(
    numericInput("alpha", "Two-sided significance level:", min = 0.001, max = 0.999, value = 0.05),
    numericInput("power", "Power:", min = 0.001, max = 0.999, value = 0.8),
    numericInput("OR", "Smallest difference of clinical/biological importance:", min = 0.01, max = 100, value = 1.5),
    numericInput("r", "The ratio of Case/Control:", min = 0.01, max = 100, value = 1),
    numericInput("p0", "Proportion of controls with exposures:", min = 0.001, max = 0.999, value = 0.2)    
  ),
  
  mainPanel(
    h3(textOutput("text1")),
    h4(textOutput("text2")),
    h3(textOutput("text3")),
    h4(textOutput("text4"))
  )
))
library(shiny)

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

shinyServer(function(input, output) {
  
  output$text1 = renderText({
    "Sample size without continuity correction:"
  })
  
  output$text2 = renderText({
    Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0)
  })
  
  output$text3 = renderText({
    "Sample size with continuity correction:"
  })
  
  output$text4 = renderText({
    Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0,corr=TRUE)
  })
  
})

將兩種Sample size的計算工具合成同個App

– 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)

shinyServer(function(input, output) {
    output$distPlot <- renderPlot({
        if (input$method == "norm") 
            {dist <- rnorm(input$obs, mean = input$mu, sd = input$sd)}
        if (input$method == "st") 
            {dist <- rt(input$obs, df = input$df)}
        hist(dist)
    })
})

練習-3

練習-3 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Sample size calculator"),
  
  sidebarPanel(
    selectInput("method", "Choose a method:", choices=c("Rate"="rate", "Case Control Study"="cc")),
    conditionalPanel(condition="input.method=='rate'",
    numericInput("Sig", "Two-sided significance level:", min = 0, max = 1.000, value = 0.05),
    numericInput("p", "The expected rate:", min = 0, max = 1, value = 0.5),
    numericInput("X", "Acceptable error range:", min = 0, max = 1, value = 0.03)
    ),
    conditionalPanel(condition="input.method=='cc'",
    numericInput("alpha", "Two-sided significance level:", min = 0.001, max = 0.999, value = 0.05),
    numericInput("power", "Power:", min = 0.001, max = 0.999, value = 0.8),
    numericInput("OR", "Smallest difference of clinical/biological importance:", min = 0.01, max = 100, value = 1.5),
    numericInput("r", "The ratio of Case/Control:", min = 0.01, max = 100, value = 1),
    numericInput("p0", "Proportion of controls with exposures:", min = 0.001, max = 0.999, value = 0.2) 
    )  
  ),
  
  mainPanel(
    h3(textOutput("text1")),
    h4(textOutput("text2")),
    h3(textOutput("text3")),
    h4(textOutput("text4"))
  )
))
library(shiny)

Samplesize = function(Sig,p,X) {
  Z = qnorm(1-Sig/2)
  n = p*(1-p)*Z^2/X^2
  n
}

Samplesize_OR = function(alpha,power,OR,r,p0,corr=FALSE) {
  Za=qnorm(1-alpha/2)
  Zb=qnorm(power)
  q0=1-p0
  p1=OR*p0/(OR*p0+q0)
  q1=1-p1
  pbar=(p0+r*p1)/(r+1)
  qbar=1-pbar
  n0=(Za*sqrt((r+1)*pbar*qbar)+Zb*sqrt(r*p0*q0+p1*q1))^2/(r*(p0-p1)^2)
  n1=r*n0
  n0corr=n0/4*(1+sqrt(1+(2*(1+r)/(n1*r*sqrt((p0-p1)^2)))))^2
  n1corr=r*n0corr
  if (corr==FALSE) {return(paste("Control:",round(n0)+1,"; Case:",round(n1)+1,"; Total:",round(n0)+round(n1)+2,sep=""))}
  if (corr==TRUE) {return(paste("Control:",round(n0corr)+1,"; Case:",round(n1corr)+1,"; Total:",round(n0corr)+round(n1corr)+2,sep=""))}
}

shinyServer(function(input, output) {
  
  output$text1 = renderText({
    if (input$method=="rate") {
      N = Samplesize(Sig = input$Sig,p = input$p, X = input$X)      
      return(paste("We need",round(N),"samples for this sampling."))
     }
    if (input$method=="cc") {"Sample size without continuity correction:"}
  })
  
  output$text2 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0)}
  })
  
  output$text3 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {"Sample size with continuity correction:"}
  })
  
  output$text4 = renderText({
    if (input$method=="rate") {return(NULL)}
    if (input$method=="cc") {Samplesize_OR(alpha=input$alpha,power=input$power,OR=input$OR,r=input$r,p0=input$p0,corr=TRUE)}
  })
  
})

建立產生SIR model的App

– 接著所有S狀態的人會根據參數β,慢慢經由S狀態改變為I狀態。因此參數β可以視為一種傳染係數

– 而所有I狀態的人則會根據參數λ,慢慢經由I狀態改變為R狀態。因此參數γ可以視為一種康復係數

F11

F11

在R內使用迴圈功能

– 下列是由1加到100的指令

x=0
for (i in 1:100) {
  x=x+i
}
x

– 下列是每隔3秒鐘讀取CDC的寫出一個檔案

for (i in 1:10) {
  data=readLines("http://nidss.cdc.gov.tw/download/Dengue_Daily_last12m.csv",encoding = "UTF-8", n = 50) #只讀取前50列
  write.table(data,paste0("e:/dengue",i,".csv"),col.names=FALSE,row.names=FALSE,quote=FALSE)
  Sys.sleep(3)
}

使用迴圈建立SIR model

  1. 設置m為預測時間範圍,可由使用者指定,在這設為50。

  2. 接著令n.S為初始易感受個案人口數(S狀態,假設是999人)。

  3. 接著令n.I為初始受感染個案人口數(I狀態,假設是1人)。

  4. 接著令n.R為初始受感染個案人口數(R狀態,假設是0人)。

  5. 令B為參數β(假設是1.5),以及令G為參數γ(假設是0.3)

  6. 先產生一個物件t(時間),他是一個從0到m的數列。除此之外,產生對應的SIR數列,長度與t相同,預設理面全部都為初始值。

  7. 計算總人口數N

  8. 建立迴圈計算各時間點SIR的個案數

  9. 畫圖。

#Parameters setting
m=50
n.S=999
n.I=1
n.R=0
B=0.7
G=0.3

#Generate vectors
t=0:m                 #t=seq(0,m,by=0.1)
S=rep(n.S,m+1)        #S=rep(n.S,length(t))
I=rep(n.I,m+1)        #I=rep(n.I,length(t))
R=rep(n.R,m+1)        #R=rep(n.R,length(t))
N=n.S+n.I+n.R

#for loop
for (i in 1:(length(t)-1)) {
  #S->I
  SI=B*S[i]*I[i]/N
  #if time space is not equal to 1, then use follow program:
  #SI=B*S[i]*I[i]/N*(t[i+1]-t[i])
  
  #I->R
  IR=G*I[i]
  #if time space is not equal to 1, then use follow program:
  #IR=G*I[i]*(t[i+1]-t[i])
  
  #To caculate the number of S, I, R in each time point
  S[i+1]=S[i]-SI
  I[i+1]=I[i]+SI-IR
  R[i+1]=R[i]+IR
}

#Type 1 plot

plot(t,S,type="l",col="blue",ylab="Population",xlab="Time",xlim=c(0,m),ylim=c(0,N),lwd=2)
lines(t,I,col="red",lwd=2)
lines(t,R,col="darkgreen",lwd=2)
legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
       ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)

#Type 2 plot

plot(-1,-1,xlim=c(0,m),ylim=c(0,N),type="l",col="blue",ylab="Population",xlab="Time")
polygon(c(t,t[length(t):1]),c(rep(0,length(t)),S[length(S):1]),col="blue",border=NA)
polygon(c(t,t[length(t):1]),c(S,S[length(S):1]+I[length(I):1]),col="red",border=NA)
polygon(c(t,t[length(t):1]),c(S+I,rep(N,length(t))),col="darkgreen",border=NA)
legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
       ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)

整合上述過程為一個自訂函數

SIR = function (m, n.S, n.I, n.R, B, G, type = "1") {
    #Generate vectors
    t=0:m                 #t=seq(0,m,by=0.1)
    S=rep(n.S,m+1)        #S=rep(n.S,length(t))
    I=rep(n.I,m+1)        #I=rep(n.I,length(t))
    R=rep(n.R,m+1)        #R=rep(n.R,length(t))
    N=n.S+n.I+n.R
    
    #for loop
    for (i in 1:(length(t)-1)) {
        #S->I
        SI=B*S[i]*I[i]/N
        #if time space is not equal to 1, then use follow program:
        #SI=B*S[i]*I[i]/N*(t[i+1]-t[i])
        
        #I->R
        IR=G*I[i]
        #if time space is not equal to 1, then use follow program:
        #IR=G*I[i]*(t[i+1]-t[i])
        
        #To caculate the number of S, I, R in each time point
        S[i+1]=S[i]-SI
        I[i+1]=I[i]+SI-IR
        R[i+1]=R[i]+IR
    }
    
    if (type=="1") {
        #Type 1 plot
        plot(t,S,type="l",col="blue",ylab="Population",xlab="Time",xlim=c(0,m),ylim=c(0,N),lwd=2)
        lines(t,I,col="red",lwd=2)
        lines(t,R,col="darkgreen",lwd=2)
        legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
               ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)
    } else {
        #Type 2 plot
        plot(-1,-1,xlim=c(0,m),ylim=c(0,N),type="l",col="blue",ylab="Population",xlab="Time")
        polygon(c(t,t[length(t):1]),c(rep(0,length(t)),S[length(S):1]),col="blue",border=NA)
        polygon(c(t,t[length(t):1]),c(S,S[length(S):1]+I[length(I):1]),col="red",border=NA)
        polygon(c(t,t[length(t):1]),c(S+I,rep(N,length(t))),col="darkgreen",border=NA)
        legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
               ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)
    }
}

#Testing function
SIR(m = 100, n.S = 999, n.I = 1, n.R = 0, B = 0.3, G = 0.1, type = 1)

SIR(m = 100, n.S = 999, n.I = 1, n.R = 0, B = 0.3, G = 0.1, type = 2)

練習-4

練習-4 解答

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("SIR model calculator"),
  
  sidebarPanel(
    sliderInput("m", "Infection time:", min = 10, max = 200, value = 30, step = 1),
    numericInput("n.S", "Number of initial susceptible cases:", min = 100, max = 1000000, value = 999, step = 1),
    numericInput("n.I", "Number of initial infected cases:", min = 1, max = 10000, value = 1, step = 1),
    numericInput("n.R", "Number of initial recovered cases:", min = 0, max = 1000000, value = 0, step = 1),
    numericInput("B", "The infected parameter (Beta):", min = 0.01, max = 10, value = 0.6, step = 0.01),
    numericInput("G", "The recovered parameter(Gamma):", min = 0.01, max = 1, value = 0.3, step = 0.01),
    radioButtons("type", "The type of plot:",  c("Type 1" = "1", "Type 2" = "2"))
  ),
  
  mainPanel(
    plotOutput("PLOT",width = "800px",height = "600px")
  )
))
library(shiny)

SIR = function (m, n.S, n.I, n.R, B, G, type = "1") {
    #Generate vectors
    t=0:m                 #t=seq(0,m,by=0.1)
    S=rep(n.S,m+1)        #S=rep(n.S,length(t))
    I=rep(n.I,m+1)        #I=rep(n.I,length(t))
    R=rep(n.R,m+1)        #R=rep(n.R,length(t))
    N=n.S+n.I+n.R
    
    #for loop
    for (i in 1:(length(t)-1)) {
        #S->I
        SI=B*S[i]*I[i]/N
        #if time space is not equal to 1, then use follow program:
        #SI=B*S[i]*I[i]/N*(t[i+1]-t[i])
        
        #I->R
        IR=G*I[i]
        #if time space is not equal to 1, then use follow program:
        #IR=G*I[i]*(t[i+1]-t[i])
        
        #To caculate the number of S, I, R in each time point
        S[i+1]=S[i]-SI
        I[i+1]=I[i]+SI-IR
        R[i+1]=R[i]+IR
    }
    
    if (type=="1") {
        #Type 1 plot
        plot(t,S,type="l",col="blue",ylab="Population",xlab="Time",xlim=c(0,m),ylim=c(0,N),lwd=2)
        lines(t,I,col="red",lwd=2)
        lines(t,R,col="darkgreen",lwd=2)
        legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
               ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)
    } else {
        #Type 2 plot
        plot(-1,-1,xlim=c(0,m),ylim=c(0,N),type="l",col="blue",ylab="Population",xlab="Time")
        polygon(c(t,t[length(t):1]),c(rep(0,length(t)),S[length(S):1]),col="blue",border=NA)
        polygon(c(t,t[length(t):1]),c(S,S[length(S):1]+I[length(I):1]),col="red",border=NA)
        polygon(c(t,t[length(t):1]),c(S+I,rep(N,length(t))),col="darkgreen",border=NA)
        legend("topright",c("S","I","R"),pch=15,col=c("blue","red","darkgreen")
               ,bg="gray90",text.col=c("blue","red","green"),cex=1.5)
    }
}

shinyServer(function(input, output) {
  
  output$PLOT = renderPlot({
    SIR(m = input$m, n.S = input$n.S, n.I = input$n.I, n.R = input$n.R, B = input$B, G = input$G, type = input$type)
  })
  
})

小結

  1. 安裝套件
  2. 撰寫App(ui.R以及server.R)
  3. 在R內自創函數
  4. 利用conditionalPanel()增加使用者介面的自由度
  5. 使用迴圈