Неправильные индексы строк возвращаются при выборе поля, когда отображаются только определенные наблюдения - Plotly, htmlWidgets, Shiny

Я пишу приложение Shiny, в котором у пользователя есть два входа ползунка: один для «кратного изменения», другой для «p-значения». Существует набор данных, содержащий 10 наблюдений, каждое из которых имеет кратное изменение и p-значение. Только точки, которые имеют p-значение меньше, чем входное значение ползунка «p-значение», и кратное изменение больше, чем входное значение ползунка «кратное изменение», отображаются в данный момент времени.

То, что я написал до сих пор, представлено ниже -

library(ggplot2)
library(plotly)
library(htmlwidgets)

ui <- shinyUI(fluidPage(
  sliderInput("threshP", "P-value:", min = 0, max = 1, value=0.05, step=0.05),
  sliderInput("threshFC", "Fold change:", min = 0, max = 10, value=5, step=0.5),
  plotlyOutput("plot1"),
  textOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  set.seed(1)
  threshP <- reactive(input$threshP)
  threshFC <- reactive(input$threshFC)

  dat <- data.frame(ID = paste0("ID",1:10), FC=runif(10,0,10), pval=runif(10,0,1))
  dat$ID <- as.character(dat$ID)

  # x-axis FC, y-axis pval
  xMax = max(dat$FC)
  xMin = min(dat$FC)
  yMax = max(dat$pval)
  yMin = min(dat$pval)

  df <- data.frame()
  p <- ggplot(df) + geom_point() + xlim(xMin, xMax) + ylim(yMin, yMax)
  gp <- ggplotly(p)

  output$plot1 <- renderPlotly({
    gp %>% onRender("
      function(el, x, data) {
      var dat = data.dat
      var selFC = [];
      var selP = [];
      dat.forEach(function(row){
        rowFC = row['FC']
        rowP = row['pval']
        if (rowP <= data.thP && data.thFC <= rowFC){
          selFC.push(rowFC);
          selP.push(rowP);
        }
      });

      var Traces = [];
      var tracePoints = {
        x: selFC,
        y: selP,
        mode: 'markers',
        marker: {
          color: 'black',
          size: 6
        },
      };
      Traces.push(tracePoints);
      Plotly.addTraces(el.id, Traces);

      var idRows = []
      for (a=0; a<data.dat.length; a++){
        idRows.push(data.dat[a]['ID'])
      }
      console.log(idRows)

      el.on('plotly_selected', function(e) {
        numSel = e.points.length
        Points = e.points
        selID = []
        for (a=0; a<numSel; a++){
          PN = Points[a].pointNumber
          selRow = idRows[PN]
          selID.push(selRow)
        }
        Shiny.onInputChange('selID', selID);
      })
      }", data = list(dat = dat, thP=threshP(), thFC=threshFC()))})

    selID <- reactive(input$selID)
    selDat <- reactive(dat[which(dat$ID %in% selID()), ])
    output$selectedValues <- renderPrint({str(selDat())})
})

shinyApp(ui, server)

Это создает правильный вывод, если все десять точек нанесены на график (когда ползунок «p-value» находится в положении 1, а ползунок «fold-change» — в положении 0). Например, на изображении ниже нанесены все 10 точек данных, и пользователь выбрал ту, у которой значение p (ось Y) близко к значению 1. Под графиком находится возвращенная строка, которая имеет складку. изменить (ось x) значение 6,61 и значение p (ось y) значение 0,992. Кажется, это работает.

Правильные строки фрейма данных возвращаются, если нанесены все 10 точек

Однако если не нанесены все десять точек, выдается неверный вывод. Например, на изображении ниже нанесено только 6 точек данных, и пользователь выбрал ту, у которой значение p (ось Y) близко к значению 1. Под графиком находится возвращенная строка, которая имеет сгиб- значение изменения (ось x) 2,02 и значение p (ось y) 0,77. Кажется, это не работает.

Возвращены неверные строки фрейма данных, если на график нанесено менее 10 точек

Я понимаю/полагаю, что проблема возникает из-за того, что когда не все 10 точек нанесены на график, возвращаемые индексы строк могут быть только между 1-c, где c - некоторое число меньше 10. Таким образом, индексы строк больше не совпадают с исходный фрейм данных. Однако я теряюсь в том, как решить эту проблему.

Любые советы о том, как решить эту проблему? Спасибо.

РЕДАКТИРОВАТЬ:

Благодаря полезным предложениям по отладке, я думаю, проблема может быть решена. Хитрость заключается в использовании pointNumber в качестве индекса в переменной массива, которая содержит подмножество отображаемого фрейма данных. Здесь это объект sselID.

library(ggplot2)
library(plotly)
library(htmlwidgets)

ui <- shinyUI(fluidPage(
  sliderInput("threshP", "P-value:", min = 0, max = 1, value=1, step=0.05),
  sliderInput("threshFC", "Fold change:", min = 0, max = 10, value=0, step=0.5),
  plotlyOutput("plot1"),
  verbatimTextOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  set.seed(1)
  threshP <- reactive(input$threshP)
  threshFC <- reactive(input$threshFC)

  dat <- data.frame(ID = paste0("ID",1:10), FC=runif(10,0,10), pval=runif(10,0,1))
  dat$ID <- as.character(dat$ID)
  print(dat)

  # x-axis FC, y-axis pval
  xMax = max(dat$FC)
  xMin = min(dat$FC)
  yMax = max(dat$pval)
  yMin = min(dat$pval)

  df <- data.frame()
  p <- ggplot(df) + geom_point() + xlim(xMin, xMax) + ylim(yMin, yMax)
  gp <- ggplotly(p)

  output$plot1 <- renderPlotly({
    gp %>% onRender("
      function(el, x, data) {
      var dat = data.dat
      var selFC = [];
      var selP = [];
      var sselID = [];
      dat.forEach(function(row){
        rowFC = row['FC']
        rowP = row['pval']
        rowID = row['ID']
        if (rowP <= data.thP && data.thFC <= rowFC){
          selFC.push(rowFC);
          selP.push(rowP);
          sselID.push(rowID);
        }
      });

console.log(sselID)

      var Traces = [];
      var tracePoints = {
        x: selFC,
        y: selP,
        text: sselID,
        mode: 'markers',
        marker: {
          color: 'black',
          size: 6
        },
      };
      Traces.push(tracePoints);
      Plotly.addTraces(el.id, Traces);

      el.on('plotly_selected', function(e) {

      console.log(e.points)
      numSel = e.points.length
      Points = e.points
      selID = []
      for (a=0; a<numSel; a++){
        PN = Points[a].pointNumber
        selRow = sselID[PN]
        selID.push(selRow)
      }
      Shiny.onInputChange('selID', selID);
      })
      }", data = list(dat = dat, thP=threshP(), thFC=threshFC()))})

  selID <- reactive(input$selID)
  selDat <- reactive(dat[which(dat$ID %in% selID()), ])
  output$selectedValues <- renderPrint({selDat()})
  })

shinyApp(ui, server) 

person Community    schedule 08.04.2017    source источник


Ответы (1)


У вас интересные задачи...

Я сделал несколько небольших изменений, чтобы увидеть, что происходит:

  • скорректированы начальные значения ползунков ввода
  • изменил вывод на verbatumTextOutput кадра данных выбора
  • добавлена ​​дополнительная информация во всплывающую подсказку (переменная ID), включив поле text в определение трассировки.
  • распечатал данные о событии в консоли javascript

Отсюда я мог видеть, что часто (почти всегда, но не всегда) в качестве первой точки была своего рода фиктивная точка. Удобно, что у него всегда был curveNumber ноль

  • поэтому я добавил охрану, чтобы исключить его из выбранного списка.

Я думаю, что это устраняет проблему.

library(ggplot2)
library(plotly)
library(htmlwidgets)

ui <- shinyUI(fluidPage(
  sliderInput("threshP", "P-value:", min = 0, max = 1, value=1, step=0.05),
  sliderInput("threshFC", "Fold change:", min = 0, max = 10, value=0, step=0.5),
  plotlyOutput("plot1"),
  verbatimTextOutput("selectedValues")
))

server <- shinyServer(function(input, output) {
  set.seed(1)
  threshP <- reactive(input$threshP)
  threshFC <- reactive(input$threshFC)

  dat <- data.frame(ID = paste0("ID",1:10), FC=runif(10,0,10), pval=runif(10,0,1))
  dat$ID <- as.character(dat$ID)
  print(dat)

  # x-axis FC, y-axis pval
  xMax = max(dat$FC)
  xMin = min(dat$FC)
  yMax = max(dat$pval)
  yMin = min(dat$pval)

  df <- data.frame()
  p <- ggplot(df) + geom_point() + xlim(xMin, xMax) + ylim(yMin, yMax)
  gp <- ggplotly(p)

  output$plot1 <- renderPlotly({
    gp %>% onRender("
                    function(el, x, data) {
                    var dat = data.dat
                    var selFC = [];
                    var selP = [];
                    var sselID = [];
                    dat.forEach(function(row){
                    rowFC = row['FC']
                    rowP = row['pval']
                    rowID = row['ID']
                    if (rowP <= data.thP && data.thFC <= rowFC){
                    selFC.push(rowFC);
                    selP.push(rowP);
                    sselID.push(rowID);
                    }
                    });

                    var Traces = [];
                    var tracePoints = {
                    x: selFC,
                    y: selP,
                    text: sselID,
                    mode: 'markers',
                    marker: {
                    color: 'black',
                    size: 6
                    },
                    };
                    Traces.push(tracePoints);
                    Plotly.addTraces(el.id, Traces);

                    var idRows = []
                    for (a=0; a<data.dat.length; a++){
                    idRows.push(data.dat[a]['ID'])
                    }
                    console.log(idRows)

                    el.on('plotly_selected', function(e) {
                      console.log(e.points)
                      numSel = e.points.length
                      Points = e.points
                      selID = []
                      for (a=0; a<numSel; a++){
                      CN = Points[a].curveNumber
                      if (CN>0){
                        PN = Points[a].pointNumber
                        selRow = idRows[PN]
                        selID.push(selRow)
                      } 
                    }
                    Shiny.onInputChange('selID', selID);
                    })
                    }", data = list(dat = dat, thP=threshP(), thFC=threshFC()))})

  selID <- reactive(input$selID)
  selDat <- reactive(dat[which(dat$ID %in% selID()), ])
  output$selectedValues <- renderPrint({selDat()})
  })

shinyApp(ui, server)

Скриншот:

введите здесь описание изображения

Скриншот eventData в консоли javascript:

введите здесь описание изображения

Для тех, кто интересуется, как получить доступ к консоли javascript, вы можете:

  • щелкните правой кнопкой мыши блестящее окно R-studio (или браузера).
  • выбрал "Проверить"
  • выберите запись "консоль"
  • см. вывод console.log (вы можете детализировать объекты)
person Mike Wise    schedule 09.04.2017
comment
Спасибо. Мне нравится текст всплывающей подсказки и verbatumTextOutput. Это действительно помогает отлаживать! Я думаю, что код, который вы разместили, все еще содержит ошибки. Я не думаю, что смогу прикрепить скриншот в комментариях, но в качестве примера, если я установлю p-value на 0,55 и fold-change на 0, и я выделю три точки, которые имеют самые низкие значения по оси x, только 2 из 3 пунктов верны. - person ; 09.04.2017
comment
Тем не менее, благодаря лучшей отладке с вашими предложениями, я думаю понял это. Я публикую решение в редактировании. Еще раз спасибо за помощь! - person ; 09.04.2017
comment
Ничего страшного, тоже любопытно, что это такое. - person Mike Wise; 09.04.2017
comment
Да, я понимаю, что вы имеете в виду. Эти номера точек являются мусором, когда вы увеличиваете масштаб. Я полагаю, вы могли бы взять значения точек и хешировать это. Может быть, сначала их обойти. - person Mike Wise; 10.04.2017
comment
Похоже, это может быть исправлено. Выйдет позже. github.com/ropensci/plotly/issues/931 - person Mike Wise; 12.04.2017