Workaround for adding flextable including images to PowerPoint - r

I have seen that there is already a question about that known limitation for pptx.
But is there a workaround like converting it first into an image and import that to pptx or something similar?
I don‘t care that the table can‘t be edited afterwards, that would actually be an advantage.
Edit:
Here as wished a minimal example where the Graphs are not included in the pptx.
library(flextable)
library(data.table)
library(officer)
library(dplyr)
z <- as.data.table(ggplot2::diamonds)
z <- z[, list(
price = mean(price, na.rm = TRUE),
list_col = list(.SD$x)
), by = "cut"]
z
ft <- flextable(data = z) %>%
compose(j = "list_col", value = as_paragraph(
plot_chunk(value = list_col, type = "dens", col = "pink",
width = 1.5, height = .4, free_scale = TRUE)
)) %>%
colformat_double(big.mark = " ", suffix = " $") %>%
set_header_labels(list_col = "density") %>%
autofit()
ft
print(ft, preview = "pptx")

You can use the new grid output feature for that—the following code demo some of its features (see ?gen_grob for more informations):
library(flextable)
library(data.table)
library(officer)
library(dplyr)
z <- as.data.table(ggplot2::diamonds)
z <- z[, list(
price = mean(price, na.rm = TRUE),
list_col = list(.SD$x)
), by = "cut"]
z
ft <- flextable(data = z) %>%
mk_par(j = "list_col", value = as_paragraph(
plot_chunk(value = list_col, type = "dens", col = "pink",
width = 1.5, height = .4, free_scale = TRUE)
)) %>%
colformat_double(big.mark = " ", suffix = " $") %>%
set_header_labels(list_col = "density") %>%
autofit()
ft
# create powerpoint
ppt <- read_pptx() %>%
add_slide() %>%
ph_with(value = plot_instr(code = plot(ft, fit = FALSE, scaling = "fixed")),
location = ph_location_type()) %>%
add_slide() %>%
ph_with(value = plot_instr(code = plot(ft)),
location = ph_location_fullsize()) %>%
add_slide() %>%
ph_with(value = plot_instr(code = plot(ft, fit = "width", scaling = "min")),
location = ph_location_fullsize())
# save powerpoint
print(ppt, preview = "pptx", target = 'output.pptx')

You could save your table as an image using save_as_image from officer package. After that you can add the image using ph_with where you add a value using the external_img function to add your image. Here is a reproducible example:
library(flextable)
library(data.table)
library(officer)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:data.table':
#>
#> between, first, last
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
z <- as.data.table(ggplot2::diamonds)
z <- z[, list(
price = mean(price, na.rm = TRUE),
list_col = list(.SD$x)
), by = "cut"]
z
#> cut price list_col
#> 1: Ideal 3457.542 3.95,3.93,4.35,4.31,4.49,4.49,...
#> 2: Premium 4584.258 3.89,4.20,3.88,3.79,4.38,3.97,...
#> 3: Good 3928.864 4.05,4.34,4.25,4.23,4.23,4.26,...
#> 4: Very Good 3981.760 3.94,3.95,4.07,4.00,4.21,3.85,...
#> 5: Fair 4358.758 3.87,6.45,6.27,5.57,5.63,6.11,...
ft <- flextable(data = z) %>%
compose(j = "list_col", value = as_paragraph(
plot_chunk(value = list_col, type = "dens", col = "pink",
width = 1.5, height = .4, free_scale = TRUE)
)) %>%
colformat_double(big.mark = " ", suffix = " $") %>%
set_header_labels(list_col = "density") %>%
autofit()
# image
image <- save_as_image(ft, path = '~/Downloads/table.png')
# create powerpoint
ppt <- read_pptx() %>% add_slide()
slide <- ppt %>% ph_with(value = external_img(image), location = ph_location(left = 0, top = 0))
# save powerpoint
print(ppt, preview = "pptx", target = 'output.pptx')
Created on 2022-09-23 with reprex v2.0.2

flextable can naturally be placed into powerpoint pptx with Officer https://ardata-fr.github.io/officeverse/officer-for-powerpoint.html
a simple example
library(flextable)
library(officer)
myflex <- flextable(head(iris))
my_pres <- read_pptx()
my_pres <- add_slide(my_pres, layout = "Title and Content", master = "Office Theme")
my_pres <- ph_with(my_pres, value =myflex,
location = ph_location_type(type = "body"))
print(my_pres, target = "example.pptx")
I earlier incorrectly called out htmltools_value() as being relevant, but this would only be so if you wanted to put a flextable (myflex) into a shiny renderUI - which is something I did a lot recently...

Related

How to print gt_tbl tables side by side with knitr kable

I would like to create beautiful tables with gt package and put three of them side by side using knitr function kable. It does not work as expected. The gt_tbl object is a list and kable prints all elements separately. Is there a way to have nice side by side gt tables? The output format is bookdown / markdown html.
library(tidyverse)
library(gt)
cars <- head(mtcars) %>%
gt() %>%
tab_header(
title = "Mtcars"
)
flowers <- head(iris) %>%
gt() %>%
tab_header(
title = "Iris"
)
knitr::kable(list(cars, flowers))
I also tried this solution Combine multiple gt tables to single one plot which does not yield any good solution. Perhaps gtsave, ggdraw and draw_image can be modified. But I didn't find a good scaling parameter.
library(tidyverse)
library(gt)
crosssection <- data.frame(id = c(1:6),
year = rep(2016,6),
income = c(20,15,35,10,76,23))
repeatedcross <- data.frame(id = c(1:6),
year = c(rep(2015,3),rep(2016,3)),
income = c(20,15,35,10,76,23))
paneldata <- data.frame(id = c(rep(1,3), rep(2,3)),
year = rep(c(2014:2016),2),
income = c(20,15,35,10,76,23))
cs <- crosssection %>%
gt() %>%
tab_header(
title = "Cross-Section" # #subtitle = "Cross-Section"
)
rc <- repeatedcross %>%
gt() %>%
tab_header(
title = "Repeated Cross-Section"
)
pd <- paneldata %>%
gt() %>%
tab_header(
title = "Paneldata"
)
cs %>% gtsave("p11.png", path = "Data/")
rc %>% gtsave("p12.png", path = "Data/")
pd %>% gtsave("p13.png", path = "Data/")
library(cowplot)
p111 <- ggdraw() + draw_image("Data/p11.png", scale = 0.8)
p112 <- ggdraw() + draw_image("Data/p12.png", scale = 0.8)
p113 <- ggdraw() + draw_image("Data/p13.png", scale = 0.8)
plot_grid(p111, p112, p113)
Those tables would clearly fit sidy by side when space between them is optimally used.
After applying the suggestion fixing scale parameter and setting nrow=1 tables are still misaligned in the Viewer pane, as well as the markdown files.
p111 <- ggdraw() + draw_image("Data/p11.png", scale = 0.7)
p112 <- ggdraw() + draw_image("Data/p12.png", scale = 0.96)
p113 <- ggdraw() + draw_image("Data/p13.png", scale = 0.7)
plot_grid(p111, p112, p113, nrow = 1)
You were near the result, I have only finished it for you.
In most cases we should control the scale parameter manually.
Code:
library(gt)
library(tidyverse)
crosssection <- data.frame(id = c(1:6),
year = rep(2016,6),
income = c(20,15,35,10,76,23))
repeatedcross <- data.frame(id = c(1:6),
year = c(rep(2015,3),rep(2016,3)),
income = c(20,15,35,10,76,23))
paneldata <- data.frame(id = c(rep(1,3), rep(2,3)),
year = rep(c(2014:2016),2),
income = c(20,15,35,10,76,23))
cs <- crosssection %>%
gt() %>%
tab_header(
title = "Cross-Section" # #subtitle = "Cross-Section"
)
rc <- repeatedcross %>%
gt() %>%
tab_header(
title = "Repeated Cross-Section"
)
pd <- paneldata %>%
gt() %>%
tab_header(
title = "Paneldata"
)
cs %>% gtsave("marco1.png")
rc %>% gtsave("marco2.png")
pd %>% gtsave("marco3.png")
library(cowplot)
p111 <- ggdraw() + draw_image("marco1.png", scale = 0.7)
p112 <- ggdraw() + draw_image("marco2.png", scale = 0.96)
p113 <- ggdraw() + draw_image("marco3.png", scale = 0.7)
plot_grid(p111, p112, p113, nrow = 1)
Output:
In Rmarkdown looks nice with these settings:

gt summary rows - position label in existing column

I have created a gt table and I want to have a row at the bottom of the table with the sum of all the columns. I want to position this so that the row label "total" sits within an existing column (the column catchment in my example) rather than out to the side. How do I do this?
library(gt) # package for making tables
library(tidyverse)
library(webshot)
webshot::install_phantomjs()
Lake_name <- c("Okareka", "Okaro", "Okataina", "Rerewhakaaitu", "Rotokakahi", "Rotomahana", "Tarawera", "Tikitapu")
Lake_labels <- c("\u14ckareka", "\u14ckaro", "\u14ckataina", "Rerewhakaaitu", "Rotokakahi", "Rotomahana", "Tarawera", "Tikitapu")
#define catchment areas
LIDAR_areas <- c(19778484, 3679975, 62923350, 52941258, 19195848, 83698343, 145261086, 5728184) # m^2
White_SW_areas <- c(19963914.610, 3675087.968, 66900327.220, 54581284.030, 19207814.960, 83724917.460, 144895034.400, 5689356.743)
White_GW_areas <- c(12485786, 3675525, 70924376, 15180499, 13491567, 101632751, 159285183, 5604187)
Catchment_Areas <- as_tibble(cbind(Lake_labels, LIDAR_areas, White_SW_areas, White_GW_areas))
Catchment_Areas$LIDAR_areas <- as.numeric(Catchment_Areas$LIDAR_areas)
Catchment_Areas$White_SW_areas <- as.numeric(Catchment_Areas$White_SW_areas)
Catchment_Areas$White_GW_areas <- as.numeric(Catchment_Areas$White_GW_areas)
f <- function(x){(x/1000000)}
Catchment_Areas <- Catchment_Areas %>% mutate(across(c(LIDAR_areas, White_GW_areas, White_SW_areas), f))
Catchment_Areas_Table <-
Catchment_Areas %>%
gt() %>%
tab_header(title = md("**Catchment Areas (m<sup>2</sup> x 10<sup>6</sup>)**")) %>%
fmt_number(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), decimals = 2) %>%
cols_align(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), align = "right") %>%
cols_label(Lake_labels = "Catchment", LIDAR_areas = "Surface Water (LIDAR)", White_SW_areas = "Surface Water (White 2020)", White_GW_areas = "Groundwater (White 2020)") %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("bottom"),
color = "black",
weight = px(2)
)#,
#cell_fill(color = "grey")
),
locations = list(
cells_column_labels(
columns = gt::everything()
)
)
) %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("top"),
color = "black",
weight = px(2)
)#,
#cell_fill(color = "grey")
),
locations = list(
cells_title()
)
)
Catchment_Areas_Table %>% summary_rows(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), fns = list(Total = "sum"))
Option 1: Move your "Catchment"/Lake_labels column into `gt(rowname_col = "Lake_labels"), this moves them into the "stub" and aligns with the summary calculations.
Option 2: Pre-calculate the summary rows ahead of time. This means you can treat the summary row as another other cell value.
Reprex below (note that I converted your dataframe to a tribble so it's more compact to reprex, datapasta::tribble_paste() is amazing for this):
library(gt) # package for making tables
library(tidyverse)
library(webshot)
Catchment_Areas <- tibble::tribble(
~Lake_labels, ~LIDAR_areas, ~White_SW_areas, ~White_GW_areas,
"Ōkareka", 19.778484, 19.96391461, 12.485786,
"Ōkaro", 3.679975, 3.675087968, 3.675525,
"Ōkataina", 62.92335, 66.90032722, 70.924376,
"Rerewhakaaitu", 52.941258, 54.58128403, 15.180499,
"Rotokakahi", 19.195848, 19.20781496, 13.491567,
"Rotomahana", 83.698343, 83.72491746, 101.632751,
"Tarawera", 145.261086, 144.8950344, 159.285183,
"Tikitapu", 5.728184, 5.689356743, 5.604187
)
### Option 1
Catchment_Areas_Table <-
Catchment_Areas %>%
gt(rowname_col = "Lake_labels") %>%
tab_header(title = md("**Catchment Areas (m<sup>2</sup> x 10<sup>6</sup>)**")) %>%
fmt_number(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), decimals = 2) %>%
cols_align(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), align = "right") %>%
cols_label(Lake_labels = "Catchment", LIDAR_areas = "Surface Water (LIDAR)",
White_SW_areas = "Surface Water (White 2020)", White_GW_areas = "Groundwater (White 2020)") %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("bottom"),
color = "black",
weight = px(2)
) # ,
# cell_fill(color = "grey")
),
locations = list(
cells_column_labels(
columns = gt::everything()
)
)
) %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("top"),
color = "black",
weight = px(2)
)
),
locations = list(
cells_title(),
cells_stub(rows = 1)
)
) %>%
summary_rows(
columns = c(LIDAR_areas, White_GW_areas, White_SW_areas),
fns = list(Total = "sum")
)
#> Warning in if ((loc$groups %>% rlang::eval_tidy()) == "title") {: the condition
#> has length > 1 and only the first element will be used
gtsave(Catchment_Areas_Table, "rowname_tab.png")
### Option 2
# Create summary ahead of time, add to bottom of the existing df.
Catchment_Areas_Sum <- Catchment_Areas %>%
add_row(
Catchment_Areas %>%
summarise(across(LIDAR_areas:last_col(), sum)) %>%
mutate(Lake_labels = "Total")
)
Catchment_Areas_Table_Sum <-
Catchment_Areas_Sum %>%
gt() %>%
tab_header(title = md("**Catchment Areas (m<sup>2</sup> x 10<sup>6</sup>)**")) %>%
fmt_number(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), decimals = 2) %>%
cols_align(columns = c(LIDAR_areas, White_GW_areas, White_SW_areas), align = "right") %>%
cols_label(Lake_labels = "Catchment", LIDAR_areas = "Surface Water (LIDAR)",
White_SW_areas = "Surface Water (White 2020)", White_GW_areas = "Groundwater (White 2020)") %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("bottom"),
color = "black",
weight = px(2)
)
),
locations = list(
cells_column_labels(
columns = gt::everything()
)
)
) %>%
tab_style( # add black underline
style = list(
cell_borders(
sides = c("top"),
color = "black",
weight = px(2)
)
),
locations = list(
cells_title()
)
) %>%
tab_style(
style = cell_borders(
sides = c("top"), color = "black", weight = px(2)
),
locations = list(
cells_body(rows = Lake_labels == "Total")
)
)
#> Warning in if ((loc$groups %>% rlang::eval_tidy()) == "title") {: the condition
#> has length > 1 and only the first element will be used
gtsave(Catchment_Areas_Table_Sum, "pre_sum_tab.png")
Created on 2021-10-29 by the reprex package (v2.0.1)

Adding a sparkline to a flextable in R

I am trying to use sparkline with a flextable I created for a report. Unfortunately, I can't get the function tabwid() to work, to convert this into an HTML widget. It is cited as being part of flextable v0.4.0, but it isn't in v0.5.
I note in the documentation below that the tabwid() function is deprecated:
https://www.rdocumentation.org/packages/flextable/versions/0.4.0/topics/tabwid
Is there an equivalent function in v0.5? If not, is there another way to use the sparkline function within a sparktable?
I noticed a conversation regarding this with the developer of flextable in 2016, and the code described there includes the tabwid() function. I can't find an alternative and would be grateful for any guidance provided.
https://github.com/davidgohel/flextable/issues/1
The code cited in the link above is as follows:
#devtools::install_github("davidgohel/oxbase")
#devtools::install_github("davidgohel/flextable")
#devtools::install_github("htmlwidgets/sparkline")
library(dplyr) library(oxbase) library(flextable) library(sparkline)
mtcars %>% group_by(cyl) %>% summarise(
hp = spk_chr(
hp, type="box",
chartRangeMin=0, chartRangeMax=max(mtcars$hp)
),
mpg = spk_chr(
mpg, type="box",
chartRangeMin=0, chartRangeMax=max(mtcars$mpg)
) ) %>% flextable() %>% tabwid() %>% spk_add_deps()
If this isn't possible, has anyone else found a way to add a sparkline into a flextable (e.g., through another package?)?
Thanks in advance for any guidance you can provide!
With the dev version on github, you can do the following - these are not interactive graphics:
# remotes::install_github("davidgohel/flextable")
library(data.table)
library(magrittr)
library(flextable)
# data prep ----
z <- as.data.table(iris)
z <- z[, list(
Sepal.Length = mean(Sepal.Length, na.rm = TRUE),
z = list(.SD$Sepal.Length)
), by = "Species"]
# flextable ----
ft <- flextable(
data = z,
col_keys = c("Species", "Sepal.Length", "box", "density", "line")
) %>%
compose(j = "box", value = as_paragraph(
plot_chunk(
value = z, type = "box",
border = "white", col = "transparent",
width = 1.5, height = .4
)
)) %>%
compose(j = "line", value = as_paragraph(
plot_chunk(value = z, type = "line", col = "white",
width = 1.5, height = .4)
)) %>%
compose(j = "density", value = as_paragraph(
plot_chunk(value = z, type = "dens", col = "white",
width = 1.5, height = .4)
)) %>%
theme_vader() %>%
align(j = c("box", "density", "line"), align = "center", part = "all") %>%
autofit()
ft

adding images to flextable using Display() & as_image() in loop doesnot work

I have a zip file which has multiple images of 2 types.1 -FrequencyRose images 2- EnergyRose images. I have created a flextable and then replacing the even rows with images and odd rows with the image titles using for loop . The loop is correctly displaying the titles but its only printing the last read image of each type mulitple times instead of actually printing all the images as per loop count.
img.file <- unzip("D:\\Mast_Image Files.zip")
fr_files <- img.file[grepl(paste("FrequencyRose", collapse = "|"), img.file)]
er_files <- img.file[grepl(paste("EnergyRose", collapse = "|"), img.file)]
fr_files has 3 image file paths and same with er_files
click
num_masts = length(img.file)
c1 = rep("Freq_rose",num_masts)
c2 = rep("Energy_Rose",num_masts)
df = data.frame(c1,c2)
dfft = flextable(df)
sso=seq(1,num_masts,2)
sse=seq(2,num_masts,2)
for (g in 1:(num_masts/2)){
ff.img = fr_files[g]
ef.img = er_files[g]
dfft2 = dfft %>%
display(
i = sse[g], col_key = "c1", pattern = "{{img}}",
formatters = list( img ~ as_image(c1,
src = ff.img, width = 3, height = 3))) %>%
display(
i = sse[g], col_key = "c2", pattern = "{{img}}",
formatters = list( img ~ as_image(c2,
src = ef.img, width = 3, height = 3))) %>%
display(
i = sso[g], col_key = "c1", pattern = paste("Freq_Rose","mast",g)) %>%
display(
i = sso[g], col_key = "c2", pattern = paste("Energy Rose","mast",g))
}
this loop is able to produce titles correctly but the only the fr_files[3], er_files[3] is looping over all the even rows of corresponding columns. output is as :final results . could not find the issue.
I can't reproduce your example (I don't have the images).
library(ggplot2)
library(tidyverse)
library(flextable)
# a data example ----
zz <- iris %>%
group_by(Species) %>%
summarise( n = n(), avg = mean(Petal.Length),
img_path = paste0( unique(as.character(Species)), ".png"),
gg = list(ggplot(tibble(Sepal.Length, Petal.Width), aes(Sepal.Length, Petal.Width)) + geom_point() + theme_minimal())
)
zz
# create the png ----
walk2(zz$gg, zz$img_path, function(gg, path){
png(filename = path, width = 300, height = 300)
print(gg)
dev.off()
})
From there you have everything you need and the flextable commands could be:
# create the flextable -----
flextable(zz, col_keys = c("Species", "n", "avg", "plot")) %>%
# here, i is selecting only odd rows
compose(i = ~ seq_along(Species) %% 2 > 0, j = "plot", value = as_paragraph(as_image(img_path, width = 300/72, height = 300/72))) %>%
theme_box() %>%
autofit()

Plotly GGplot - plot reponsive

The code output is a plot that I would like it be responsive, to adjust according to window dimension.
Using just ggplot gives me the result desired but I want to use the interactive tooltip of plotly, but when I do the figure is not responsive.
Is there any fix that it could work ? The code is bellow. I really appreciate any help !
library(dplyr)
library(ggplot2)
library(lubridate)
library(plotly)
df <- data.frame(matrix(c("2017-09-04","2017-09-05","2017-09-06","2017-09-07","2017-09-08",103,104,105,106,107,17356,18022,17000,20100,15230),ncol = 3, nrow = 5))
colnames(df) <- c("DATE","ORDER_ID","SALES")
df$DATE <- as.Date(df$DATE, format = "%Y-%m-%d")
df$SALES <- as.numeric(as.character(df$SALES))
df$ORDER_ID <- as.numeric(as.character(df$ORDER_ID))
TOTALSALES <- df %>% select(ORDER_ID,DATE,SALES) %>% mutate(weekday = wday(DATE, label=TRUE)) %>% mutate(DATE=as.Date(DATE)) %>% filter(!wday(DATE) %in% c(1, 7) & !(DATE %in% as.Date(c('2017-01-02','2017-02-27','2017-02-28','2017-04-14'))) ) %>% group_by(day=floor_date(DATE,"day")) %>% summarise(sales=sum(SALES)) %>% data.frame()
TOTALSALES <- ggplot(TOTALSALES ,aes(x=day,y=sales,text=paste('Vendas (R$):', format(sales,digits=9, decimal.mark=",",nsmall=2,big.mark = "."),'<br>Data: ',format(day,"%d/%m/%Y"))))+ geom_point(colour = "black", size = 1)+stat_smooth() +labs(title='TOTAL SALES',x='dias',y='valor')+ scale_x_date(date_minor_breaks = "1 week")
m <- list(
l = 120,
r = 2,
b = 2,
t = 50,
pad = 4
)
TOTALSALES <- ggplotly(TOTALSALES,tooltip = c("text")) %>% config(displayModeBar = F) %>% layout(autosize = F, width = 1000, height = 500, margin = m,xaxis = list(
zeroline = F
),
yaxis = list(
hoverformat = '.2f'
))
TOTALSALES

Resources