How to save an html page that was created with `formattable` and has conditional coloring? - html

I used formattable package in R to create a conditionally colored table rendered in the viewer as an html page (This is the link the the forum response that helped me to do this (thanks for the responder!): How to conditionally color table elements using formattable and color_tile?). I want to save it exactly like if I click export and save as a web page within the viewer.
I am trying to save the html page seen in the Viewer window with the conditional coloring. I've tried the two solutions here but am not able to save with their colors still present:
Save View() output of RStudio as html
Save html result to a txt or html file
TLDR: I want to save the output from the code below (Credit to Ruth Gg) with the colors still present basically via code not with the click and point button within viewer.
Here is the code to make the table I am trying to save from the previous StackOverflow forum:
library(formattable)
# Set custom colors
green <- "#71CA97"
red <- "#ff7f7f"
# Make example table
col1 <- c(1.2, 4.2, 5.6, 7.1)
col2 <- c(5.0, 1.3, 10.3, 6.0)
col3 <- c(4.7, 6.3, 1.5, 6.3)
mydata <- data.frame(col1, col2, col3)
# Define color_tile_mean function
color_tile_mean <- function (...) {
formatter("span", style = function(x) {
style(display = "block",
padding = "0 4px",
`border-radius` = "4px",
`background-color` = ifelse(x < mean(x) , red, green)) # Remember to change the colors!
})}
# Use it just like color_tile but without colors
formattable(mydata, align=c("c", "c", "c"),list(
col1=color_tile_mean(),
col2=color_tile_mean(),
col3=color_tile_mean()
)
)
Here is one of the packages I tried to use:
library(formattable)
# Set custom colors
green <- "#71CA97"
red <- "#ff7f7f"
# Make example table
col1 <- c(1.2, 4.2, 5.6, 7.1)
col2 <- c(5.0, 1.3, 10.3, 6.0)
col3 <- c(4.7, 6.3, 1.5, 6.3)
mydata <- data.frame(col1, col2, col3)
# Define color_tile_mean function
color_tile_mean <- function (...) {
formatter("span", style = function(x) {
style(display = "block",
padding = "0 4px",
`border-radius` = "4px",
`background-color` = ifelse(x < mean(x) , red, green)) # Remember to change the colors!
})}
bboop <- formattable(mydata, align=c("c", "c", "c"),list(
col1=color_tile_mean(),
col2=color_tile_mean(),
col3=color_tile_mean()
))
htmlwidgets::saveWidget(widget = bboop, file = f)
#f is string of filepath
Error when trying to use htmlwidgets::saveWidget:
Error in .getNamespace(pkg) :
invalid type/length (symbol/0) in vector allocation
Does anyone have any advice? Sorry this is long. I wanted to be thorough.

Related

R - combine image and table then export as PDF

I have four goals:
Connect to a Postgresql database and pull some data
Gloss up a table with some colour and formatting
Include an image (company logo) above it
Export as PDF
1 and 2 are easy enough and 4 seems possible even if not convenient, but I don't think R was designed to add and position images. I've attached some sample code of how I envision creating the table, and then a mockup of what I think the final version might look like. Can anyone advise on the best way to accomplish this?
Sample data:
data(mtcars)
df <- head(mtcars)
HTML approach: flexible and portable to other apps
library(tableHTML)
html_table <- df %>%
tableHTML(rownames = FALSE, border = 0) %>%
add_css_row(css = list(c('font-family', 'text-align'), c('sans-serif', 'center'))) %>%
add_css_header(css = list(c('background-color', 'color'), c('#173ACC', 'white')), headers = 1:ncol(df))
Grob approach: Creating a ggplot-like image. I've seen recommendations to use grid.arrange to place an image on top and export as a PDF
library(ggpubr)
tbody.style = tbody_style(color = "black",
fill = "white", hjust=1, x=0.9)
grob_table <- ggtexttable(df, rows = NULL,
theme = ttheme(
colnames.style = colnames_style(color = "white", fill = "#173ACC"),
tbody.style = tbody.style
)
)
grid.arrange(table_image)
You are almost there. You just need to import your image (could be png, jpeg or svg) then pass it to grid::rasterGrob. Use the options in rasterGrob to adjust size etc. Then pass your grob table to gridExtra::grid.arrange
logo_imported <- png::readPNG(system.file("img", "Rlogo.png", package="png"), TRUE)
lg <- grid::rasterGrob(logo_imported)
gridExtra::grid.arrange(lg, grob_table)
You can then either render this to pdf by adding it to an rmarkdown report (probably best), or you can save directly to pdf via
gridExtra::grid.arrange(lg, grob_table)
pdf(file = "My Plot.pdf",
width = 4, # The width of the plot in inches
height = 4)

Extend width of column with renderDataTable in Shiny

I having trouble understanding the behavior of renderDataTable function using Shiny.
I am trying to extend the width of one specific column.
When I am not using Shiny, and just trying to visualize the output of the table, I write the below and I get the expected output in the plot (Amazon Title column is extended):
Category <- c("Tools & Home Improvement", "Tools & Home Improvement")
AmazonTitle <- c("0.15,Klein Tools NCVT-2 Non Contact Voltage Tester- Dual Range Pen Voltage Detector for Standard and Low Voltage with 3 m Drop Protection", " ABCDFGEGEEFE")
ASIN_url <- c("<a href='https://www.amazon.com/dp/B004FXJOQO'>https://www.amazon.com/dp/B004FXJOQO</a>", "<a href='https://www.amazon.com/dp/B004FXJOQO'>https://www.amazon.com/dp/B0043XJOQO</a>")
ASIN <- c("B004FXJOQO", "B0043XJOQO")
All_ASIN_Information <- data.frame(Category, AmazonTitle, ASIN_url, ASIN)
DT::datatable(All_ASIN_Information, escape=FALSE,
options = list(
pageLength = 20, autoWidth = TRUE,
columnDefs = list(list( targets = 2, width = '600px'))
)
)
But when I use this exact block inside a DT::renderDataTable function for Shiny, the result is different and the column width is not extended....
See behavior for Shiny with below code:
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
DT::dataTableOutput("Table_ASIN")))
server <- function(input, output){
output$Table_ASIN <- DT::renderDataTable(
DT::datatable(All_ASIN_Information, escape=FALSE,
options = list(
pageLength = 20, autoWidth = TRUE,
columnDefs = list(list( targets = 2, width = '600px'))
)))
}
shinyApp(ui, server)
I don't know if this behavior is caused by the hyperlinks created in column 'ASIN_url' but I would really need them anyway.
Any help much appreciated on this !
One option would be to shorten the link like this:
ASIN_url <- c("<a href='https://www.amazon.com/dp/B004FXJOQO'>Link</a>", "<a href='https://www.amazon.com/dp/B004FXJOQO'>Link</a>")
Another would be to add a scroll bar by including scrollX = TRUE in the option list

How to extract input from a dynamic matrix in R shiny

This is related to an old question about creating a matrix-style input in a shiny app with dynamic dimensions. My goal is to have a matrix of numerical inputs (the dimensions of which are determined by other user inputs), and then pass that matrix to other R commands and print some output from those calculations. I have code that successfully executes everything except that I can only access the user inputs as characters.
Here is an example that sets up the input and just prints a couple cells from the matrix (this works fine, but isn't what I need):
shiny::runApp(list(
ui = pageWithSidebar(
headerPanel("test"),
sidebarPanel(
numericInput(inputId = "nrow",
label = "number of rows",
min = 1,
max = 20,
value = 1),
numericInput(inputId = "ncol",
label = "number of columns",
min = 1,
max = 20,
value = 1)
),
mainPanel(
tableOutput("value"),
uiOutput("textoutput"))
),
server = function(input,output){
isolate({
output$value <-renderTable({
num.inputs.col1 <- paste0("<input id='r", 1:input$nrow, "c", 1, "' class='shiny-bound-input' type='number' value='1'>")
df <- data.frame(num.inputs.col1)
if (input$ncol >= 2){
for (i in 2:input$ncol){
num.inputs.coli <- paste0("<input id='r", 1:input$nrow, "c", i, "' class='shiny-bound-input' type='number' value='1'>")
df <- cbind(df,num.inputs.coli)
}
}
colnames(df) <- paste0("time ",as.numeric(1:input$ncol))
df
}, sanitize.text.function = function(x) x)
})
output$textoutput <- renderUI(paste0("Cells [1,1] and [2,2]: ",input$r1c1,",",input$r2c2))
}
))
However, when I try to do any operation on the inputs in the matrix, such as output$textoutput <- renderUI(as.numeric(paste0(input$r1c1))+as.numeric(paste0(input$r2c2))), I get classic R errors like $ operator is invalid for atomic vectors. I have tried many combinations of 'as.numeric', 'as.character', ect. to try to get it into the correct format. When I check the structure of those input cells, I see that they have an extra 'NULL' attribute that I can't seem to get rid of, but I am unsure if that is the root of the problem.
In short, how do I extract the plain numbers from that matrix? Or is there a better way to do this in shiny? The only other solution I'm aware of is the rhandsontable package, which I would prefer not to use if there is a reasonable alternative.
Any suggestions would be very appreciated. Thank you!
Edit/solution: replacing renderUI and uiOutput with renderPrint and verbatimTextOutput solves the problem. Thank you for the comment, blondeclover!

Adding flextable including images in PowerPoint

I'm trying to add a flextable object including an image to PowerPoint using officer. But the image disappears. I can reproduce the problem using the example David has posted hear
library(flextable)
library(tibble)
download.file("https://www.r-project.org/logo/Rlogo.png", destfile = "Rlogo.png")
pupil.tbl <- tribble(
~col1, ~col2, ~col3,
"A", "B", "Rlogo.png",
"C", "D", "Rlogo.png"
)
pupil.tbl <- as.data.frame(pupil.tbl)
# display only col1 and col2
pupil.ft <- flextable(pupil.tbl, col_keys = c("col1", "col2") )
add_img_to_flextable <- function(ft, i, j){
display(
ft, i=i, col_key = j, pattern = "{{att_tbl}}",
formatters = list(# use col3 even if not displayed
att_tbl ~ as_image(col3, src = col3, width = 1.29, height = 1)
)
)
}
pupil.ft <- add_img_to_flextable(pupil.ft, i = 2, j = "col2")
If I run:
print(pupil.ft,preview='html')
print(pupil.ft,preview='docx')
everything works fine, but in PPTX format the image isn't shown anymore
print(pupil.ft,preview='pptx')
Is this a known limitation of PowerPoint? Is there any workaround how to show the images inside a table?
Thanks,
Philipp
That is a known limitation of PowerPoint, it can not handle images in cells

Removing multiple elements with removeUI / wrapping multiple elements with tags$div() assigning an id for each variable

I was suggested using insertUI here and found that it is a great feature. The following code allows to generate control widgets for a single or multiple elements using insertUI, but struck on incorporating removeUI related part. Tried jQuery options to remove inserted UI elements but did not work out. I found the following from Shiny dynamic UI, i.e., Note that, if you are inserting multiple elements in one call, you must wrap them in either a tagList() or a tags$div() (the latter option has the advantage that you can give it an id to make it easier to reference or remove it later on). Also, comments here gave some clues, i.e., tags$div(id="sepal.width.div", sliderInput("sepal.width.slider", ...)), but my lack of HTML/CSS knowledge stops me going forward. I'm looking at (a) wrapping multiple widget element(s) with tags$div() assigning a unique id for each variable, which will be used in removeUI; (b) calling multiple elements via removeUI.
varnames <- names(iris[,1:4]) # names
varinit <- apply(iris[,1:4],2,median) # initival value used in slider
varmin <- apply(iris[,1:4],2,min) # min.
varmax <- apply(iris[,1:4],2,max) # max.
ListofSelVars <<- vector(mode="character")
# control widgets for all elements
allControls <- lapply(setNames(varnames, varnames), function(x) {
sliderInput(x, x, varmin[x], varmax[x], c(varmin[x], varinit[x]),
round = -2)
})
ui <- navbarPage(
tabPanel("Plot",
sidebarLayout(
sidebarPanel(
checkboxGroupInput("ConditioningVariables", "Conditioning variables (choose one or more):",
varnames,inline = TRUE),
# add an action button
actionButton("add", "Update UI elements")
),
mainPanel()
)
)
)
server <- function(input, output, session) {
observeEvent(input$add, {
insertUI(
selector ='#add',
where = "afterEnd",
ui = allControls[setdiff(input$ConditioningVariables,ListofSelVars)]
)
## removeUI related goes, here
## removeUI(selector=paste0())
## setdiff(ListofSelVars,input$ConditioningVariables) gives elements to be removed
## Global variable, keep track of elements that are selected
ListofSelVars <<- input$ConditioningVariables
})
}
shinyApp(ui, server)
Here is the working code. The main issue is with the names here, i.e. Sepal.Width. I wrapped each slider with a div with id like div.Sepal.Width so that it is easier to remove. removeUI requires a jQuery selector, so it appears that something like #div.Sepal.Width would work, except that it does not, because . is itself a jQuery selector that means class, so we need to double escape the .. Of course you can also remove the . when you first create the divs, thus avoiding the trouble...
varnames <- names(iris[,1:4]) # names
varinit <- apply(iris[,1:4],2,median) # initival value used in slider
varmin <- apply(iris[,1:4],2,min) # min.
varmax <- apply(iris[,1:4],2,max) # max.
ListofSelVars <<- vector(mode="character")
# control widgets for all elements
allControls <- lapply(setNames(varnames, varnames), function(x) {
tags$div(id=paste0("div.",x), sliderInput(x, x, varmin[x], varmax[x], c(varmin[x], varinit[x]),
round = -2))
})
ui <- fluidPage(
titlePanel("Dynamic sliders"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("ConditioningVariables", "Conditioning variables (choose one or more):",
varnames,inline = TRUE),
# add an action button
actionButton("add", "Update UI elements")
),
mainPanel(
uiOutput("plot_out")
)
)
)
server <- function(input, output, session) {
observeEvent(input$add, {
insertUI(
selector ='#add',
where = "afterEnd",
ui = allControls[setdiff(input$ConditioningVariables,ListofSelVars)]
)
ListofRemoval <- setdiff(ListofSelVars,input$ConditioningVariables)
for (item in ListofRemoval) {
item = gsub(".", "\\.", item, fixed=TRUE)
item = paste0("#div\\.", item)
removeUI(item)
}
ListofSelVars <<- input$ConditioningVariables
})
}
shinyApp(ui, server)