2017-12-01 10 views
0

내 코드에 문제가 있는지 알아 내려고하고 있습니다. 여기에 무슨 일이 일어나고 있습니까?
처음으로 실행하고 데이터 테이블 행을 클릭하면 모든 문자 정보를 볼 수 있습니다. 그런데 줄거리에 대한 다른 여러 관측을 선택하고 같은 줄에서 다시 클릭하면 이전에 그 위치에 있었던 정보 (예 : 1 행 -> Luke Skywalker)에 대한 정보가 제공됩니다.htmlOutput 또는 데이터 테이블. 어떤 것이 제대로 새로 고침되지 않습니까?

library(shiny) 
library(dplyr) 
library(DT) 
library(plotly) 


# 1) Prepare layout 


hair = starwars %>% 
    select(hair_color) %>% 
    arrange(hair_color) %>% 
    distinct() 


spec = starwars %>% 
    select(species) %>% 
    arrange(species) %>% 
    distinct() 


ui <- fluidPage(
    sidebarLayout(
    sidebarPanel(
     selectInput('hair', 'Hair', hair, multiple = TRUE), 
     selectInput('spec', 'Species', spec, multiple = TRUE), 
     htmlOutput('txt') 
    ), 
    mainPanel(
     plotlyOutput('plot'), 
     dataTableOutput('table') 
    ) 
) 
) 

# 2) Prepare data 

srv <- function(input, output){ 

    starwars_data <- reactive({ 
    starwars_data_as_table <- as.data.frame(starwars) 
    starwars_data_as_table = starwars_data_as_table %>% 
     tibble::rownames_to_column(var = 'ID') 

    starwars_data_as_table$gender[is.na(starwars_data_as_table$gender)] <- 'not applicable' 
    starwars_data_as_table$homeworld[is.na(starwars_data_as_table$homeworld)] <- 'unknown' 
    starwars_data_as_table$species[is.na(starwars_data_as_table$species)] <- 'unknown' 
    starwars_data_as_table$hair_color[is.na(starwars_data_as_table$hair_color)] <- 'not applicable' 

    # a) add missing info 

    starwars_data = starwars_data_as_table %>% 
     mutate(
     height = case_when(
      name == 'Finn' ~ as.integer(178), 
      name == 'Rey' ~ as.integer(170), 
      name == 'Poe Dameron' ~ as.integer(172), 
      name == 'BB8' ~ as.integer(67), 
      name == 'Captain Phasma' ~ as.integer(200), 
      TRUE ~ height 
     ), 
     mass = case_when(
      name == 'Finn' ~ 73, 
      name == 'Rey' ~ 54, 
      name == 'Poe Dameron' ~ 80, 
      name == 'BB8' ~ 18, 
      name == 'Captain Phasma' ~ 76, 
      TRUE ~ mass 
     ), 
     film_counter = lengths(films), 
     vehicle_counter = lengths(vehicles), 
     starship_counter = lengths(starships) 
    ) 

    colnames(starwars_data) <- c("ID", "Name","Height", "Weight", 
           "Hair","Skin","Eyes", 
           "Birth", "Gender", 
           "Homeworld","Species", "Movies", 
           "Vehicles", "Starship", "Number of movies", 
           "Number of vehicles", "Number of starships") 
    starwars_data 

    }) 

    # filter data using input box 
    starwars_data_filtered <- reactive({ 

    dta <- starwars_data() 
    if(length(input$hair) > 0){ 
     dta <- dta %>% 
     filter(Hair %in% input$hair) 
    } 
    if (length(input$spec) > 0) { 
     dta <- dta %>% 
     filter(Species %in% input$spec) 
    } 
    if (length(input$spec) > 0 & length(input$hair) > 0) { 
     dta <- dta %>% 
     filter(Hair %in% input$hair) %>% 
     filter(Species %in% input$spec) 
    } 
    dta 
    }) 



    output$plot <- renderPlotly({ 
    plot_ly(starwars_data_filtered(), 
      source = 'scatter') %>% 
     add_markers(
     x = ~Height, 
     y = ~Homeworld, 
     color = ~factor(Gender), 
     key = ~ID 
    ) %>% 
     layout(
     xaxis = list(title = 'Height', rangemode = "tozero"), 
     yaxis = list(title = 'Homeland', rangemode = "tozero"), 
     dragmode = "select" 
    ) 
    }) 


    selected_data = reactive({ 
    sel_data = starwars_data_filtered() %>% 
     select(ID, 
      Name, 
      Height, 
      Weight, 
      Hair, 
      'Birth', 
      'Number of movies', 
      'Number of vehicles', 
      'Number of starships') 
    ed = event_data("plotly_selected", source = "scatter") 
    if(!is.null(ed)){ 
     sel_data = sel_data %>% 
     filter(ID %in% ed$key)  
    } 
    sel_data 
    }) 

    output$table = renderDataTable({ 
    d = selected_data() 
    if(!is.null(d)){ 
     datatable(d, selection = 'single', rownames = FALSE) 
    } 
    }) 

    output$txt = renderText({ 
    row_count <- input$table_rows_selected 
    if(!is.null(row_count)){ 

     # a function to create a list from the vector 
     vectorBulletList <- function(vector) { 
     if(length(vector > 1)) { 
      paste0("<ul><li>", 
       paste0(
        paste0(vector, collpase = ""), collapse = "</li><li>"), 
       "</li></ul>") 
     } 
     } 

     # in starwars dataframe, vehicles and starships are lists 
     # need to select the first element of the list (the character vector) 
     vehicles <- starwars_data()[row_count, "Vehicles"][[1]] 
     starships <- starwars_data()[row_count, "Starship"][[1]] 
     movies <- starwars_data()[row_count, "Movies"][[1]] 

     paste("Name: ", "<b>",starwars_data()[row_count,"Name"],"<br>","</b>", 
      "Gender: ", "<b>",starwars_data()[row_count,"Gender"],"<br>","</b>", 
      "Birth: ", "<b>",starwars_data()[row_count,"Birth"],"<br>","</b>", 
      "Homeworld: ", "<b>",starwars_data()[row_count,"Homeworld"],"<br>","</b>", 
      "Species: ", "<b>",starwars_data()[row_count,"Species"],"<br>","</b>", 
      "Height: ", "<b>",starwars_data()[row_count,"Height"],"<br>","</b>", 
      "Weight: ", "<b>",starwars_data()[row_count,"Weight"],"<br>","</b>", 
      "Hair: ", "<b>",starwars_data()[row_count,"Hair"],"<br>","</b>", 
      "Skin: ", "<b>",starwars_data()[row_count,"Skin"],"<br>","</b>", 
      "Eyes: ", "<b>",starwars_data()[row_count,"Eyes"],"<br>","</b>", 
      "<br>", 
      "Vehicles: ", "<b>", vectorBulletList(vehicles),"<br>","</b>", 
      "<br>", 
      "Starship: ", "<b>", vectorBulletList(starships),"<br>","</b>", 
      "<br>", 
      "Movies: ", "<b>", vectorBulletList(movies),"<br>","</b>") 
    } 
    }) 


} 
shinyApp(ui, srv) 

답변

2

문제는

귀하의 데이터 테이블은 (당신이 당신의 음모에 포인트를 선택하면 업데이트 있음) dataframe을 기반으로,하지만 당신은 output$txt의 원래 starwars_data() dataframe을 부분 집합하고 있습니다. 데이터 테이블에 사용 된 데이터 프레임과 다른 데이터 프레임에서 행을 가져옵니다. 따라서 output$txt에 을 사용해야합니다.

하지만 output$txt을 생성하기 위해 에 필요한 모든 항목 (예 : 영화, 우주선, 차량)이 포함되어 있지 않습니다. 을 정의 할 때 열의 하위 집합을 선택하는 대신 hide the columns from the datatable output을 사용할 수 있습니다.

우선 해결, 우리는 우리가 숨길 컬럼의 인덱스를 얻을 수 있습니다. 여기에 우리가 할 거라고 방법의 예는 다음과 같습니다

### select columns to remove based on columns we want to show ### 
columns2show <- c("name", "birth_year", "mass", "vehicles") # columns to show 
columns2hide <- which(!(colnames(starwars) %in% columns2show)) # column index to hide 
colnames(starwars)[columns2hide] # check hidden columns 

편집 : krakowi는 지적, 우리의 열 인덱스가 R을 기반으로하지만, 데이터 테이블 자바 스크립트로 생성됩니다. R은 1에서 시작하지만 자바 스크립트는 0에서 시작하기 때문에 원본 응답은 데이터 테이블의 잘못된 열을 가져옵니다. 따라서 자바 스크립트로 계산할 때 columns2hide에서 1을 빼야 올바른 열 인덱스를 얻을 수 있습니다. 아래를 참조

columns2hide <- columns2hide - 1 

다음, 우리는 options을 추가하여 데이터 테이블에서 이러한 열을 숨길해야합니다 :

datatable(d, selection = 'single', rownames = FALSE, 
        ## columns to hide ## 
        options = list(columnDefs = list(list(visible = FALSE, targets = columns2hide)))) 

마지막으로, output$txt, 우리는 에 starwars_data()을 변경해야합니다 그래서 우리 올바른 데이터 프레임에서 행을 가져옵니다.

것은 코드의 완성 : 당신의 도움에 대한

library(shiny) 
library(dplyr) 
library(DT) 
library(plotly) 


# 1) Prepare layout 


hair = starwars %>% 
    select(hair_color) %>% 
    arrange(hair_color) %>% 
    distinct() 


spec = starwars %>% 
    select(species) %>% 
    arrange(species) %>% 
    distinct() 


ui <- fluidPage(
    sidebarLayout(
     sidebarPanel(
      selectInput('hair', 'Hair', hair, multiple = TRUE), 
      selectInput('spec', 'Species', spec, multiple = TRUE), 
      htmlOutput('txt') 
     ), 
     mainPanel(
      plotlyOutput('plot'), 
      dataTableOutput('table') 
     ) 
    ) 
) 

# 2) Prepare data 

srv <- function(input, output){ 

    starwars_data <- reactive({ 
     starwars_data_as_table <- as.data.frame(starwars) 
     starwars_data_as_table = starwars_data_as_table %>% 
      tibble::rownames_to_column(var = 'ID') 

     starwars_data_as_table$gender[is.na(starwars_data_as_table$gender)] <- 'not applicable' 
     starwars_data_as_table$homeworld[is.na(starwars_data_as_table$homeworld)] <- 'unknown' 
     starwars_data_as_table$species[is.na(starwars_data_as_table$species)] <- 'unknown' 
     starwars_data_as_table$hair_color[is.na(starwars_data_as_table$hair_color)] <- 'not applicable' 

     # a) add missing info 

     starwars_data = starwars_data_as_table %>% 
      mutate(
       height = case_when(
        name == 'Finn' ~ as.integer(178), 
        name == 'Rey' ~ as.integer(170), 
        name == 'Poe Dameron' ~ as.integer(172), 
        name == 'BB8' ~ as.integer(67), 
        name == 'Captain Phasma' ~ as.integer(200), 
        TRUE ~ height 
       ), 
       mass = case_when(
        name == 'Finn' ~ 73, 
        name == 'Rey' ~ 54, 
        name == 'Poe Dameron' ~ 80, 
        name == 'BB8' ~ 18, 
        name == 'Captain Phasma' ~ 76, 
        TRUE ~ mass 
       ), 
       film_counter = lengths(films), 
       vehicle_counter = lengths(vehicles), 
       starship_counter = lengths(starships) 
      ) 

     colnames(starwars_data) <- c("ID", "Name","Height", "Weight", 
            "Hair","Skin","Eyes", 
            "Birth", "Gender", 
            "Homeworld","Species", "Movies", 
            "Vehicles", "Starship", "Number of movies", 
            "Number of vehicles", "Number of starships") 
     starwars_data 

    }) 

    # filter data using input box 
    starwars_data_filtered <- reactive({ 

     dta <- starwars_data() 
     if(length(input$hair) > 0){ 
      dta <- dta %>% 
       filter(Hair %in% input$hair) 
     } 
     if (length(input$spec) > 0) { 
      dta <- dta %>% 
       filter(Species %in% input$spec) 
     } 
     if (length(input$spec) > 0 & length(input$hair) > 0) { 
      dta <- dta %>% 
       filter(Hair %in% input$hair) %>% 
       filter(Species %in% input$spec) 
     } 
     dta 
    }) 



    output$plot <- renderPlotly({ 
     plot_ly(starwars_data_filtered(), 
       source = 'scatter') %>% 
      add_markers(
       x = ~Height, 
       y = ~Homeworld, 
       color = ~factor(Gender), 
       key = ~ID 
      ) %>% 
      layout(
       xaxis = list(title = 'Height', rangemode = "tozero"), 
       yaxis = list(title = 'Homeland', rangemode = "tozero"), 
       dragmode = "select" 
      ) 
    }) 


    selected_data = reactive({ 
     # need to keep all columns from the original dataframe 
     # to have necessary info for output$txt 
     sel_data = starwars_data_filtered() 
     ed = event_data("plotly_selected", source = "scatter") 
     if(!is.null(ed)){ 
      sel_data = sel_data %>% 
       filter(ID %in% ed$key)  
     } 
     sel_data 
    }) 

    output$table = renderDataTable({ 
     d = selected_data() 

     # column names to show in datatable 
     columns2show <- c("ID", "Name", "Height", "Weight", "Hair", "Birth", 
          "Number of movies", "Number of vehicles", "Number of starships") 
     # column indexes to hide in datatable - subtract one to account for JS indexing 
     columns2hide <- which(!(colnames(selected_data()) %in% columns2show)) 
     columns2hide <- columns2hide - 1 

     if(!is.null(d)){ 
      datatable(d, selection = 'single', rownames = FALSE, 
         ## columns to hide ## 
         options = list(columnDefs = list(list(visible = FALSE, targets = columns2hide)))) 
     } 
    }) 

    output$txt = renderText({ 
     row_count <- input$table_rows_selected 
     if(!is.null(row_count)){ 

      # a function to create a list from the vector 
      vectorBulletList <- function(vector) { 
       if(length(vector > 1)) { 
        paste0("<ul><li>", 
          paste0(
           paste0(vector, collpase = ""), collapse = "</li><li>"), 
          "</li></ul>") 
       } 
      } 

      # need to subset dataframe that reacts to selecting points on plot 
      # change starwars_data() to selected_data() 

      # in starwars dataframe, vehicles and starships are lists 
      # need to select the first element of the list (the character vector) 
      vehicles <- selected_data()[row_count, "Vehicles"][[1]] 
      starships <- selected_data()[row_count, "Starship"][[1]] 
      movies <- selected_data()[row_count, "Movies"][[1]] 

      paste("Name: ", "<b>",selected_data()[row_count,"Name"],"<br>","</b>", 
        "Gender: ", "<b>",selected_data()[row_count,"Gender"],"<br>","</b>", 
        "Birth: ", "<b>",selected_data()[row_count,"Birth"],"<br>","</b>", 
        "Homeworld: ", "<b>",selected_data()[row_count,"Homeworld"],"<br>","</b>", 
        "Species: ", "<b>",selected_data()[row_count,"Species"],"<br>","</b>", 
        "Height: ", "<b>",selected_data()[row_count,"Height"],"<br>","</b>", 
        "Weight: ", "<b>",selected_data()[row_count,"Weight"],"<br>","</b>", 
        "Hair: ", "<b>",selected_data()[row_count,"Hair"],"<br>","</b>", 
        "Skin: ", "<b>",selected_data()[row_count,"Skin"],"<br>","</b>", 
        "Eyes: ", "<b>",selected_data()[row_count,"Eyes"],"<br>","</b>", 
        "<br>", 
        "Vehicles: ", "<b>", vectorBulletList(vehicles),"<br>","</b>", 
        "<br>", 
        "Starship: ", "<b>", vectorBulletList(starships),"<br>","</b>", 
        "<br>", 
        "Movies: ", "<b>", vectorBulletList(movies),"<br>","</b>") 
     } 
    }) 


} 
shinyApp(ui, srv) 
+0

많은 감사합니다! 단계별로 귀하의 전체 설명은 내가 무슨 일이 일어나고 있는지 이해하는 것은 정말로 중요합니다. 나는 한 달 전에 R R 모험을 시작했는데, 때로는 사물을 전체적으로 보려고 애를 씁니다. – krakowi

+0

안녕하세요, 죄송합니다. 귀찮습니다.하지만 모든 column2show가 datatable로 표시되는 것처럼 보입니다. 출생 대신에 성별이 보이고 영화 수가 누락되었습니다. – krakowi

+0

도움이 필요하지 않습니다. :) JavaScript가 0에서 인덱싱을 시작하는 반면 R은 1에서 시작하므로 columns2hide에서 1을 뺀 다음 잘 작동합니다. – krakowi