[英]Download multiple plotly plots to PDF Shiny
My Shiny App displays a plotly plot for whatever input the user selects. 我的闪亮应用程序显示用户选择的任何输入的情节图。 I want a download button that saves ALL the plots inside a PDF file on the user's system.
我想要一个下载按钮,将所有图表保存在用户系统的PDF文件中。 I'm using R markdown for knitting a PDF report and then donwloading it using downloadHandler in Shiny.
我正在使用R markdown来编写PDF报告,然后使用Shiny中的downloadHandler来下载它。 As of now, I can create each plot individually in my Shiny code and then pass them as a list of parameters to my r markdown file.
截至目前,我可以在我的Shiny代码中单独创建每个绘图,然后将它们作为参数列表传递给我的r markdown文件。 Since I have a large number of plots (>25) in my actual project, I want to do it in a loop.
由于我在实际项目中有大量的图(> 25),我想在循环中进行。 Here's a reprodcuible example of what I have so far:
这是我到目前为止所做的一个可重复的例子:
library(shiny)
dummy.df <- structure(list(
Tid = structure(
1:24, .Label = c("20180321-032-000001",
"20180321-032-000003", "20180321-032-000004", "20180321-032-000005",
"20180321-032-000006", "20180321-032-000007", "20180321-032-000008",
"20180321-032-000009", "20180321-032-000010", "20180321-032-000011",
"20180321-032-000012", "20180321-032-000013", "20180321-032-000014",
"20180321-032-000015", "20180321-032-000016", "20180321-032-000017",
"20180321-032-000018", "20180321-032-000020", "20180321-032-000021",
"20180321-032-000022", "20180321-032-000024", "20180321-032-000025",
"20180321-032-000026", "20180321-032-000027"), class = "factor"),
Measurand1 = c(4.1938661428, 4.2866076398, 4.2527368322,
4.1653403962, 4.27242291066667, 4.16539040846667, 4.34047710253333,
4.22442363773333, 4.19234076866667, 4.2468291332, 3.9844897884,
4.22141039866667, 4.20227445513333, 4.33310654473333, 4.1927596214,
4.15925140273333, 4.11148968806667, 4.08674611913333, 4.18821475666667,
4.2206477116, 3.48470470453333, 4.2483107466, 4.209376197,
4.04040350253333),
Measurand2 = c(240.457556634854, 248.218468503733,
251.064523520989, 255.454918894609, 250.780599536337, 258.342398843477,
252.343710644105, 249.881670507113, 254.937548700795, 257.252509533017,
258.10699153634, 252.191362744656, 246.944795528771, 247.527116069484,
261.060987461132, 257.770850218767, 259.844790397474, 243.046373553637,
247.026385356368, 254.288899315579, 233.51454714355, 250.556819253509,
255.8242909112, 254.938735944406),
Measurand3 = c(70.0613216684803,
70.5004961457819, 70.8382322052776, 69.9282599322167, 68.3045749634227,
71.5636835352475, 69.1173532716941, 71.3604764318073, 69.5045949393461,
71.2211656142532, 72.5716638087178, 69.2085312787522, 70.7872214372161,
70.7247180047809, 69.9466984209057, 71.8433220247599, 72.2055956743742,
71.0348320947071, 69.3848050049961, 69.9884660785462, 73.160638501285,
69.7524898841488, 71.1958302879424, 72.6060886025082)),
class = "data.frame", row.names = c(NA, 24L)
)
# Define UI for application
ui <- fluidPage(
titlePanel("Download Demo"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "variable",
label = "Plot Measurand",
choices = colnames(dummy.df)[2:11]
),
hr(),
downloadButton("downloadplot1", label = "Download plots")
),
mainPanel(
plotlyOutput("myplot1")
)
)
)
# Define server logic
server <- function(input, output) {
# Output graph
output$myplot1 <- renderPlotly({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~get(input$variable), type = 'scatter',
mode = 'markers') %>%
layout(title = 'Values',
xaxis = list(title = "Points", showgrid = TRUE, zeroline = FALSE),
yaxis = list(title = input$variable, showgrid = TRUE, zeroline = FALSE))
})
# Creating plots individually and passing them as a list of parameters to RMD
# Example for the first two measurands
test.plot1 <- reactive({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~Measurand1, type = 'scatter', mode = 'markers')
})
test.plot2 <- reactive({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~Measurand2, type = 'scatter', mode = 'markers')
})
output$downloadplot1 <- downloadHandler(
filename = "plots.pdf",
content = function(file){
tempReport <- file.path(tempdir(), "report1.Rmd")
file.copy("download_content.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = test.plot1(), k = test.plot2())
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
And my RMD file: 我的RMD档案:
---
title: "Report"
output: pdf_document
always_allow_html: yes
params:
n: NA
k: NA
---
```{r,echo=FALSE}
library(plotly)
tmpFile <- tempfile(fileext = ".png")
export(params$n, file = tmpFile)
export(params$k, file = tmpFile)
```
What I want to do is pass ALL the plots as a parameterized list to rmd, where each of the plot will be plotted in the knitted PDF document and then downloaded. 我想要做的是将所有图作为参数化列表传递给rmd,其中每个图将绘制在编织的PDF文档中,然后下载。
Something along the lines of: 有点像:
# IN server
# Generate plots in a loop
list.of.measurands <- c("Measurand1", "Measurand2") #....all my measurands
plots.gen <- lapply(list.of.measurands, function(msrnd){
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~msrnd, type = 'scatter', mode = 'markers')
})
Pass this list as the parameters to Rmd: 将此列表作为参数传递给Rmd:
# Inside downloadHandler
params <- list(n = plots.gen)
And plot all plots in a loop in the rmd file: 并在rmd文件的循环中绘制所有绘图:
---
title: "Report"
output: pdf_document
always_allow_html: yes
params:
n: NA
k: NA
---
```{r,echo=FALSE}
library(plotly)
tmpFile <- tempfile(fileext = ".png")
for (item in params$n){
export(item, file = tmpFile)
}
```
This creates a blank report. 这会创建一个空白报告。 What am I missing?
我错过了什么?
Update 更新
Following Gregor de Cillia's comment, I changed my plot_ly function to have y = dummy.df[[msrnd]]
. 根据Gregor de Cillia的评论,我将plot_ly函数更改为
y = dummy.df[[msrnd]]
。 I have also tried as_widget() but no success in getting plots in my report. 我也尝试了as_widget(),但在我的报告中获取情节却没有成功。
plots.gen <- lapply(list.of.measurands, function(msrnd){
as_widget(plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = dummy.df[[msrnd]],
type = 'scatter', mode = 'markers'))
})
The Problem 问题
Okay, so after spending a decent amount of time playing around with plotly
and knitr, I'm pretty sure that there's a problem with printing plotly
graphs in a loop while inside a knitr report. 好吧,所以在花了相当多的时间玩弄
plotly
和plotly
,我很确定在plotly
报告中打印plotly
图时会出现问题。 I will file an issue at the plotly repository, because there must be some kind of bug. 我将在plotly存储库中提交一个问题,因为必定存在某种错误。 Even when exporting the graph as .png, then importing it again and displaying it in the
knitr
report, only one graph at a time can be shown. 即使将图形导出为.png,然后再次导入并在
knitr
报告中显示它,也只能显示一次一个图形。 Weird. 奇怪的。
The Solution 解决方案
Anyhow, I found a solution without using knitr
to get a pdf of all graphs that are produced in your Shiny Application. 无论如何,我找到了一个解决方案,没有使用
knitr
来获取你的Shiny应用程序中生成的所有图形的pdf。 It relies on the staplr
package to combine PDF files, so you have to install that package and also install the pdftk toolkit. 它依赖于
staplr
包来组合PDF文件,因此您必须安装该包并安装pdftk工具包。
Afterwards, use the following code I wrote while adapting your Shiny App: 之后,使用我在调整您的Shiny App时编写的以下代码:
library(shiny)
library(plotly)
library(staplr)
dummy.df <- structure(list(
Tid = structure(
1:24, .Label = c("20180321-032-000001",
"20180321-032-000003", "20180321-032-000004", "20180321-032-000005",
"20180321-032-000006", "20180321-032-000007", "20180321-032-000008",
"20180321-032-000009", "20180321-032-000010", "20180321-032-000011",
"20180321-032-000012", "20180321-032-000013", "20180321-032-000014",
"20180321-032-000015", "20180321-032-000016", "20180321-032-000017",
"20180321-032-000018", "20180321-032-000020", "20180321-032-000021",
"20180321-032-000022", "20180321-032-000024", "20180321-032-000025",
"20180321-032-000026", "20180321-032-000027"), class = "factor"),
Measurand1 = c(4.1938661428, 4.2866076398, 4.2527368322,
4.1653403962, 4.27242291066667, 4.16539040846667, 4.34047710253333,
4.22442363773333, 4.19234076866667, 4.2468291332, 3.9844897884,
4.22141039866667, 4.20227445513333, 4.33310654473333, 4.1927596214,
4.15925140273333, 4.11148968806667, 4.08674611913333, 4.18821475666667,
4.2206477116, 3.48470470453333, 4.2483107466, 4.209376197,
4.04040350253333),
Measurand2 = c(240.457556634854, 248.218468503733,
251.064523520989, 255.454918894609, 250.780599536337, 258.342398843477,
252.343710644105, 249.881670507113, 254.937548700795, 257.252509533017,
258.10699153634, 252.191362744656, 246.944795528771, 247.527116069484,
261.060987461132, 257.770850218767, 259.844790397474, 243.046373553637,
247.026385356368, 254.288899315579, 233.51454714355, 250.556819253509,
255.8242909112, 254.938735944406),
Measurand3 = c(70.0613216684803,
70.5004961457819, 70.8382322052776, 69.9282599322167, 68.3045749634227,
71.5636835352475, 69.1173532716941, 71.3604764318073, 69.5045949393461,
71.2211656142532, 72.5716638087178, 69.2085312787522, 70.7872214372161,
70.7247180047809, 69.9466984209057, 71.8433220247599, 72.2055956743742,
71.0348320947071, 69.3848050049961, 69.9884660785462, 73.160638501285,
69.7524898841488, 71.1958302879424, 72.6060886025082)),
class = "data.frame", row.names = c(NA, 24L)
)
# Define UI for application
ui <- fluidPage(
titlePanel("Download Demo"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "variable",
label = "Plot Measurand",
choices = colnames(dummy.df)[2:11]
),
hr(),
downloadButton("downloadplot1", label = "Download plots")
),
mainPanel(
plotlyOutput("myplot1")
)
)
)
# Define server logic
server <- function(input, output) {
# Output graph
output$myplot1 <- renderPlotly({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~get(input$variable), type = 'scatter',
mode = 'markers') %>%
layout(title = 'Values',
xaxis = list(title = "Points", showgrid = TRUE, zeroline = FALSE),
yaxis = list(title = input$variable, showgrid = TRUE, zeroline = FALSE))
})
# Creating plots individually and passing them as a list of parameters to RMD
# Example for the first two measurands
test.plot1 <- reactive({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~Measurand1, type = 'scatter', mode = 'markers')
})
test.plot2 <- reactive({
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~Measurand2, type = 'scatter', mode = 'markers')
})
output$downloadplot1 <- downloadHandler(
filename = "plots.pdf",
content = function(file){
# Set up parameters to pass to Rmd document
plots <- list(test.plot1(), test.plot2())
# Plot indices
ind_vec <- seq_along(plots)
# Create tempfiles for all plots
tfiles <- sapply(ind_vec, FUN = function(x)
return(tempfile(fileext = ".pdf")))
# create tempfiles for the plots with the second page deleted
tfiles_repl <- sapply(ind_vec, FUN = function(x)
return(tempfile(fileext = ".pdf")))
# Save the objects as .pdf files
for (i in ind_vec) {
# Export files
export(plots[[i]], tfiles[[i]])
# Remove second page bc for some reason it is whitespace
staplr::remove_pages(2, input_filepath = tfiles[[i]],
output_filepath = tfiles_repl[[i]])
}
# Combine the plots into one pdf
staplr::staple_pdf(input_files = tfiles_repl, output_filepath = file)
# Remove .pdf files
lapply(tfiles, FUN = file.remove)
lapply(tfiles_repl, FUN = file.remove)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
I only adapted the code inside the downloadHandler()
function. 我只修改了
downloadHandler()
函数中的代码。 This code basically produces .pdf
files of all plots that are inside the plots
list (where you later have to specify all your 25 plots, I would do this in a loop). 此代码基本上生成了
plots
列表中所有绘图的.pdf
文件(之后您必须指定所有25个绘图,我会在循环中执行此操作)。 Then, it combines all plots into one .pdf
, before deleting the second page of each .pdf, which is necessary because for some reason export()
produces a PDF with the second page being completely blank. 然后,它将所有绘图组合成一个
.pdf
,然后删除每个.pdf的第二页,这是必要的,因为出于某种原因, export()
生成PDF,第二页完全空白。
My Suggestion 我的建议
If I were you, I would want to get rid of plotly
at all, and replace it with ggplot2
graphs. 如果我是你,我会想要
plotly
摆脱plotly
,并用ggplot2
图代替它。 It would be way easier to do exactly what you want (including the knitr
solution). 这样做会更容易做到你想要的(包括
knitr
解决方案)。 Graphs created with plotly
create an extra layer of complexity, because they are web objects that first have to be converted to static files. 使用
plotly
创建的图形会创建额外的复杂层,因为它们是首先必须转换为静态文件的Web对象。
I think @Stanislaus Stadlmann is on point. 我认为@Stanislaus Stadlmann非常关注。 For some reason,
plotly::export
does not work inside a loop of a rmarkdown file. 出于某种原因,
plotly::export
在rmarkdown文件的循环内不起作用。 I suspect it is for the same reason knitr::include_graphic
does not work inside a loop . 我怀疑是出于同样的原因,
knitr::include_graphic
在循环中不起作用 。 A workaround is to use the markdown syntax to insert your images. 解决方法是使用markdown语法插入图像。 Here is the rmarkdown file that works:
这是有效的rmarkdown文件:
---
title: "Report"
output: pdf_document
always_allow_html: yes
params:
n: NA
---
```{r,echo=FALSE,warning=FALSE, results="asis"}
library(plotly)
for (item in params$n) {
tmpFile <- tempfile(fileext = ".png")
export(item, file = tmpFile)
cat("![](",tmpFile,")\n")
}
```
And this is my downloadplot1
function: 这是我的
downloadplot1
函数:
output$downloadplot1 <- downloadHandler(
filename = "plots.pdf",
content = function(file){
tempReport <- file.path(tempdir(), "report1.Rmd")
file.copy("download_content.Rmd", tempReport, overwrite = TRUE)
list.of.measurands <- c("Measurand1", "Measurand2") #....all my measurands
plots.gen <- lapply(list.of.measurands, function(msrnd){
plot_ly(dummy.df, x = c(1:nrow(dummy.df)), y = ~get(msrnd), type = 'scatter', mode = 'markers')
})
params <- list(n = plots.gen)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.