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()
Related
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)
I need to show data caption, computer name and period in the header of table.
I have also requirements: zebra theme, merging cells if needed. That's why I chose flextable.
Here is my code:
library(officer) # border settings library
library(flextable) # drawing tables library
library(dplyr)
Caption <- "<b><big>Computer01.domain.com</big></b><br>Network Interface<br>Gbit Total/sec<br><small>2021-05-14 18:04 to 2021-05-25 13:29</small>"
bold_border <- fp_border(color="gray", width =2)
std_border <- fp_border(color="gray")
stub <- "2021-05-14 01:40 to 2021-05-17 08:26"
table_data <- data.frame (
Instance = c("Intel[R] Ethernet 10G",
"Intel[R] Ethernet Converged Network Adapter _1",
"Intel[R] Ethernet Converged Network Adapter _2",
"Intel[R] Ethernet 10G",
"Intel[R] Gigabit"),
Max = c(2.45, 2.41, 2.29, 2.17, 0),
Avg = c(0.15, 0.15, 0.15, 0.17, 0)
)
table <- table_data %>% flextable() %>%
set_caption(caption = Caption , html_escape = F) %>%
bg(bg = "#579FAD", part = "header") %>%
color(color = "white", part = "header") %>%
theme_zebra(
odd_header = "#579FAD",
odd_body = "#E0EFF4",
even_header = "transparent",
even_body = "transparent"
) %>%
set_table_properties(width = 1, layout = "autofit") %>%
hline(part="all", border = std_border ) %>%
vline(part="all", border = std_border ) %>%
border_outer( border = bold_border, part = "all" ) %>%
fix_border_issues() %>%
set_header_labels(
values = list(Instance = InstanceName ) ) %>%
flextable::font (part = "all" , fontname = "Calibri")
save_as_docx( table, path = file.path("c:\\temp", "test01.docx") )
save_as_html (table, path = file.path("c:\\temp", "test01.html"))
Here is what I got in html which is okay for me:
But in docx format my header style is not applied:
How can I create header like I did for html that can be saved to both html and docx?
If I have to create separate tables - one for html, other for docx - it's not so good but acceptable options. That case my question how to create header I made in html but for docx format?
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")
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.
I have called the following and it makes an HTML file with networkD3 perfectly.
forceNetwork(Links=g2$links, Nodes=g2$nodes, Source='source', Target='target', NodeID='name', Group='group', opacityNoHover=TRUE, Nodesize='group',height = 1000, width = 1000, fontSize = 20,linkDistance=200,clickAction = mcs2, zoom=TRUE) %>% saveNetwork(file = "test-network.html")
However in the created HTML it sets the fill parameter for the browser and viewer to be false.
<script type="application/htmlwidget-sizing" data-for="htmlwidget-18644f4e905f10bd8c40">
{"viewer":{"width":1300,"height":1300,"padding":10,"fill":false},"browser":{"width":1300,"height":1300,"padding":10,"fill":false}}
</script>
The above is from the generated HTML file and is the final <script> tag that is generated from the saveNetwork call.
How can I change the fill and padding values from R before calling saveNetwork in R through perl? Below is the code that does not produce the desired effects.
$r->run(q`fn <- fn <- forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
Group = "group", opacity = 0.4, zoom = TRUE)`);
$r -> run(q`fn$sizingPolicy$viewer$fill <- TRUE`);
$r -> run(q`fn$sizingPolicy$browser$fill <- TRUE`);
$r -> run(q`saveNetwork(fn, file = "test-network.html", selfcontained=TRUE)`);
However this still outputs the fill parameter with "false".
library(networkD3)
data(MisLinks)
data(MisNodes)
fn <- forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
Group = "group", opacity = 0.4, zoom = TRUE)
fn$sizingPolicy$viewer$fill <- TRUE
fn$sizingPolicy$browser$fill <- TRUE
fn$sizingPolicy$viewer$padding <- 20
fn$sizingPolicy$browser$padding <- 20
saveNetwork(fn, "forceNetwork.html", selfcontained = TRUE)
A quick search reveals that the htmlwidgets package provides sizingPolicy which allows you to adjust the required parameters
Something like this
sizingPolicy(padding = 0, browser.fill = TRUE)