使用fileInput清理数据框并绘制它 - R Shiny

时间:2016-08-03 21:56:00

标签: r reactive-programming shiny shinydashboard

目标:创建一个闪亮的应用程序,可以使用fileInput()函数从用户获取数据,然后在dplyr和许多其他工具的帮助下清理正在引入的数据数据处理技术然后绘制此数据并显示已清理数据的表格,并且还有一个下载清理数据的下载按钮。

我做了什么:我已经成功创建了没有fileInput的应用程序。我只是使用R脚本清理数据,然后在应用程序的用户界面部分之前添加脚本并运行应用程序,它运行正常。但是,这没有用户输入功能。

下面的代码是我的ui和服务器端的简化版本,它试图添加用户输入功能。

Dept <- c(1,2,3)
DepartmentName <-c("abc","def","ghi")
Dept_Names <- cbind.data.frame(Dept,DepartmentName)

ui <- dashboardPage(   
  dashboardHeader(title="ABC"),
  dashboardSidebar(width = 150,
                   sidebarMenu(  
          menuItem("DataInput",tabName = "DataInput"),      
          menuItem("Dashboard", tabName = "Dashboard")
                   )),
  dashboardBody(
    tabItems(
      tabItem(tabName = "DataInput", 
              box(fileInput('file1', 'Choose CSV File',
                            accept=c('.csv')),
                  tags$hr(),
                  checkboxInput('header', 'Header', TRUE),
                  radioButtons('sep', 'Separator',
                               c(Comma=',',
                                 Semicolon=';',
                                 Tab='\t'),
                               ','),
                  actionButton("Load", "Load the File"))
      ),
      tabItem( tabName = "Dashboard",
               fluidRow(column(9,box(status = "primary",plotOutput("plot1"))
               ),
               column(3,
                      box(title="Controls",
                          selectInput(inputId = "Dept", 
                                      label = "Select", 
                                      choices = C(1,2,3)), 
                          numericInput(inputId = "ClassNum", 
                                       label = "Enter the Class Number", value = 1, min=0, max=100000),
                          status = "warning", solidHeader = TRUE, width="100%"),

                      downloadButton('downloadData', 'Download')

                )
              ), 
               box(DT::dataTableOutput("table"), width = "100%,",status = "primary")
      )
    )
  )
)

server <- function(input,output) { 

  data <- reactive({
    if(input$Load == 0){return()}
    inFile <- input$file1
    if (is.null(inFile)){return(NULL)}

    isolate({ 
      input$Load
      a <- read.csv(inFile$datapath, header = input$header,sep = input$sep,stringsAsFactors =FALSE)

      options(stringsAsFactors = FALSE) 
      a <- a[,1:5] 
      names(a) <- c("Dept","Description","Price","ClassNum","Quantity") 

      a <- a %>%
        filter(Quantity > 0) %>%
        filter(!(Price==""))  

      for (i in 1:length(a)) {  
        for (j in 1:nrow(a)) {
          if (i==2) {next}
          a[j,i] <- gsub(",", "", a[j,i])
        }  
      }  
      rm(i,j)  

      a <- merge(a,Dept_Names, by="Dept")

      for (j in 1:nrow(a)) { 
        if (a$ClassNum[j]=="") {a[j,4] <-0} else {a[j,4] <- a$ClassNum[j]}
      } 
      rm(j)

      a$Dept <- as.numeric(as.character(a$Dept))
      a$ClassNum <- as.numeric(as.character(a$ClassNum))
      a$Price <- as.numeric(as.character(a$Price))
      a$Quantity <- as.numeric(as.character(a$Quantity))

      a <- a %>%
        mutate(Revenue=Price*Quantity,Key1=paste(Dept, "_", ClassNum, sep="")) 

      total_complete <- a %>%
        group_by(Dept, ClassNum) %>%
        summarise(Revenue=sum(Revenue)) %>%
        arrange(Dept, desc(Revenue)) %>%
        mutate(Key1=paste(Dept, "_", ClassNum, sep="")) 

      ofn <- paste0("ABC",Sys.time(),".csv")
      ofn <- gsub(":","_",ofn)

    })

  })


  output$downloadData <- downloadHandler(
    filename = data()$ofn,
    content = function(file) {
      write.csv(data()$total_complete, file, row.names = FALSE)
    }
  )


  output$table <- DT::renderDataTable(DT::datatable({data1 <- data()$total_complete[data()$total_complete[,1]==input$Dept ,]
  }))

  output$plot1 <- renderPlot({

    ggplot(total_complete[total_complete$Department==input$Dept,], 
           aes(reorder(x=Key1,desc(Revenue)), y=Revenue, fill=Key1)) +
      geom_bar(stat="identity") +
      theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5)) 
  })



} 



shinyApp(ui, server)

输入数据如下所示

Dept #  Description Retail  Class # Q
10  MOP 11.99   100 1
10  Broom   7.99    101 2
10  soap    5.99    102 3
10  key 7.99    103 4
10  salmon  34.99   104 5
10  steak   11.99   105 5
10  wine    9.99    106 7

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

我使用了您上面提供的数据,并设法调试您的代码。

  • 首先,我将变量dept的最后一个值更改为10,同样地将selectInput中的选项更改为aDept_Names的合并导致数据框空白,应用程序崩溃了。为了防止崩溃,我添加了一个函数validate,它要求合并后的数据集a非空。

  • 我在C

  • 中将c更改为selectInput
  • data没有返回值,我将其设置为total_complete是已清理数据框的列表,ofn是下载数据框的名称:

    return(list(total_complete = total_complete, ofn = ofn))
    
  • 在我添加render*的两个req(data())函数中,如果data()不可用(NULL

  • data()$个函数中的render*添加到total_complete并将Departments更改为Dept

要获取非空图和非空表,在上传您问题中包含的数据后,请将selectInput的值设置为10

我在代码中发表了评论,表明了上述所有变化。

完整代码:

library(shiny)
library(shinydashboard)
library(ggplot2)
library(dplyr)

rm(ui)
rm(server)

Dept <- c(1,2,10) # changed 3 to 10 to avoid an empty data set a after merging.
DepartmentName <-c("abc","def","ghi")
Dept_Names <- cbind.data.frame(Dept,DepartmentName)

ui <- dashboardPage(   
  dashboardHeader(title="ABC"),
  dashboardSidebar(width = 150,
                   sidebarMenu(  
                     menuItem("DataInput",tabName = "DataInput"),      
                     menuItem("Dashboard", tabName = "Dashboard")
                   )),
  dashboardBody(
    tabItems(
      tabItem(tabName = "DataInput", 
              box(fileInput('file1', 'Choose CSV File',
                            accept=c('.csv')),
                  tags$hr(),
                  checkboxInput('header', 'Header', TRUE),
                  radioButtons('sep', 'Separator',
                               c(Comma=',',
                                 Semicolon=';',
                                 Tab='\t',
                                 Whitespace = " "),
                               ','),
                  actionButton("Load", "Load the File"))
      ),
      tabItem( tabName = "Dashboard",
               fluidRow(column(9,box(status = "primary", plotOutput("plot1"))
               ),
               column(3,
                      box(title="Controls",
                          selectInput(inputId = "Dept", 
                                      label = "Select", 
                                      choices = c(1,2,10)), # changed C to c and 3 to 10 
                          numericInput(inputId = "ClassNum", 
                                       label = "Enter the Class Number", value = 1, min=0, max=100000),
                          status = "warning", solidHeader = TRUE, width="100%"),

                      downloadButton('downloadData', 'Download')

               )
               ), 
               box(DT::dataTableOutput("table"), width = "100%,",status = "primary")
      )
    )
  )
)

server <- function(input,output) { 

  data <- reactive({
    if(input$Load == 0) {
      return(NULL)
    }
    inFile <- input$file1
    if (is.null(inFile)) {
      return(NULL)
    }
      a <- read.csv(inFile$datapath, header = input$header,sep = input$sep,stringsAsFactors =FALSE)

      options(stringsAsFactors = FALSE) 
      a <- a[,1:5] 
      names(a) <- c("Dept","Description","Price","ClassNum","Quantity") 

      a <- a %>%
        filter(Quantity > 0) %>%
        filter(!(Price==""))  

      for (i in 1:length(a)) {  
        for (j in 1:nrow(a)) {
          if (i==2) {next}
          a[j,i] <- gsub(",", "", a[j,i])
        }  
      }  
      rm(i,j)  

      # You have to be careful here because it can be an empty data frame!
      a <- merge(a, Dept_Names, by="Dept")

      validate(
        need(nrow(a) != 0, 'Merge was not successful')
      )


      for (j in 1:nrow(a)) { 
        if (a$ClassNum[j]=="") {
          a[j,4] <-0
        } else {
          a[j,4] <- a$ClassNum[j]
        }
      } 
      rm(j)

      a$Dept <- as.numeric(as.character(a$Dept))
      a$ClassNum <- as.numeric(as.character(a$ClassNum))
      a$Price <- as.numeric(as.character(a$Price))
      a$Quantity <- as.numeric(as.character(a$Quantity))

      a <- a %>%
        mutate(Revenue=Price*Quantity,Key1=paste(Dept, "_", ClassNum, sep="")) 

      total_complete <- a %>%
        group_by(Dept, ClassNum) %>%
        summarise(Revenue=sum(Revenue)) %>%
        arrange(Dept, desc(Revenue)) %>%
        mutate(Key1=paste(Dept, "_", ClassNum, sep="")) 

      ofn <- paste0("ABC",Sys.time(),".csv")
      ofn <- gsub(":","_",ofn)


      # It looks like that you want to return a list
      # total_complete is a processed data frame and ofn is a name of the file
      # that is going to be downloaded
      return(list(total_complete = total_complete, ofn = ofn))

    })


  output$downloadData <- downloadHandler( 
    filename = function() { # added a function as in an example from ?downloadHandler
      data()$ofn
    },
    content = function(file) {
      write.csv(data()$total_complete, file, row.names = FALSE)
    }
  )


  output$table <- DT::renderDataTable({ 
    req(data()) # require that data() is available
    DT::datatable(data()$total_complete[data()$total_complete[,1]==input$Dept ,])
  })

  output$plot1 <- renderPlot({
    req(data()) # require that data() is available

    # there is no such variable as "Department" hence changed to $Dept
    # added data()$
    ggplot(data()$total_complete[data()$total_complete$Dept==input$Dept,], 
           aes(reorder(x=Key1,desc(Revenue)), y=Revenue, fill=Key1)) +
      geom_bar(stat="identity") +
      theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5)) 
  })
} 

shinyApp(ui, server)