闪亮:ggplot的动态颜色(填充)输入

Mal*_*l_a 3 r colors shiny shinyjs

我确实需要一些帮助作为帖子:闪亮服务器中的动态颜色输入并不能完全解决我的问题.

我想在我闪亮的应用程序中选择动态颜色(填充).我准备了一个示例代码:

library(shiny)
library(shinyjs)
library(reshape2)
library(ggplot2)

dat <- data.frame(matrix(rnorm(60, 2, 3), ncol=3))
dat <- melt(dat)

runApp(shinyApp(
  ui = fluidPage(
    selectizeInput("select","Select:", choices=as.list(levels(dat$variable)), selected="X1",multiple =TRUE),
    uiOutput('myPanel'),
    plotOutput("plot"),
    downloadButton('downloadplot',label='Download Plot')
  ),
  server = function(input, output, session) {
    cols <- reactive({
      lapply(seq_along(unique(input$select)), function(i) {
        colourInput(paste("col", i, sep="_"), "Choose colour:", "black")        
      })
    })

    output$myPanel <- renderUI({cols()})

    cols2 <- reactive({        
      if (is.null(input$col_1)) {
        cols <- rep("#000000", length(input$select))
      } else {
        cols <- unlist(colors())
      }
      cols})

    testplot <- function(){
      dat <- dat[dat$variable %in% input$select, ]
      ggplot(dat, aes(x=variable,y=value, fill=cols2()[1])) + geom_boxplot()}

    output$plot <- renderPlot({testplot()})

    output$downloadplot <- downloadHandler(
      filename ="plot.pdf",
      content = function(file) {
        pdf(file, width=12, height=6.3)
        print(testplot())
        dev.off()
      })
  }
))
Run Code Online (Sandbox Code Playgroud)

我希望用户选择boxplot的填充颜色.将根据所选变量的数量显示颜色小部件的数量selectizeInput("select"....直到这一点,一切都运作良好,但更进一步,我无法弄清楚如何将这种颜色应用到ggplot等...

这是我的问题:

  1. 我如何正确地将填充颜色连接到ggplot

  2. 我可以使默认颜色colourInput()对应默认颜色调色板(不是一种颜色 - >在我的情况下是黑色)

  3. 我不喜欢选择颜色文本,而是colourInput(paste("col", i, sep="_"), "Choose colour:",希望拥有变量的相应名称(选择变量selectizeInput)(在本例中为X1,X2和X3)

  4. 我想要一个可以重置所有选择颜色的按钮

提前谢谢大家,我希望这可以解决

干杯

Mic*_*jka 6

这些是非常好的和具体的问题,我很高兴,希望,回答他们:)

  1. 我如何正确地将填充颜色连接到ggplot

在这种情况下,我认为最好的方法是根据variable(反应性)填充框并添加一个新图层,scale_fill_manual在该图层中为不同的框指定自定义颜色.颜色的数量必须明显等于水平的数量variable.这可能是最好的方法,因为你总会有一个正确的传奇.

ggplot(dat, aes(x = variable, y = value, fill = variable)) + 
          geom_boxplot() +
          scale_fill_manual(values = cols)
Run Code Online (Sandbox Code Playgroud)
  1. 我可以使colourInput()的默认颜色对应默认调色板(不是一种颜色 - >在我的情况下是黑色)

当然,你可以做到.

首先,您需要知道ggplot使用的离散变量的默认颜色.为了生成这些颜色,我们将使用gg_color_hue这个讨论中找到的函数.我已将其名称更改gg_fill_hue为遵循ggplot惯例.

我们可以renderUI在我们首先指定所选级别/变量的地方编码所有内容.为了消除由于动态(并且可能以不同的顺序)生成的小部件而导致的明确性,我们对级别/变量的名称进行排序.

然后我们生成适当数量的默认颜色,gg_fil_hue并将它们分配给适当的小部件.

为了简化操作,我们将IDs这些小部件更改为col+"varname",由下式给出input$select

output$myPanel <- renderUI({ 
      lev <- sort(unique(input$select)) # sorting so that "things" are unambigious
      cols <- gg_fill_hue(length(lev))

      # New IDs "colX1" so that it partly coincide with input$select...
      lapply(seq_along(lev), function(i) {
        colourInput(inputId = paste0("col", lev[i]),
                    label = paste0("Choose colour for ", lev[i]), 
                    value = cols[i]
        )        
      })
    })
Run Code Online (Sandbox Code Playgroud)

3.而不是在colourInput中选择颜色文本(粘贴("col",i,sep ="_"),"选择颜色:",我希望得到变量的相应名称(从selectizeInput中选择变量)(在这种情况X1,X2和X3)

它也是在上面的代码中完成的 - 简单粘贴.


现在,让我们来看看由于生成的小部件的动态数量而产生的一个非常重要的问题.我们必须根据一个独特的方式设置盒子的颜色,colorInput这些输入可能有1,2或10个.

我相信,解决这个问题的一个非常好的方法是创建一个字符向量,其中的元素指定了我们通常如何访问这些小部件.在下面的示例中,此向量如下所示:c("input$X1", "input$X2", ...).

然后使用非标准评估(eval,parse)我们可以评估这些输入以获得具有所选颜色的矢量,然后我们将其传递给scale_fill_manual图层.

为了防止选择之间可能出现的错误,我们将使用函数"req"来确保带颜色的向量的长度与所选级别/变量的长度相同.

output$plot <- renderPlot({
        cols <- paste0("c(", paste0("input$col", sort(input$select), collapse = ", "), ")")
        # print(cols)
        cols <- eval(parse(text = cols))
        # print(cols)

        # To prevent errors
        req(length(cols) == length(input$select))

        dat <- dat[dat$variable %in% input$select, ]
        ggplot(dat, aes(x = variable, y = value, fill = variable)) + 
          geom_boxplot() +
          scale_fill_manual(values = cols)

    })
Run Code Online (Sandbox Code Playgroud)
  1. 我想要一个可以重置所有选择颜色的按钮

actionButton客户端定义了一个ID="reset"我们创建一个将要更新colorInputs 的观察者.

我们的目标是返回一个列表,updateColourInput其中包含适用于每个可用colourInput窗口小部件的参数化.

我们使用所有选定的级别/变量定义变量,并生成适当数量的默认颜色.我们再次对矢量进行排序以避免歧义.

然后我们使用lapplydo.call调用具有updateColourInput作为列表给出的指定参数的函数.

observeEvent(input$reset, {
      # Problem: dynamic number of widgets
      # - lapply, do.call

      lev <- sort(unique(input$select))
      cols <- gg_fill_hue(length(lev))

      lapply(seq_along(lev), function(i) {
              do.call(what = "updateColourInput",
                      args = list(
                        session = session,
                        inputId = paste0("col", lev[i]),
                        value = cols[i]
                      )
              )
      })
    })
Run Code Online (Sandbox Code Playgroud)

完整示例:

library(shiny)
library(shinyjs)
library(reshape2)
library(ggplot2)

dat <- data.frame(matrix(rnorm(60, 2, 3), ncol=3))
dat <- melt(dat)

# Function that produces default gg-colours is taken from this discussion:
# https://stackoverflow.com/questions/8197559/emulate-ggplot2-default-color-palette
gg_fill_hue <- function(n) {
  hues = seq(15, 375, length = n + 1)
  hcl(h = hues, l = 65, c = 100)[1:n]
}

runApp(shinyApp(
  ui = fluidPage(
    selectizeInput("select", "Select:", 
                   choices = as.list(levels(dat$variable)), 
                   selected = "X1", 
                   multiple = TRUE), 
    uiOutput('myPanel'),
    plotOutput("plot"),
    downloadButton('downloadplot', label = 'Download Plot'),
    actionButton("reset", "Default colours", icon = icon("undo"))
  ),
  server = function(input, output, session) {

    output$myPanel <- renderUI({ 
      lev <- sort(unique(input$select)) # sorting so that "things" are unambigious
      cols <- gg_fill_hue(length(lev))

      # New IDs "colX1" so that it partly coincide with input$select...
      lapply(seq_along(lev), function(i) {
        colourInput(inputId = paste0("col", lev[i]),
                    label = paste0("Choose colour for ", lev[i]), 
                    value = cols[i]
        )        
      })
    })


    output$plot <- renderPlot({
      cols <- paste0("c(", paste0("input$col", sort(input$select), collapse = ", "), ")")
      # print(cols)
      cols <- eval(parse(text = cols))
      # print(cols)

      # To prevent errors
      req(length(cols) == length(input$select))

      dat <- dat[dat$variable %in% input$select, ]
      ggplot(dat, aes(x = variable, y = value, fill = variable)) + 
        geom_boxplot() +
        scale_fill_manual(values = cols)

    })


    observeEvent(input$reset, {
      # Problem: dynamic number of widgets
      # - lapply, do.call

      lev <- sort(unique(input$select))
      cols <- gg_fill_hue(length(lev))

      lapply(seq_along(lev), function(i) {
        do.call(what = "updateColourInput",
                args = list(
                  session = session,
                  inputId = paste0("col", lev[i]),
                  value = cols[i]
                )
        )
      })
    })




    output$downloadplot <- downloadHandler(
      filename = "plot.pdf",
      content = function(file) {
        pdf(file, width = 12, height = 6.3)
        print(testplot())
        dev.off()
      })
  }
))
Run Code Online (Sandbox Code Playgroud)

  • 这是一个惊人的答案!我很感激,它就像一个魅力!:) (2认同)