Search code examples
htmlrplotlyr-markdown

How to setup plotly script in Rmarkdown to reduce HTML file


I'm trying to setup "partial_bundle" for barplots in my Rmarkdown to reduce the size of my html file.

The issue is that this function needs an internet connection to download the script. I need to make it run offline.

Can someone please help setting it up? Im looking to download the script manually and setup my Rmarkdown...

Thanks in advance.


Solution

  • It would be helpful if I knew more about what you're doing. Are you going to use RMD for the plots in general, or is this just to create a local dependency? You could change your library for Plotly by adding it. Or are you planning on storing the JS file in your local files? If you added it to your library, you would only do that once; whenever you need to bundle, it's already there—no work required (until you update Plotly).

    Without knowing exactly the steps you'll take down the road, I'll provide the essentials to accomplish this in a couple of different ways.

    Regardless of where you place the file, as long as the file is there, you will only need to create the dependency list and add it to the plot.

    First I've put it all together as if it's going into your library; after that, I break it down further, showing the differences between using the library and other locations in your directory.

    Altogether:

    library(plotly)
        
    data(hobbs, package="plotly") 
    
    # plotly with partial bundle
    plt <- plot_ly(type = "bar", data = hobbs, x = ~nms, y = ~r) %>% partial_bundle()
    plt # take a look
    
    # look at the dependencies
    plt$dependencies # 5th one is plotly_basic
    
    # take the temp location shown in dependencies and copy file to library
    # library location dependencies must be within pkg sub dir htmlwidgets
    getLib <- system.file(package = "plotly", "htmlwidgets/lib/plotlyjs")
    
    # use the info in dependency and library to copy the file
    file.copy(from = paste0(plt$dependencies[[5]]$src$file, # temp locale
                            plt$dependencies[[5]]$script),  
              to = paste0(getLib, plt$dependencies[[5]]$script)) # library
    
    # inspect what you expect
    list.files(getLib)
    
    # change the dependency in the plot
    
    # create dependency list for library
    pbund <- htmltools::htmlDependency(
      name = plt$dependencies[[5]]$name,
      version = plt$dependencies[[5]]$version,
      src = "htmlwidgets/lib/plotlyjs",                            
      script = plt$dependencies[[5]]$script,
      package = "plotly" )
    
    # replace dependency in plot
    plt$dependencies[[5]] <- pbund
    
    # inspect what you expect
    plt$dependencies[[5]]
    
    plt
    

    Broken down with direction:

    To start: getting the file you need

    You could definitely download the JS files, but there is an easier way. The easiest way to do this is to call partial_bundle and see what it's pointing to. However, you wouldn't do this in an RMD with your actual plots unless you changed the chunk options to eval=F, but then why include it? The collection of this js script may be better suited for the rarely ran (like when Plotly updates) R script (you would need it for other computers or if Plotly was updated).

    I created a plot, called partial_bundle, and took a look at the dependencies.

    library(plotly)
    
    data(hobbs, package="plotly") 
    head(hobbs)
    
    # partial plotly bundle
    plt <- plot_ly(type = "bar", data = hobbs, x = ~nms, y = ~r) %>% partial_bundle()
    plt # take a look; ugly, but it works
    
    invisible(lapply(1:length(plt$dependencies),
                     function(k) message(plt$dependencies[[k]]$name)))
    # typedarray
    # jquery
    # crosstalk
    # plotly-htmlwidgets-css
    # plotly-basic
    
    plt$dependencies[[5]]
    # List of 12
    #  $ name          : chr "plotly-basic"
    #  $ version       : chr "2.5.1"
    #  $ src           :List of 2
    #   ..$ href: chr "https://cdn.plot.ly"
    #   ..$ file: chr "/var/folders/zp/rgt0rwln7xq132_rnrmhfb400000gn/T//RtmpFtB0bB"
    #  $ meta          : NULL
    #  $ script        : chr "plotly-basic-2.5.1.min.js"
    #  $ stylesheet    : NULL
    #  $ head          : NULL
    #  $ attachment    : NULL
    #  $ all_files     : logi FALSE
    #  $ local         : logi TRUE
    #  $ minified      : logi TRUE
    #  $ partial_bundle: chr "auto"
    #  - attr(*, "class")= chr "html_dependency" 
    

    If you look at the last call, plt$dependencies[[5]], you've got some useful information.

    What this tells me is that there is a CDN (content data network on the web), but there is also a temp file (yours will look a little different).

    # cdn
    plt$dependencies[[5]]$src$href
    # $href
    # [1] "https://cdn.plot.ly"
    
    # tempfile path
    plt$dependencies[[5]]$src$file
    # $file
    # [1] "/var/folders/zp/rgt0rwln7xq132_rnrmhfb400000gn/T//RtmpFtB0bB"
    
    
    list.files(plt$dependencies[[5]]$src$file)
    

    You should see the file name "plotly-basic-2.5.1.min.js", when you list.files(). You've already downloaded the file to a temporary location just by calling the plot.

    You could leave the file where it is for now (not great long term); You could put it somewhere else in your local directory, or you could add it to your plotly library package.

    There is also a URL. You could download the file (if you can't get to your temp files or for any other reason you chose.

    This is how you would assemble the entire URL:

    link <- with(plt$dependencies[[5]], paste0(src$href, "/", script))
    

    If you wanted to download this file to a directory you started in the object tf, you can download this in R using:

    download.file(url = link, destfile = tf)
    

    Moving the file

    If you choose to copy the file to somewhere else in your directory or add it to your Plotly library, the move is essentially the same.

    Since I don't know your directory or where you'd place it, I will demonstrate moving it to the Plotly library.

    Library directory

    To get the directory of any package in your library, use system.file(). Most packages that use htmlwidgets have a YAML that governs the dependencies for that package. Plotly doesn't (makes this even easier).

    I chose to put the file with the other Plotly JS files in the subdirectory htmlwidgets/lib/plotlyjs. It must be within /plotly/htmlwidgets, but beyond that is up to you.

    getLib <- system.file(package = "plotly", "htmlwidgets/lib/plotlyjs")
    # [1] "/Library/Frameworks/R.framework/Versions/4.1/Resources/library/plotly/htmlwidgets/lib/plotlyjs" 
    
    Copying a file to a new directory

    Whether you want to move it to your library or another location, this will work.

    file.copy(from = paste0(plt$dependencies[[5]]$src$file,
                            plt$dependencies[[5]]$script),
              to = paste0(getLib, plt$dependencies[[5]]$script))
    
    # inspect what you expect
    list.files(getLib)
    

    Creating the dependency object

    Once you've got the file in the spot you want it, as long as the file is there, you only need to assemble the dependency list and add it to your plot. So if your RMD has the plots you're using it with, the only part that requires coding is creating the dependency list and adding it to the plot. If you launch the RMD from more than one computer, keep the code to access, copy, or move the JS file in a chunk set to eval=F or commented out unless needed.

    If you were to use the library versus not using the library, there is one crucial difference for the dependency list—all_files. If it is not in your library, this must be set to FALSE. If it is in your library, it won't matter for this project (true or false).

    Here's the dependency I had shown earlier with an arrow pointing out all_files.

    plt$dependencies[[5]]
    # List of 12
    #  $ name          : chr "plotly-basic"
    #  $ version       : chr "2.5.1"
    #  $ src           :List of 2
    #   ..$ href: chr "https://cdn.plot.ly"
    #   ..$ file: chr "/var/folders/zp/rgt0rwln7xq132_rnrmhfb400000gn/T//RtmpFtB0bB"
    #  $ meta          : NULL
    #  $ script        : chr "plotly-basic-2.5.1.min.js"
    #  $ stylesheet    : NULL
    #  $ head          : NULL
    #  $ attachment    : NULL
    #  $ all_files     : logi FALSE                   <- all_files is here!
    #  $ local         : logi TRUE
    #  $ minified      : logi TRUE
    #  $ partial_bundle: chr "auto"
    #  - attr(*, "class")= chr "html_dependency" 
    

    You won't want to overwrite this dependency, you want to replace it. (It will cause you problems if you try to piecemeal this.)

    Using the package htmltools, this is how you can create it. Your src is going to be the absolute path, with two exceptions.

    Exception 1: It's stored in the same folder as the RMD, if it's an RMD. Exception 2: It's stored in your plotly library; use path within the package, if you put it in your library.

    Library

    Here's a dependency for my library.

    pbund <- htmltools::htmlDependency(
      name = plt$dependencies[[5]]$name,
      version = plt$dependencies[[5]]$version,
      src = "htmlwidgets/lib/plotlyjs",                            
      script = plt$dependencies[[5]]$script,
      package = "plotly" )
    

    Which is exactly the same as:

    pbund <- htmltools::htmlDependency(
      name = "plotly-basic",
      version = "2.5.1",
      src = "htmlwidgets/lib/plotlyjs",
      script = "plotly-basic-2.5.1.min.js",
      package = "plotly")
    
    Another directory (not library)

    Here's the same dependency, but pointing to the temp file.

    pbund <- htmltools::htmlDependency(
      name = plt$dependencies[[5]]$name,
      version = plt$dependencies[[5]]$version,
      src = plt$dependencies[[5]]$src$file,                        
      script = plt$dependencies[[5]]$script,
      all_files = F)
    

    Which is exactly the same as:

    pbund <- htmltools::htmlDependency(
      name = "plotly-basic",
      version = "2.5.1",
      src = "/var/folders/zp/rgt0rwln7xq132_rnrmhfb400000gn/T//RtmpFtB0bB",
      script = "plotly-basic-2.5.1.min.js",
      all_files = F)
    

    Then replace the dependency.

    Now add it to your plot

    plt$dependencies[[5]] <- pbund
    # inspect what you expect
    plt$dependencies[[5]]