4

I would like to combine the richness of html widgets (mainly plotly and networkD3), with the possibility of arranging them as plots in a grid in R, to export them as pdf graphic for a publication. However, if I create some html widget objects in R and try to arrange them with gridExtra::grid.arrange() (see minimal example below) I get the following error:

Error in gList(list(x = list(links = list(source = c(0, 0), target = c(1,  : 
only 'grobs' allowed in "gList"

I searched for “converting html widgets to grobs or plots” to be able to arrange them in a grid, but could not find any relevant information. I could go the long manual way of first saving the html widget as a website with htmlwidgets::saveWidget(), then converting it to a pdf with webshot::webshot() and arranging the graphics manually with a vector graphic editor (such as Inkscape). But despite the manual effort, all the conversion steps might results in a decrease in quality.

Maybe I could somehow arrange the graphics in LaTeX R-Markdown document with tables or columns and specify the paper dimension to fit my plot. But somehow that feels neither like an ideal solution, since I might want to arrange my graphs dynamically in several columns and rows, add titles, ect., which would make the arrangement quite complex in LaTeX/knitr.

So before I embark on the journey of knitting everything manually together, I would like to ask whether you know a better way to arrange some html widgets plots and export them to pdf within R?


Minimal example

Here is a little minimal example to reproduce my problem. Let’s say my aim is a graph which compares the energy flow of two different processes with each other (Sankey diagrams with networkD3), by arranging them next to each other in a grid, like so: Exemplary arranged Sankey plots

This would be my code:

library(networkD3)
library(grid)
library(gridExtra)

## Create simplified Data
dfSankey <- list()
dfSankey$nodes <- data.frame(Name = c("Final Energy","Conversion Losses","Useful Energy"))
#1st process
dfSankey$links1 <- data.frame(Source = c(0,0),Target=c(1,2),Value=c(30,70))
#2nd process
dfSankey$links2 <- data.frame(Source = c(0,0),Target=c(1,2),Value=c(10,60))

## Draw Sankeys
plSankey1 <- sankeyNetwork(Links = dfSankey$links1, Nodes = dfSankey$nodes, Source = 'Source',
                          Target = 'Target', Value = 'Value', NodeID = 'Name')
plSankey2 <- sankeyNetwork(Links = dfSankey$links2, Nodes = dfSankey$nodes, Source = 'Source',
                           Target = 'Target', Value = 'Value', NodeID = 'Name')

#Arrange them next to each other
grid.arrange(plSankey1,plSankey2)

This results in the error message mentioned in the beginning.


Windows 7, R-Studio 0.99.903, R version 3.2.3, grid 3.2.3, gridExtra 2.2.1, networkD3 0.2.13

Nino
  • 366
  • 2
  • 12
  • http://www.rmarkdown.rstudio.com/flexdashboard/ You're welcome – Amit Kohli Oct 17 '16 at 14:40
  • 1
    [http://rmarkdown.rstudio.com/flexdashboard/](http://rmarkdown.rstudio.com/flexdashboard/) Perfect, but without www. before ("Page not found") – Nino Oct 17 '16 at 14:51
  • And then to export to PDF I have to use *webshot::webshot()* I guess? – Nino Oct 17 '16 at 14:57
  • `rmarkdown::render("mydoc.Rmd", output_format = "pdf_document")` – Amit Kohli Oct 17 '16 at 16:42
  • Thanks again @AmitKohli Flexdashboard works like a charm for html output. But if I render the document as a PDF, it does not take into account any columns, but rather prints each column on an extra page. And unfortunately both webshot as well as priting a PDF directly from chrome results in a largely empty file ( see [this thread](https://github.com/rstudio/flexdashboard/issues/52) ) – Nino Oct 18 '16 at 14:17
  • it seems jjallaire's last comment makes it possible? – Amit Kohli Oct 18 '16 at 19:53
  • Exporting a pdf containing the two figures is no problem. But I would like them to be next to each other - such as in the example graphic in my post above. The PDF export just prints them each on one page like in any standard document, without respecting the column structure of the dashboard. – Nino Oct 19 '16 at 08:36

1 Answers1

2

After the inspiration of Amit Kohli I started experimenting with flexdashboard, all kinds of Rmarkdown formats (html, pdf, isolides, ...), Rpresentations - but all of them had some drawbacks compared to my target.

In my opinion the best visual results can be achieved with a shiny app. Then saving the resulting website as pdf with chrome and finally croping the pdf with briss.

library(networkD3)

## Create simplified Data
dfSankey <- list()
dfSankey$nodes <- data.frame(Name = c("Final Energy","Conversion Losses","Useful Energy"))
#1st process
dfSankey$links1 <- data.frame(Source = c(0,0),Target=c(1,2),Value=c(30,70))
#2nd process
dfSankey$links2 <- data.frame(Source = c(0,0),Target=c(1,2),Value=c(10,60))

# Define UI for application that draws two Sankeys in columns
ui <- shinyUI(fluidPage(column(6,
                               h2("Process 1", align = "center"),
                               uiOutput("plot1.ui")
                               ),
                        column(6,
                               h2("Process 2", align = "center"),
                               uiOutput("plot2.ui")
                               )
))

# Define server logic required to draw the sankeys
server <- shinyServer(function(input, output) {

    #Render plot area relative to flow height
    output$plot1.ui <- renderUI({
      sankeyNetworkOutput("Plot1", height = "400px")
    })
    output$plot2.ui <- renderUI({
      sankeyNetworkOutput("Plot2", height=paste0(400*(70/100),"px"))
    })

    #Render Sankeys
   output$Plot1 <- renderSankeyNetwork({
     sankeyNetwork(Links = dfSankey$links1, Nodes = dfSankey$nodes, Source = 'Source',
                   Target = 'Target', Value = 'Value', NodeID = 'Name', fontSize = 15, nodeWidth = 10)
   })

   output$Plot2 <- renderSankeyNetwork({
     sankeyNetwork(Links = dfSankey$links2, Nodes = dfSankey$nodes, Source = 'Source',
                   Target = 'Target', Value = 'Value', NodeID = 'Name', fontSize = 15, nodeWidth = 10)
   })
})

# Run the application 
shinyApp(ui = ui, server = server)

Still feels a bit like a overkill to write a shiny app and doing all this manual converting just for arranging two html widget figures in a pdf, but at least the resulting pdf picture looks very pleasing.

I hope this might help others too.

Nino
  • 366
  • 2
  • 12