r - Shiny: Dynamic colour (fill) input for ggplot -
i need post: dynamic color input in shiny server not give full answer problem.
i have dynamic colour (fill) selection in shiny app. have prepared sample code:
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() }) } ))
i user choose fill colour of boxplot. number of colour widgets appear according number of selected variables in selectizeinput("select"...
. till point working perfectly, going further not able figure out how apply colour ggplot, etc...
here questions:
how can connect fill colour ggplot correctly
can make default colour of
colourinput()
correspond default colour palette (not 1 colour --> in case black)instead of choose colour text in
colourinput(paste("col", i, sep="_"), "choose colour:",
love have corresponding name (choosen variableselectizeinput
) of variable (in case x1, x2 , x3)i have button reset all choosen colours
thank in advance , hope can solved
cheers
these nice , concrete questions , i'm glad to, hopefully, answer them :)
- how can connect fill colour ggplot correctly
in case best way, think, fill boxes according variable
(which reactive) , add new layer scale_fill_manual
in specify custom colours different boxes. number of colours has equal number of levels of variable
. best way because have correct legend.
ggplot(dat, aes(x = variable, y = value, fill = variable)) + geom_boxplot() + scale_fill_manual(values = cols)
- can make default colour of colourinput() correspond default colour palette (not 1 colour --> in case black)
of course, can it.
first, need know default colours discrete variables ggplot uses. generate these colours use function gg_color_hue
found in this nice discussion. i've changed name gg_fill_hue
follow ggplot convention.
we can code within renderui
first specify selected levels/variables. rid of unambiguity caused due dynamically (and possibly in different order) generated widgets, sort names of levels/variables.
then generate appropriate number of default colours gg_fil_hue
, assign them appropriate widget.
to make things easier, change ids
of these widgets col
+ "varname" given input$select
output$mypanel <- renderui({ lev <- sort(unique(input$select)) # sorting "things" unambigious cols <- gg_fill_hue(length(lev)) # new ids "colx1" partly coincide input$select... lapply(seq_along(lev), function(i) { colourinput(inputid = paste0("col", lev[i]), label = paste0("choose colour ", lev[i]), value = cols[i] ) }) })
3.instead of choose colour text in colourinput(paste("col", i, sep="_"), "choose colour:", love have corresponding name (choosen variable selectizeinput) of variable (in case x1, x2 , x3)
it done in code above - simple pasting.
now, let's take @ important issue arises due dynamical number of generated widgets. have set colours of boxes according unique colorinput
, there may 1,2 or 10 inputs.
a nice way of approaching problem, believe, create character vector elements specifying how access these widgets. in example below vector looks follows: c("input$x1", "input$x2", ...)
.
then using non-standard evaluation (eval
, parse
) can evaluate these inputs vector selected colours pass scale_fill_manual
layer.
to prevent errors may arise between selections, use function `req´ make sure length of vector colours same length of selected levels/variables.
output$plot <- renderplot({ cols <- paste0("c(", paste0("input$col", sort(input$select), collapse = ", "), ")") # print(cols) cols <- eval(parse(text = cols)) # print(cols) # 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) })
- i have button reset choosen colours
after defining actionbutton
on client side id="reset"
create observer that's going update colorinput
s.
our goal return list updatecolourinput
appropriate parametrisation each available colourinput
widget.
we define variable chosen levels/variables , generate appropriate number of default colours. again sort vector avoid ambiguity.
then use lapply
, do.call
call updatecolourinput
function specified parameters given list.
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] ) ) }) })
full example:
library(shiny) library(shinyjs) library(reshape2) library(ggplot2) dat <- data.frame(matrix(rnorm(60, 2, 3), ncol=3)) dat <- melt(dat) # function produces default gg-colours taken 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 "things" unambigious cols <- gg_fill_hue(length(lev)) # new ids "colx1" partly coincide input$select... lapply(seq_along(lev), function(i) { colourinput(inputid = paste0("col", lev[i]), label = paste0("choose colour ", 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) # 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() }) } ))
Comments
Post a Comment