基于另一列中用户输入的值,在列中反应性地进行计算。

huangapple go评论72阅读模式
英文:

Reactively doing calculations in columns, based on the values of user input in another column

问题

我有一个应用程序,在该应用程序中,用户在闪亮的DT中输入文本和数字信息,然后这些值保存在数据框中。其中一个列,Duration,接受一个数字,然后另外两列,StartEnd,根据这些值进行计算。我知道计算是进行的,因为如果我将Duration的值从0更改为任何数字,那么数学计算就会进行。问题是,如果我在应用程序中更改Duration的值,那么StartEnd的值不会发生响应性变化。

有人知道为什么会这样吗?

英文:

I have an app in which the user inputs text and numeric information in a shiny DT, and then those values are saved in a dataframe. One of the columns, Duration, intakes a number and then two other columns, Start and End, do calculations based on those values. I know that the calculations are being done because if I change the value for Duration from 0 to any number, the math is done. The problem is that if I change the value for Duration in the app, the values for Start and End do not reactively change.

Does anyone have any ideas as to why this would be?

library(shiny)
library(tidyverse)
library(DT)


ui <- fluidPage(
  
  #numeric input that determines the number of rows for the phases table
  numericInput("num_exps", 
               label = "Enter the number of experimental phases:",
               min = 1,
               max = 1000,
               value = 1),
  
  DT::dataTableOutput('phasesTable'),
  
  br(),
  br(),
  
  verbatimTextOutput("reactive_verbatim"),
  br(),
  DT::dataTableOutput('habit_table'),
  br(),
  verbatimTextOutput("habit_verbatim"),
)


server <- function(input, output, session) {
  
  #Number output for number of conditions
  output$value <- renderPrint({ input$num_exps })
  
  #Creating a data.frame that is dynamic in its length
  phases_df <- reactive({data.frame(
    'Phase Number' = sapply(1: as.integer(input$num_exps), function(i) { i })  )
  })
  
  #calculating the start and end times for each phase, and adding them to phases_df as timings
  timings <- reactive({
    req(phases_df()) %>% 
      mutate('Phase Name' = "") %>%
      mutate('Phase Group' = 0) %>%
      mutate(Duration = 0) %>%
      mutate(End = cumsum(Duration)) %>%
      mutate(Start = End - Duration) %>%
      relocate(Start, .before = End)
  })
  
  #Convert dat1 reactive to data frame and set to a reactiveVal
  Dat = reactiveVal()
  
  observe({
    d = timings()
    d = as.data.frame(d)
    Dat(d)
  })
  
  #columns to not be editable
  checkboxesColumns_list <- c(0, 1, 5:9)
  
  output$phasesTable <- renderDT({
    datatable(
      timings(), 
      #escape = FALSE,
      editable = list(target = "cell", disable = list(columns = checkboxesColumns_list)),
      options = list(paging = FALSE,
                     ordering = FALSE,
                     scrollx = FALSE,
                     searching = FALSE,
                     stringsAsFactors = FALSE,
                     info = FALSE),
      selection = "none")}, server = FALSE)
  
  #edit the shiny datatable and dataframe based on the edits
  observeEvent(input[["phasesTable_cell_edit"]], { 
    info <- input[["phasesTable_cell_edit"]] 
    
    Dat(editData(Dat(), info))
  })
  
  #print the verbatim of the dataframe
  output$reactive_verbatim <- renderPrint({ 
    str(Dat())
  })
  
  
}
shinyApp(ui, server)

答案1

得分: 1

我尝试了一下。我确信有更简洁的解决方案,但我认为这个方法会让你朝着正确的方向前进。不再使用timings响应式来生成你的数据表,我决定使用Dat,因为在这里使用响应式Val比响应式更容易操作。

我创建了两个辅助函数create_timingscreate_dt

create_timingsphases_df()构建初始数据框以设置timings()。然后,在此之后,一切都使用了Dat响应式Val。当进行新更改时,将使用更新的数据运行create_timings。这会更新Dat,从而使output$phasesTable无效,触发它输出新的DT。

基于另一列中用户输入的值,在列中反应性地进行计算。

server <- function(input, output, session) {
  
  #Number output for number of conditions
  output$value <- renderPrint({ input$num_exps })
  
  #Creating a data.frame that is dynamic in its length
  phases_df <- reactive({
    data.frame(
      'Phase Number' = sapply(1:as.integer(input$num_exps), function(i) {i})
    )
  })
  
  # Initialize timings_df then perform calculations based on user input for "Duration"
  create_timings = function(phases) {
    # init df
    if(length(phases) == 1) {
      phases %>%
        mutate(
          'Phase Name' = "",
          'Phase Group' = 0,
           Duration = 0,
           Start = 0,
           End = 0
        )
    } else {
      phases %>%
        mutate(End = cumsum(Duration)) %>%
        mutate(Start = End - Duration)
    }
  }
  
  # Helper dt func
  create_dt = function(dt, checkboxesColumns_list = c(0, 1, 5:9)) {
    datatable(
      dt, 
      #escape = FALSE,
      editable = list(target = "cell", disable = list(columns = checkboxesColumns_list)),
      options = list(paging = FALSE,
                     ordering = FALSE,
                     scrollx = FALSE,
                     searching = FALSE,
                     stringsAsFactors = FALSE,
                     info = FALSE),
      selection = "none")
  }
  
  #calculating the start and end times for each phase, and adding them to phases_df as timings
  timings <- reactive({
    req(phases_df())
    create_timings(phases_df())
  })
  
  #Convert dat1 reactive to data frame and set to a reactiveVal
  Dat = reactiveVal()
  
  observe({
    d = timings()
    d = as.data.frame(d)
    Dat(d)
  })
  
  #columns to not be editable
  # checkboxesColumns_list <- c(0, 1, 5:9)
  
  # Uses the reactive Dat() object here
  output$phasesTable <- renderDT({
    create_dt(dt = Dat())
  }, server = FALSE)
  
  #edit the shiny datatable and dataframe based on the edits
  observeEvent(input[["phasesTable_cell_edit"]], { 
    info <- input[["phasesTable_cell_edit"]]
    
    # This updates Dat with new data which is first passed into `create_timings`
    Dat(create_timings(editData(Dat(), info)))
  })
  
  #print the verbatim of the dataframe
  output$reactive_verbatim <- renderPrint({ 
    str(Dat())
  })
  
}
英文:

I took a stab at this. I'm sure there's a cleaner solution but I think this will get you in the right direction. Instead of using the timings reactive for generating your datatable. I decided to use Dat since it's easier to work with a reactiveVal here instead of a reactive.

I created two helper functions create_timings and create_dt.

create_timings builds out the initial df from the phases_df() to set up timings(). Then after that everything is using the Dat reactiveVal. When new changes are made, create_timings is run with the updated data. This updates Dat which invalidates output$phasesTable triggering it to output the new DT.

基于另一列中用户输入的值,在列中反应性地进行计算。

server &lt;- function(input, output, session) {
#Number output for number of conditions
output$value &lt;- renderPrint({ input$num_exps })
#Creating a data.frame that is dynamic in its length
phases_df &lt;- reactive({
data.frame(
&#39;Phase Number&#39; = sapply(1:as.integer(input$num_exps), function(i) {i})
)
})
# Initialize timings_df then perform calculations based on user input for &quot;Duration&quot;
create_timings = function(phases) {
# init df
if(length(phases) == 1) {
phases %&gt;%
mutate(
&#39;Phase Name&#39; = &quot;&quot;,
&#39;Phase Group&#39; = 0,
Duration = 0,
Start = 0,
End = 0
)
} else {
phases %&gt;%
mutate(End = cumsum(Duration)) %&gt;%
mutate(Start = End - Duration)
}
}
# Helper dt func
create_dt = function(dt, checkboxesColumns_list = c(0, 1, 5:9)) {
datatable(
dt, 
#escape = FALSE,
editable = list(target = &quot;cell&quot;, disable = list(columns = checkboxesColumns_list)),
options = list(paging = FALSE,
ordering = FALSE,
scrollx = FALSE,
searching = FALSE,
stringsAsFactors = FALSE,
info = FALSE),
selection = &quot;none&quot;)
}
#calculating the start and end times for each phase, and adding them to phases_df as timings
timings &lt;- reactive({
req(phases_df())
create_timings(phases_df())
})
#Convert dat1 reactive to data frame and set to a reactiveVal
Dat = reactiveVal()
observe({
d = timings()
d = as.data.frame(d)
Dat(d)
})
#columns to not be editable
# checkboxesColumns_list &lt;- c(0, 1, 5:9)
# Uses the reactive Dat() object here
output$phasesTable &lt;- renderDT({
create_dt(dt = Dat())
}, server = FALSE)
#edit the shiny datatable and dataframe based on the edits
observeEvent(input[[&quot;phasesTable_cell_edit&quot;]], { 
info &lt;- input[[&quot;phasesTable_cell_edit&quot;]]
# This updates Dat with new data which is first passed into `create_timings`
Dat(create_timings(editData(Dat(), info)))
})
#print the verbatim of the dataframe
output$reactive_verbatim &lt;- renderPrint({ 
str(Dat())
})
}

huangapple
  • 本文由 发表于 2023年6月1日 02:19:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76376310.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定