Forum Discussion
R Custom Visual - Setting colours for each value in a field/column
source('./r_files/flatten_HTML.r')
############### Library Declarations ###############
libraryRequireInstall("ggplot2");
libraryRequireInstall("plotly");
libraryRequireInstall("stringr");
libraryRequireInstall("reshape2");
libraryRequireInstall("ggplotify");
libraryRequireInstall("grid");
libraryRequireInstall("readxl");
####################################################
#Enable debugging in RStudio
fileRda = "C:/Users/biedermannf/Temp/tempData.Rda"
if(file.exists(dirname(fileRda)))
{
if(Sys.getenv("RSTUDIO")!="")
load(file= fileRda)
else
save(list = ls(all.names = TRUE), file=fileRda)
}
###############Internal function definitions#################
# Functions used only for this visual
#############################################################
#############################################################
# Set up parameters/report variables
#############################################################
validToPlot <- TRUE
# Data
if(!exists("columnGroups")){
columnGroups <- NULL
validToPlot <- FALSE
}
if(!exists("overlayGroups") && validToPlot){
# Set up a dummy variable with a single value
overlayGroups <- data.frame(OG = factor(rep("Dummy Value", length(columnGroups[, 1])), levels = "Dummy Value"))
}
if(!exists("stackingGroups")){
stackingGroups <- NULL
}
if(!exists("heights")){
heights <- NULL
validToPlot <- FALSE
}
#############################################################
# Column grouping parameters
#############################################################
# Default the column group labels to be slanted at 45 degrees
# and split into lines 30 chars wide.
x.axis.offset.labels <- FALSE
x.axis.labels.srt <- 45
x.axis.hjust <- 1
x.axis.maxwidth <- 30
# overlapping column parameters
# Min and max alpha - bottom column will have min alpha,
# graduating to max alpha for top column
alphaMin <- 0.2
if(exists("settings_overlay_params_alphaMin")){
alphaMin <- min(1.0, max(0.0, settings_overlay_params_alphaMin))
}
alphaMax <- 0.8
if(exists("settings_overlay_params_alphaMax")){
alphaMax <- min(1.0, max(0.0,settings_overlay_params_alphaMax))
}
overlay <- 0.8
if(exists("settings_overlay_params_overlay")){
overlay <- min(1.0, max(0.0, settings_overlay_params_overlay))
}
#############################################################
# Stacking parameters
#############################################################
#############################################################
# Check inputs
if((!exists("columnGroups") ||!exists("heights"))) # invalid input
{
validToPlot <- FALSE
}
if(validToPlot){
# Set up variables to hold the names of the supplied data columns (there'll only be one column in each incoming data frame)
cGrpsName <- names(columnGroups)[[1]]
oGrpsName <- names(overlayGroups)[[1]]
heightsName <- names(heights)[[1]]
# Set up column groups
columnGroups[, cGrpsName] <- str_wrap(columnGroups[, cGrpsName], width = 30)
colGrps <- factor(columnGroups, levels = unique(columnGroups), ordered = T)
# Set up overlay groups
oGrps <- NULL
nGrps <- 1
if(exists("overlayGroups")){
oGrps <- levels(overlayGroups[, oGrpsName])
nGrps <- length(oGrps)
}
columnWidth <- 0.9 / (nGrps - (nGrps - 1) * overlay)
}
if(validToPlot){
# Base ggplot
g <- ggplot(data=data.frame(columnGroups, overlayGroups, heights), aes(x = get(cGrpsName), y = get(heightsName), fill = get(oGrpsName)))
# Loop through each overlay group (there should be at least a dummy group here) and add to plot
for(i in 1:nGrps){
g <- g + geom_col(data = data.frame(columnGroups, overlayGroups, heights)[overlayGroups[, oGrpsName] == oGrps[i], ], aes(x = get(cGrpsName), y = get(heightsName), fill = get(oGrpsName)), width = columnWidth, position = position_nudge(x = 0.9*((i-1)/(nGrps-1)-1/2) + columnWidth * (1/2-(i-1)/(nGrps-1))) )
}
if(nGrps == 1){
# Don't produce a legend as it'll just display "Dummy Value"
# ToDo: this won't necessarily be the case if data coming only has one value in a supplied overlayGroups column - needs to be fixed
g <- g + theme(legend = element_blank())
# Set the colours of the columns
g <- g + scale_fill_manual(values=alpha(c("springgreen4", "cornflowerblue"), c(alphaMin, alphaMax)), breaks = oGrps)
} else {
# Set the colours of the columns
g <- g + scale_fill_manual(values=alpha(c("springgreen4", "cornflowerblue"), seq(alphaMin, alphaMax, length = nGrps)), breaks = oGrps)
# Add the correct axis labelling
g <- g + labs(x = cGrpsName, y = heightsName);
# Rotate the x axis text labels (if required)
g <- g + theme(axis.text.x = element_text(size = 6, angle = x.axis.labels.srt, hjust = x.axis.hjust, vjust = 1), legend.title = element_blank(), axis.title.x = element_blank());
}
#
g <- g + theme(plot.margin = unit(c(0, 0, 0, 0), "native"))
# plotly doesn't currently like mucking around with grobs - leave it commented out for now.
# if (x.axis.offset.labels == TRUE){
# # Adjust alternate tick lengths to be longer
# gg <- ggplotGrob(g)
# xaxis <- gg$grobs[[which(gg$layout$name == "axis-b")]]
# # Get the tick marks and tick mark labels
# ticks <- xaxis$children[[2]]
# # Get the tick marks
# marks = ticks$grobs[[1]]
# # change the length in an alternating way
# long <- unit.c(unit(1, "npc") - unit(18, "pt"), unit(1, "npc"))
# short <- unit.c(unit(1, "npc") - unit(4, "pt"), unit(1, "npc"))
# # update the length
# marks$y = unit.c(rep(unit.c(short, long), 4), short)
# # Put the tick marks back into the plot
# ticks$grobs[[1]] <- marks
# xaxis$children[[2]] <- ticks
# gg$grobs[[which(gg$layout$name == "axis-b")]] <- xaxis
# g <- as.ggplot(gg)
# }
} else {
g <- ggplot() + theme(axis.line = element_blank()) + labs(title = "Invalid data - cannot produce plot")
}
p <- ggplotly(g);
####################################################
############# Create and save widget ###############
internalSaveWidget(p, 'out.html');
####################################################Daniel :)
- dm-p7 years agoSuper User
Hi Anonymous,
I've set up a visual project with your files and sample data, and everything seems to be running okay.
The challenge now is having unpacked how the R visuals work vs. TypeScript, the approach used for these visuals doesn't seem to work in the same way as for R (to my knowledge).
Usually, you have full access to the dataRoles in the dataView when the visual renders, and you use this information to build your list for the properties pane, using enumerateObjectInstances to push them in. When using R, the whole result of the execution is provided as the scriptResult, e.g.:
The above dataView is from the developer visual for the R project. For TypeScript visuals, you'd get data in the metadata object and whichever dataViewMapping you were using, e.g. table.
I've decoded the payloadBase64 and this is essentially the JavaScript and HTML to render the visual, post-processing with R. What this looks like is we can add simple properties to the pane but any data-bound ones come with a separate challenge; there doesn't seem to be suitable hook in a similar place to the TypeScript workflow and I was hoping that there might be, so I apologise if I got your hopes up.
I'll yield the floor to someone else, but it might be better to get in touch with the custom visuals team directly to see if they can definitively comfirm your question - you can email them at [email protected]
I'd be keen to hear if anyone else can solve this, or if the team have any advice on how it can be managed in the R visuals.
Regards,
Daniel
- Anonymous7 years agoNot applicable
Hi again Daniel,
Thanks very much for having a look at it - I appreciate the time you spent trying to get it working. It looks like I'm not going mad after all and can't set colours the way I wanted to be able to (unless the visuals team has some insight on how to do it). I may just end up doing something simple like passing in a colour theme based on some enumerated values (which is a bit too much like hard-coding for my taste).
I'll post any information I can gather here in case someone else runs into the same issue.
Thanks again.
Frank
- Anonymous7 years agoNot applicable
There doesn't appear to be a way to pass dynamic/data driven properties through to R according to the PBI visuals team. Their recommendation was to implement the visual in TypeScript.