i do need some help as the post: Dynamic color input in shiny server does not give full answer to my problem.
I would like to have dynamic colour (fill) selection in my shiny app. I have prepared a 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 would like the user to choose fill colour of the boxplot. The number of colour widgets will appear according to number of selected variables in selectizeInput("select"...
. Till this point everything is working perfectly, however going further i am not able to figure out how to apply this colour to the ggplot, etc...
Here are my questions:
How i can connect the fill colour to ggplot correctly
Can i make the default colour of
colourInput()
correspond to the default colour palette (not to one colour --> in my case is black)Instead of Choose colour text in
colourInput(paste("col", i, sep="_"), "Choose colour:",
i would love to have the corresponding name (choosen variable fromselectizeInput
) of the variable (in this case X1, X2 and X3)I would like as well to have a button which could reset all the choosen colours
Thank You all in advance and i hope this can be solved
Cheers
These are very nice and concrete questions and I'm glad to, hopefully, answer them :)
In this case the best way, I think, is to fill boxes according to the
variable
(which is reactive) and to add a new layerscale_fill_manual
in which you specify custom colours for different boxes. The number of colours has to be obviously equal to the number of levels ofvariable
. This is probably the best way because you will always have a correct legend.Of course, you can do it.
First, you need to know the default colours for discrete variables that ggplot uses. To generate these colours we will use a function
gg_color_hue
found in this nice discussion. I've changed its name togg_fill_hue
to follow a ggplot convention.We can code everything within
renderUI
where we first specify the selected levels/variables. To get rid of unambiguity which would be caused due to dynamically (and possibly in a different order) generated widgets, we sort the names of levels/variables.Then we generate appropriate number of default colours with
gg_fil_hue
and assign them to the appropriate widget.To make things easier, we change the
IDs
of these widgets tocol
+ "varname" which is given byinput$select
It is done in the code above as well - simple pasting.
Now, let's take a look at a very important issue that arises due to dynamical number of generated widgets. We have to set the colours of boxes according to a unique
colorInput
and there may by 1,2 or even 10 those inputs.A very nice way of approaching this problem, I believe, is to create a character vector with elements specifying how we would normally access these widgets. In the example below this vector looks as follows:
c("input$X1", "input$X2", ...)
.Then using non-standard evaluation (
eval
,parse
) we can evaluate these inputs to get a vector with selected colours which we then pass toscale_fill_manual
layer.To prevent errors that may arise between selections, we will use the function `req´ to make sure that the length of the vector with colours is the same as the length of the selected levels/variables.
After defining the
actionButton
on the client side with anID="reset"
we create an observer that's going to updatecolorInput
s.Our goal is to return a list with
updateColourInput
with an appropriate parametrisation for each availablecolourInput
widget.We define a variable with all chosen levels/variables and generate an appropriate number of default colours. We again sort the vector to avoid ambiguity.
Then we use
lapply
anddo.call
to call aupdateColourInput
function with specified parameters that are given as a list.Full Example: