R: saving multiple html widgets together - html

I am using the R programming language. I am interested in learning how to save several "html widgets" together. I have been able to manually create different types of html widgets:
#widget 1
library(htmlwidgets)
library(leaflet)
library(RColorBrewer)
# create map data
map_data <- data.frame(
"Lati" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), "Longi" = c(-79.3871, -79.3860, -79.3807, -79.3806, -79.3957),
"Job" = c("Economist", "Economist", "Teacher", "Teacher", "Lawyer"),
"First_Name" = c("John", "James", "Jack", "Jason", "Jim"),
"Last_Name" = c("Smith", "Charles", "Henry", "David", "Robert"),
"vehicle" = c("car", "van", "car", "none", "car")
)
kingdom <- c("Economist", "Lawyer", "Teacher")
my_palette <- brewer.pal(3, "Paired")
factpal <- colorFactor(my_palette, levels = kingdom)
groups <- unique(map_data$Job)
# finalize map
map <- leaflet(map_data) %>%
addTiles(group = "OpenStreetMap") %>%
addCircleMarkers(~Longi, ~Lati, popup = ~Job,
radius = 10, weight = 2, opacity = 1, color = ~factpal(Job),
fill = TRUE, fillOpacity = 1, group = ~Job
)
widget_1 = map %>%
addLayersControl(overlayGroups = groups, options = layersControlOptions(collapsed = FALSE)) %>%
addTiles() %>%
addMarkers(lng = ~Longi,
lat = ~Lati,
popup = ~paste("Job", Job, "<br>",
"First_Name:", First_Name, "<br>",
"Last_Name:", Last_Name, "<br>", "vehicle:", vehicle, "<br>"))
widget 2:
##### widget 2
library(plotly)
library(ggplot2)
p_plot <- data.frame(frequency = c(rnorm(31, 1), rnorm(31)),
is_consumed = factor(round(runif(62))))
p2 <- p_plot %>%
ggplot(aes(frequency, fill = is_consumed)) +
geom_density(alpha = 0.5)
widget_2 = ggplotly(p2)
widget 3:
#####widget_3
today <- Sys.Date()
tm <- seq(0, 600, by = 10)
x <- today - tm
y <- rnorm(length(x))
widget_3 <- plot_ly(x = ~x, y = ~y, mode = 'lines', text = paste(tm, "days from today"))
widget 4:
####widget_4
library(igraph)
library(dplyr)
library(visNetwork)
Data_I_Have <- data.frame(
"Node_A" = c("John", "John", "John", "Peter", "Peter", "Peter", "Tim", "Kevin", "Adam", "Adam", "Xavier"),
"Node_B" = c("Claude", "Peter", "Tim", "Tim", "Claude", "Henry", "Kevin", "Claude", "Tim", "Henry", "Claude")
)
graph_file <- data.frame(Data_I_Have$Node_A, Data_I_Have$Node_B)
colnames(graph_file) <- c("Data_I_Have$Node_A", "Data_I_Have$Node_B")
graph <- graph.data.frame(graph_file, directed=F)
graph <- simplify(graph)
nodes <- data.frame(id = V(graph)$name, title = V(graph)$name)
nodes <- nodes[order(nodes$id, decreasing = F),]
edges <- get.data.frame(graph, what="edges")[1:2]
widget_4 = visNetwork(nodes, edges) %>% visIgraphLayout(layout = "layout_with_fr") %>%
visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE)
From here, I found another stackoverflow post where a similar question was asked: Using R and plot.ly, how to save multiples htmlwidgets to my html?
In this post, it explains how to save several html widgets together - the person who answered the question wrote a function to do so:
library(htmltools)
save_tags <- function (tags, file, selfcontained = F, libdir = "./lib")
{
if (is.null(libdir)) {
libdir <- paste(tools::file_path_sans_ext(basename(file)),
"_files", sep = "")
}
htmltools::save_html(tags, file = file, libdir = libdir)
if (selfcontained) {
if (!htmlwidgets:::pandoc_available()) {
stop("Saving a widget with selfcontained = TRUE requires pandoc. For details see:\n",
"https://github.com/rstudio/rmarkdown/blob/master/PANDOC.md")
}
htmlwidgets:::pandoc_self_contained_html(file, file)
unlink(libdir, recursive = TRUE)
}
return(htmltools::tags$iframe(src= file, height = "400px", width = "100%", style="border:0;"))
}
I tried using this function to save the 4 widgets together:
save_tags(widget_1, widget_2, widget_3, widget_4)
But doing so, I got the following error:
Error in dirname(file) : a character vector argument expected
Is there a straightforward and simple way for saving multiple html widgets together?
Thanks
NOTE: I know that you can use the combineWidgets() function in R:
library(manipulateWidget)
combineWidgets(widget_1, widget_2, widget_3, widget_4)
However, I am working with a computer that has no internet access or USB ports. This computer has a pre-installed copy of R with limited libraries (it has all the libraries used throughout my question except "manipulateWidget"). I am looking for the simplest way to save multiple html widgets together (e.g. is this possible in base R)?
Thanks

If format doesn't matter too much, you can merge the widgets using tagList and save them directly:
htmltools::save_html(tagList(widget_1, widget_2, widget_3, widget_4), file = "C://Users//Me//Desktop//widgets.html")
(It goes without saying that you will need to edit the filepath!)
If you want to control the layout of the widgets, you can wrap each in a div, and then style those:
doc <- htmltools::tagList(
div(widget_1, style = "float:left;width:50%;"),
div(widget_2,style = "float:left;width:50%;"),
div(widget_3, style = "float:left;width:50%;"),
div(widget_4, style = "float:left;width:50%;")
)
htmltools::save_html(html = doc, file = "C://Users//Me//Desktop//widgets.html")

Related

KableExtra html table not collapsing rows

I have a simple Table that I want to visualize in an html format using kableExtra. This table has a few repeated cells in the first column and I would like to collapse these cells into one. Only problem is that the package isn't letting me do that. How can I solve this?
This is my data:
df <- data.frame( Vegitation = c("Tree", "Tree", "Tree" , "Fruit", "Fruit", "Water"),
Non_sense_var1 = c(17,14,1,20,21,0),
Non_sense_var2 = c(15,1,11,2,2.1,60),
Non_sense_var3 = c(4,6,14,2,7,7)
)
And this is the code for my table:
header_line <- c("Vegitation", "Value 1", "Value 2", "Value 3")
kbl(df, escape = F, align = 'lcccc')%>%
add_header_above( header_line, bold = T, line = F, font_size = 11) %>%
kable_styling(full_width = T, font_size = 10, html_font = 'arial') %>%
kable_classic() %>%
column_spec(1, width = "2.2cm", bold = TRUE ) %>%
column_spec(2, width = "2.2cm") %>%
column_spec(c(3:4), width = "2.2cm", color = '#FF7F0E') %>%
collapse_rows(1, valign = "top")
And when I try to run this code, this is what I get:
EDIT: Currently (the date being Sept. 27 2022), KableExtra has issues when collapsing rows in similar scenarios as to mentioned here. There is no official production fix in yet. You can try the fix via github update but what that did for me was mess up other formatting of my table. You can also try another package for your use case. As of now, those seem like the possible available options.
Given this issue seems to have been persistent with the kbl (https://github.com/haozhu233/kableExtra/issues/624), you may consider another package such as reactable, huxatable, or gt
a couple of examples:
df <- data.frame( Vegitation = c("Tree", "Tree", "Tree" , "Fruit", "Fruit", "Water"),
Non_sense_var1 = c(17,14,1,20,21,0),
Non_sense_var2 = c(15,1,11,2,2.1,60),
Non_sense_var3 = c(4,6,14,2,7,7)
)
header_line <- c("Vegitation", "Value 1", "Value 2", "Value 3")
library(reactable)
reactable(df,
columns = list(
Vegitation = colDef(
style = JS("function(rowInfo, column, state) {
const firstSorted = state.sorted[0]
// Merge cells if unsorted or sorting by school
if (!firstSorted || firstSorted.id === 'Vegitation') {
const prevRow = state.pageRows[rowInfo.viewIndex - 1]
if (prevRow && rowInfo.values['Vegitation'] === prevRow['Vegitation']) {
return { visibility: 'hidden' }
}
}
}"))))
library(gt)
df <- df %>%
group_by(Vegitation)
gt(df)

Creating label in leaflet map using htmltools produces tiny label

Creating a leaflet map. First step, specify the label. The code used on leaflet github puts
%>% lapply(htmltool::HTML)
after the sprintf() function. However, making it is creating the label as a type:"list" resulting in the error: "Error in sum(sapply(label, function(x) { : invalid 'type' (list) of argument"
So to try and get around this I just load the htmltools library and use the code
HTML(sprintf(...))
Doing this works and runs the map, however, the labels show up as small boxes with no information (see picture link below)
I can't tell if this is something to do with the code inside sprintf() or if this has to do with HTML().
The weird thing is that the %>% lapply method was working just fine, but something happened and now its giving the error mentioned above
Image with the small label shown as little white box
labels.dest2 <- sprintf("<div style = 'overflow-wrap: anywhere;'><strong>%s <br/>%s Destinations</div><br/>%s Euclidean Miles from LAX on average<br/>%s minutes between OD tweets </div><br/>%s Miles from LAX on average</div><br/>%s minutes from LAX on average</div>",
puma.spdf$NAME,
puma.spdf$Dest_pt_count,
puma.spdf$Avg_Euc_Dist_Mi,
puma.spdf$Avg_tweetTime,
puma.spdf$Avg_RtDist_Mi,
puma.spdf$Avg_RtTime_min) %>% lapply(htmltools::HTML)
leaflet() %>% addTiles() %>% etc...
FULL CODE HERE
## Map with OD data and travel stats ##
labels.dest2 <- HTML(sprintf("<div style = 'overflow-wrap: anywhere;'> <strong>%s <br/>%g Destinations</div><br/>%s Euclidean Miles from LAX on average<br/>%s minutes between OD tweets </div><br/>%s Miles from LAX on average</div><br/>%s minutes from LAX on average</div>",
puma.spdf$NAME,
puma.spdf$Dest_pt_count,
puma.spdf$Avg_Euc_Dist_Mi,
puma.spdf$Avg_tweetTime,
puma.spdf$Avg_RtDist_Mi,
puma.spdf$Avg_RtTime_min))
leaflet() %>% addTiles() %>%
setView(lng=-118.243683, lat=34.1, zoom = 9.35) %>%
addEasyButton(easyButton(
icon="fa-crosshairs", title = "Default View",
onClick=JS("function(btn, map) {var groupLayer = map.layerManager.getLayerGroup('Destinations (red)'); map.fitBounds(groupLayer.getBounds());}"))) %>%
addProviderTiles(providers$CartoDB.Positron,
group = "Grey") %>%
addProviderTiles(providers$OpenStreetMap.BlackAndWhite,
group = "OSM") %>%
# Add Polygons
# Destination data
addPolygons(data = puma.spdf,
group = "Destination Density",
fillColor = ~pal.dest(Dest_pt_count),
weight = 1,
opacity = 90,
color = "white",
dashArray = "3",
fillOpacity = 0.5,
highlight = highlightOptions(weight = 2,
color = "#666",
dashArray = "",
fillOpacity = 0.7,
bringToFront = TRUE,
sendToBack = TRUE),
label = labels.dest2,
labelOptions = labelOptions(style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto")) %>%
addLegend(values=puma.spdf$Dest_pt_count,
group = "Destination Density",
pal=pal.dest,
title="Destination Density (Dest per PUMA)",
position = "bottomright") %>%
# Add Points
addCircleMarkers(data = D.spdf,
radius = 2,
color = "red",
group = "Destinations (red)",
fillOpacity = 0.5) %>%
addCircleMarkers(data = O.spdf,
radius = 2,
color = "green",
group = "Origins (green)") %>%
# Add Layer Controls
addLayersControl(
baseGroups = c("OSM (default)", "Grey"),
overlayGroups = c("Destinations (red)", "Origins (green)","Destination Density"),
options = layersControlOptions(collapsed = FALSE)
)
The problem was that the first column puma.spdf$NAME was not part of the dataset and was throwing off the string.. check to make sure all the variables you want to show are actually part of the dataset.

Embedding flextable in outlook with rdcomclient

I am facing the following issue: I created a beautiful flextable from a dataframe in R, which I would like to send via email. I use htmltools_value to get the HTML code of the flextable. Now I am able to embed this as htmlbody in my email which works in a sense that I succesfully send the email. However, the email is losing all the colors and borders with rest of the formatting still as defined in the flextable. Anyone faced similar issues or has an idea what could be the problem?
require(flextable)
require(RDCOMClient)
header_col2 <- c("","","", "", "2nd header", "2nd header","More headers", "More headers", "More headers", "More headers")
dfTest <- mtcars[c(1:6),c(1:10)]
ft <- flextable(dfTest)
ft <- add_header_row(ft,values = header_col2,top = T,colwidths = c(rep(1,10))) ft <- merge_h(ft, part = "header")
ft <-bold(ft, bold=T, part="header")
ft <-theme_zebra(ft,odd_header = 'red', even_header = 'grey', odd_body = 'lightblue', even_body = "white")
ft <- color(ft, color = "white", part = "header")
ft <- bold(ft, bold = TRUE, part = "header")
ft <- fontsize(ft, size = 11, part = "header")
std_border = fp_border(color="white", width = 1.5)
big_border = fp_border(color="gray", width = 1)
ft <- border_outer(ft, part="all", border = big_border )
ft <- border_inner_v(ft, part="header", border = std_border )
body <- htmltools_value(ft)
# or body <- format(ft, type = "html")
OutApp <- COMCreate("Outlook.Application")
outMail = OutApp$CreateItem(0)
outMail[["To"]] = "test#test.com"
outMail[["subject"]] = "TEST"
outMail[["HTMLbody"]] = body
outMail$Send()

Failing to merge html files in R

So I have two html pages, html_1.html and html_2.html I I would like to stack them one on top of the other in R. How to do that?
example:
library(dygraphs)
m1 = dygraph(nhtemp, main = "New Haven Temperatures") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
m2 = dygraph(nhtemp, main = "New Haven Temperatures") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
library(htmltools)
save_html(m1, file = 'm1.html')
save_html(m2, file = 'm2.html')
##Now load and merge m1.html and m2.html
The simplest way is to use an R markdown document:
---
title: ""
output: html_document
---
```{r echo=FALSE, message=FALSE, warning=FALSE}
library(dygraphs)
dygraph(nhtemp, main = "New Haven Temperatures", elementId = "a") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
dygraph(nhtemp, main = "New Haven Temperatures", elementId = "b") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
```
That takes care of many complex things for you.
The heavyweight way is to build the page on your own without getting into the gnarly details of widget javascript dependencies:
library(dygraphs)
library(htmlwidgets)
library(htmltools)
w1 <- dygraph(nhtemp, main = "New Haven Temperatures", elementId = "a") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
w2 <- dygraph(nhtemp, main = "New Haven Temperatures", elementId = "b") %>%
dyRangeSelector(dateWindow = c("1920-01-01", "1960-01-01"))
saveWidget(w1, "w1.html")
saveWidget(w2, "w2.html")
w1_src <- sprintf("data:text/html;base64,%s", openssl::base64_encode(rawToChar(readBin("w1.html", "raw", file.size("w1.html")))))
w2_src <- sprintf("data:text/html;base64,%s", openssl::base64_encode(rawToChar(readBin("w2.html", "raw", file.size("w2.html")))))
tags$html(
tags$body(
tags$iframe(src=w1_src, seamless="", frameborder="0", allowtransparency="true", scrolling="no", style="width:100%;height:400px"),
tags$iframe(src=w2_src, seamless="", frameborder="0", allowtransparency="true", scrolling="no", style="width:100%;height:400px")
)
) %>%
save_html("bothwidgets.html")
You can't just save_html() a widget since they depend on components that get automagically incorporated for you. You need to use iframes in the second approach unless you want to deal with widget dependency de-duplication and proper component inclusion for a lighter weight document on your own.
In this case, the difference isn't too bad, but still substantial. The first output file is 1.3MB the second is 2MB.
Note that you'll likely need to size the iframes better than I did in a production environment.

htmlwidgets side by side in html?

Say I have two htmlwidgets
# Load energy projection data
# Load energy projection data
library(networkD3)
URL <- paste0(
"https://cdn.rawgit.com/christophergandrud/networkD3/",
"master/JSONdata/energy.json")
Energy <- jsonlite::fromJSON(URL)
# Plot
sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30)
and
library(leaflet)
data(quakes)
# Show first 20 rows from the `quakes` dataset
leaflet(data = quakes[1:20,]) %>% addTiles() %>%
addMarkers(~long, ~lat, popup = ~as.character(mag))
And I want to put them side by side in an html page. How can I do this? Could I use an iframe? Other?
There are lots of ways to answer this. Often sizing and positioning will vary based on who authored the htmlwidget, so you might need to experiment a little. The easiest way if you don't plan to use a CSS framework with grid helpers will be to wrap each htmlwidget in tags$div() and use CSS. You also might be interested in the very nice new flexbox-based dashboard package from RStudio http://github.com/rstudio/flexdashboard.
# Load energy projection data
# Load energy projection data
library(networkD3)
URL <- paste0(
"https://cdn.rawgit.com/christophergandrud/networkD3/",
"master/JSONdata/energy.json")
Energy <- jsonlite::fromJSON(URL)
# Plot
sn <- sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30,
width = "100%")
library(leaflet)
data(quakes)
# Show first 20 rows from the `quakes` dataset
leaf <- leaflet(data = quakes[1:20,]) %>% addTiles() %>%
addMarkers(~long, ~lat, popup = ~as.character(mag))
library(htmltools)
browsable(
tagList(list(
tags$div(
style = 'width:50%;display:block;float:left;',
sn
),
tags$div(
style = 'width:50%;display:block;float:left;',
leaf
)
))
)