如何在闪亮中将文本传递给 summarise()

How can I pass text to summarise() in shiny

提问人:toxin 提问时间:10/24/2023 最后编辑:toxin 更新时间:10/25/2023 访问量:69

问:

我正在构建一个 shinyapp。我尝试将用户选择为 char 的函数保存到 a 中,然后用于生成类似“n = n(), sum = sum(value), mean = mean(value)” 或更短的东西,最后传递给 summarise()
我尝试了 和 ,都失败了。
data.frame()paste()enquote()eval(parse())

library(shiny)
library(shinyjs)
library(tidyverse)

ui <- fluidPage(
  useShinyjs(),
  fluidRow(
    column(width = 3,
      wellPanel(
        selectInput("data_groupby", label = "group_by", choices = c(Choose = "", "group", "sameple", "var"), selected = c("group", "var"), multiple = TRUE),
        actionButton("data_groupby_ok", "click here to group_by"),
        selectInput("data_summarise_fun", "Function", choices = c(Choose = "", "n", "sum", "mean")),
        actionButton("data_summarise_add", "Summarise"),
        h4("paras to be passed to summarise():"),
        verbatimTextOutput("summarise_paras")
      )
    ),
    column(width = 3,
      h4("data:"),
      verbatimTextOutput("data")
    ),
    column(width = 3,
      h4("data_summarised:"),
      verbatimTextOutput("data_summarised")
    ),
    column(width = 3,
      h4("data_desired (if add 3 functions):"),
      verbatimTextOutput("data_desired")
    )
  )
)

server <- function(input, output, session) {

  aggr <- reactiveValues(
    data = NULL,
    summarise_paras = data.frame(column = character()),
    data_grouped = NULL,
    data_summarised = NULL,
    data_desired = NULL
  )

  set.seed(50)
  aggr$data <- data.frame(
    group = rep(LETTERS[1:3], each = 6),
    sample = rep(c(paste0("A_", 1:3), paste0("B_", 1:3), paste0("C_", 1:3)), each = 2),
    var = rep(c("height", "weight"), times = 9),
    value = runif(18, min = 0, max = 1)
  )

  aggr$data_desired <- data.frame(
    group = rep(LETTERS[1:3], each = 6),
    sample = rep(c(paste0("A_", 1:3), paste0("B_", 1:3), paste0("C_", 1:3)), each = 2),
    var = rep(c("height", "weight"), times = 9),
    value = runif(18, min = 0, max = 1)
  ) %>%
    group_by(group, var) %>% summarise(n = n(), sum = sum(value), mean = mean(value))

  observeEvent(input$data_groupby_ok, {
    groupby <- input$data_groupby
    aggr$data_grouped <- aggr$data %>% group_by(across(all_of(groupby)))
    disable("data_groupby")
  })

  observe({
    toggleState("data_summarise_add", nchar(input$data_summarise_fun) >0)
  })

  observeEvent(input$data_summarise_add, {

    tryCatch({
      fun <- input$data_summarise_fun

      if (fun == "n") {
        para <- "n = n()"
      } else if (fun == "sum") {
        para <- "sum = sum(value)"
      } else if (fun == "mean") {
        para <- "mean = mean(value)"
      }
      aggr$summarise_paras <- aggr$summarise_paras %>%
                              filter(column != para) %>%
                              bind_rows(data.frame(column = para))
      paras <- paste(aggr$summarise_paras$column, collapse = ",")
      aggr$data_summarised <- aggr$data_grouped %>% summarise(enquote(paras))

      reset("data_summarise_fun")
    }, error = function(e) {
      showNotification(paste(e$message), type = "error", duration = 5)
    })
  })

  output$data <- renderPrint({print(aggr$data)})
  output$summarise_paras <- renderPrint({print(aggr$summarise_paras)})
  output$data_summarised <- renderPrint({print(aggr$data_summarised)})
  output$data_desired <- renderPrint({print(aggr$data_desired)})
}

shinyApp(ui = ui, server = server)

我想要的输出是 ,它打印在我闪亮的示例中aggr$data_desired

r dplyr eval summarize nse

评论

0赞 Limey 10/24/2023
我认为这可能是一个 XY 问题为什么要定义使用字符串做什么?因为您想控制它在功能或应用程序中的作用,也许?summarise
0赞 toxin 10/24/2023
是的!我想在 Shiny 中使用它。将函数作为 char 保存到 a 中,然后用于生成类似 “n = n(), sum = sum(value), mean = mean(value)” 的内容,最后传递给data.frame(paras = character())paste(., collapse = ",")summarise()
1赞 Limey 10/24/2023
我怀疑@Mark他们说没有办法以你想做的方式做你想做的事是正确的。因此,根据您(OP)之前的评论,我认为您可能希望允许您的应用程序的用户选择一些分组列,选择一个分析列,选择一些汇总统计信息,然后根据他们的选择为他们提供输出。我接近了吗?如果是这样,那么这绝对是一个 XY 问题。请描述您希望您的应用如何运行。
0赞 toxin 10/25/2023
是的,你们是对的。我用我试图解决的问题的最小示例重新编辑了我的问题

答:

0赞 Mark 10/24/2023 #1

问题是(大概)您希望值是“n = 10,sum = -0.369,mean = -0.036987”之类的值,但是您编写的不是这样 - 这很糟糕,因为它不是有效的 R 代码(您不能只用 R 编写,您会得到错误,但也因为 equals 用于赋值,并且您似乎不想赋值给变量。x = 1, y = 2Error: unexpected ',' in "x = 1,"n()n

一种选择是使用胶水

data |> summarise(val = glue::glue("n = {n()}, sum = {sum(value)}, mean = {mean(value)}"), .by = c(group, var))

输出:

   group var                                                          val
1      A   a n = 10, sum = -0.369871614091487, mean = -0.0369871614091487
2      A   b   n = 10, sum = -4.90551745324375, mean = -0.490551745324375
3      A   c   n = 10, sum = -4.04729937846017, mean = -0.404729937846017
4      A   d   n = 10, sum = -2.54372850319173, mean = -0.254372850319173
5      B   c   n = 10, sum = 0.936866512219415, mean = 0.0936866512219415
6      B   d   n = 10, sum = -3.34912487194284, mean = -0.334912487194284
7      B   a n = 10, sum = -0.341116609361569, mean = -0.0341116609361569
8      B   b   n = 10, sum = -3.18225275070246, mean = -0.318225275070246
9      C   a   n = 10, sum = -4.74290766135976, mean = -0.474290766135976
10     C   b   n = 10, sum = 0.112798793892494, mean = 0.0112798793892494
11     C   c   n = 10, sum = -1.44746005504636, mean = -0.144746005504636
12     C   d   n = 10, sum = -1.31363366405953, mean = -0.131363366405953
13     D   c     n = 10, sum = -3.3104100896434, mean = -0.33104100896434
14     D   d     n = 10, sum = 1.38864716115738, mean = 0.138864716115738
15     D   a     n = 10, sum = 6.04682327356391, mean = 0.604682327356391
16     D   b   n = 10, sum = -2.40214704657337, mean = -0.240214704657337
17     E   a   n = 10, sum = -2.66630215861559, mean = -0.266630215861559
18     E   b n = 10, sum = -0.889225624753177, mean = -0.0889225624753177
19     E   c   n = 10, sum = -6.39670148391452, mean = -0.639670148391452
20     E   d   n = 10, sum = -4.79912286801664, mean = -0.479912286801664

评论

0赞 toxin 10/24/2023
这不是我想要的结果,它的结构应该像 result1,以最常见的方式
0赞 Mark 10/24/2023
谢谢你现在告诉我,在我已经写好了答案之后,哈哈
1赞 Mark 10/24/2023
我认为我对为什么你的代码不起作用的反馈的本质是一样的。不过,将整个内容放在纯文本中,然后在其上运行 eval(parse()) 是有效的
0赞 toxin 10/24/2023
对于不完整的问题,我真的很抱歉,我试图跳过描述太多,但相反,我制造了更多的麻烦
0赞 Mark 10/25/2023
@toxin没关系——如果你能回答 Limey 的问题,那就太好了
0赞 Limey 10/25/2023 #2

这是一个 MWE,我认为它实现了你的目标,但采取了与你不同的方法。我放弃了中间数据框,并使用 Shiny 小部件的值来生成汇总表。唯一棘手的部分是使用 ,这是将 Shiny 的输入字符串转换为 tidyverse 所需的 ed 符号所必需的。!!as.symbol()tidy-select

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

ui <- fluidPage(
  selectInput(
    "analysisVar", 
    "Analysis variable:", 
    choices = names(mtcars),
    selected = "mpg"
  ),
  selectInput(
    "groupVar", 
    "Group variable:", 
    choices = names(mtcars),
    selected = "cyl"
  ),
  selectInput(
    "statistics", 
    "Summary statistics", 
    choices = c("mean", "min", "max", "median"), 
    multiple = TRUE,
    selected = "mean"
  ),
  dataTableOutput("results")
)

server <- function(input, output, session) {
  summaryStats <- reactive({
    req(input$groupVar, input$analysisVar)
    
    mtcars %>% 
      group_by(!!as.symbol(input$groupVar)) %>% 
      summarise(
        across(
          !!as.symbol(input$analysisVar), 
          .fns = list(mean = mean, min = min, max = max, median = median),
          .names = "{.fn}"
        ),
        .groups = "drop"
      ) %>% 
      select(input$groupVar, input$statistics)
  })
  
  output$results <- renderDataTable({ summaryStats() })
}

shinyApp(ui, server)