Introduction about webApp writing by R

Chin Lin

Wednesday, September 24, 2014

基本介紹

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

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

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

R語言

安裝套件

F0

在開始之前,先讓我們看看R語言能寫出什麼樣的App

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

F1

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

F2

開始創造一個簡單的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")
  )
))

F3

開始創造一個簡單的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)
    })

})

F4

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

F5

F6

練習-1

簡單介紹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

F7

簡介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)

plot of chunk unnamed-chunk-5

增加可控參數至剛剛的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)
    })

})

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

F8

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)

創造一個新的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組之暴露率

F9

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

分享你寫的App

  1. 壓縮Myapp資料夾並上傳到GitHub

  2. 自己架設一台伺服器,將檔案放在上面,並請使用者聯結到你的伺服器上使用

  3. 上傳至shinyapps.io

在R之中的準備動作

– 請試著用一開始安裝shiny的方法來安裝devtool

– 複製下面的代碼可以直接下載&安裝完shinyapps (shinyapps套件無法以正常程序安裝)

devtools::install_github('rstudio/shinyapps')
library(shinyapps)

F12

建立R與你的帳戶的聯結

F10

F11

F13

分享

– 點選Publish後,會出現個小視窗,指定檔名後(這也是你未來的網址名稱)就可以上傳至shinyapps.io

F13

小結

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