将数据帧作为参数从 Shiny 应用程序传递到 RMarkdown
我仍在努力解决我正在开发的 Shiny 应用程序的某些方面。目的是用户上传 csv 数据文件,然后对其进行处理以生成报告(来自 .Rmd 模板),然后用户可以将其下载为可编辑的 Word .doc。
如果我在正常的 R 会话中渲染它,.Rmd 工作正常。但是,如果从我的 Shiny 应用程序完成,我会收到以下错误:
Warning: Error in unique: object 'report.data' not found
[No stack trace available]
report.data 应该是通过读取输入 .csv 文件生成的数据帧。令人困惑的是,该应用程序有时确实可以工作(我认为如果 report.data 已经在全局环境中可用,就会发生这种情况。)。
我尝试在 .Rmd 文件的标头中定义参数(请参阅下面的注释行。) - 如果我这样做,则代码运行时不会出现错误,但生成的 Word 文档是空白的,除了标题。
谁能看到我哪里出错了?一如既往,感谢您花时间阅读本文并回复。
抱歉,我觉得我正在创建很多线程来寻求有关 Shiny 中似乎非常基本的事情的帮助,但我确实搜索了类似的问题,但从未找到完全正确的事情!但一旦我具备了这些基本的东西,我应该能够自己取得进步。
为report.data生成示例输入的.csv文件的代码:
library(dplyr)
set.seed(1234)
product1.parameter1.location1 <- data.frame(
result = rnorm(25, mean = 2.5, sd = 0.2),
product = c("Red Aeroplanes"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 1")
)
product1.parameter1.location2 <- data.frame(
result = rnorm(25, mean = 2.6, sd = 0.1),
product = c("Red Aeroplanes"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 2")
)
product1 <- rbind(product1.parameter1.location1, product1.parameter1.location2)
product2.parameter1.location1 <- data.frame(
result = rnorm(25, mean = 10, sd = 2),
product = c("Blue Trollies"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 1")
)
product2.parameter1.location2 <- data.frame(
result = rnorm(25, mean = 9.5, sd = 0.75),
product = c("Blue Trollies"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 2"))
product2.parameter1 <- rbind(product2.parameter1.location1, product2.parameter1.location2)
product2.parameter2.location1 <- data.frame(
result = rnorm(25, mean = 30, sd = 1.8),
product = c("Blue Trollies"),
parameter = c("Parameter 2"),
sample.no = c(1:25),
location = c("Factory 1")
)
product2.parameter2.location2 <- data.frame(
result = rnorm(25, mean = 25, sd = 0.75),
product = c("Blue Trollies"),
parameter = c("Parameter 2"),
sample.no = c(1:25),
location = c("Factory 2"))
product2.parameter2 <- rbind(product2.parameter2.location1, product2.parameter2.location2)
product2 <- rbind(product2.parameter1, product2.parameter2)
product3.parameter1.location1 <- data.frame(
result = rnorm(35, mean = 2, sd = 0.2),
product = c("Brown Carriages"),
parameter = c("Parameter 1"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter1.location2 <- data.frame(
result = rnorm(35, mean = 1.9, sd = 0.15),
product = c("Brown Carriages"),
parameter = c("Parameter 1"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter1 <- rbind(product3.parameter1.location1, product3.parameter1.location2)
product3.parameter2.location1 <- data.frame(
result = rnorm(35, mean = 4, sd = 0.4),
product = c("Brown Carriages"),
parameter = c("Parameter 2"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter2.location2 <- data.frame(
result = rnorm(35, mean = 3.8, sd = 0.5),
product = c("Brown Carriages"),
parameter = c("Parameter 2"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter2 <- rbind(product3.parameter2.location1, product3.parameter2.location2)
product3.parameter3.location1 <- data.frame(
result = rnorm(35, mean = 10, sd = 1.8),
product = c("Brown Carriages"),
parameter = c("Parameter 3"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter3.location2 <- data.frame(
result = rnorm(35, mean = 10, sd = 2),
product = c("Brown Carriages"),
parameter = c("Parameter 3"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter3 <- rbind(product3.parameter3.location1, product3.parameter3.location2)
product3 <- rbind(product3.parameter1, product3.parameter2, product3.parameter3)
write.csv(product1, "product1.csv", row.names = FALSE)
write.csv(product2, "product2.csv", row.names = FALSE)
write.csv(product3, "product3.csv", row.names = FALSE)
report.data <- rbind(product1, product2, product3) %>% mutate(identifier = paste(product, parameter, sep = " "))
write.csv(report.data, "all.data.csv", row.names = FALSE)
app.R代码:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("R Shiny app"),
# Sidebar with file input
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "file1",
label = "Select file(s)",
multiple = TRUE,
accept = NULL,
width = NULL,
buttonLabel = "Browse...",
placeholder = "No file(s) selected"
),
downloadButton("report", "Generate report")
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$report <- downloadHandler(
reactive(file <- input$file1),
# For PDF output, change this to "report.pdf"
filename = "report.doc",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "wordreport.Rmd")
file.copy("wordreport.Rmd", tempReport, overwrite = TRUE)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
params <- list(report.data = input$file1)
rmarkdown::render(tempReport, output_file = "wordreport.doc",
params = params,
envir = new.env(parent = globalenv()))
file.copy("wordreport.doc", file)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
.Rmd文件(与参数声明相关的行被注释掉):
---
title: "Comparison Report for [CATEGORY] in [MONTH/YEAR]"
output: word_document
toc: yes
#params:
#report.data: report.data
---
```{r setup, include=FALSE, comment = "", results = 'asis', echo = FALSE}
library(dplyr)
library(ggplot2)
library(purrr)
knitr::opts_chunk$set(echo = FALSE)
```
#report.data <- params$report.data
```
my_plot <- function(df) {
ggplot(df, aes(x = sample.no, y = result)) +
geom_point(aes(colour = location)) +
geom_hline(aes(yintercept = mean(result)), colour = "black", linetype = "dotted") +
geom_hline(aes(yintercept = mean(result) + 1.96 * sd(result)), colour = "red", linetype = "dashed") +
geom_hline(aes(yintercept = mean(result) - 1.96 * sd(result)), colour = "red", linetype = "dashed") +
theme_classic() +
theme(legend.title = element_blank()) +
labs(
title = paste0("Comparison for ", unique(df$identifier)),
x = "Sample number",
y = "Result") +
#caption = paste0("Caption here.")) +
expand_limits(y = 0) +
coord_cartesian(xlim = c(0, max(df$sample.no) + 2)) +
theme(
plot.caption=element_text(size=12, hjust = 0, margin = margin(t=20)),
plot.margin = margin(b=50)
)
}
```
```{r, comment = "", results = 'asis', echo = FALSE}
purrr::map(unique(report.data$identifier),
function(x) {
#section heading
cat("#", (x), "\n")
cat("\n\n")
# filter data before passing it to the plot function
report.data %>%
dplyr::filter(identifier == x) %>%
my_plot() %>% print()
cat("\n\n")
no.outofbounds <- report.data %>%
dplyr::filter(identifier == x) %>%
mutate(outofbounds = ifelse(result > mean(result)+1.96*sd(result)|result < mean(result)-1.96*sd(result), TRUE, FALSE)) %>%
dplyr::filter(outofbounds == TRUE) %>%
nrow()
ifelse(no.outofbounds > 0, paste(cat(no.outofbounds), " results greater than 1.96 standard deviations away from the mean."), "All results within 1.96 standard deviations of the mean.") %>%
cat()
cat("\n\n")
CV <- report.data %>%
dplyr::filter(identifier == x) %>%
summarise(CV = sd(result)/mean(result) * 100) %>%
round(2)
cat("\n\n")
paste("The all-site/factor CV for this parameter is ", CV, "%.") %>%
cat()
cat("\n\n")
cat("APPROVED/REJECTED.")
cat("\n\n")
}
) -> results
```
I'm still struggling with some aspects of a Shiny app I'm working on. The intention is that the user uploads a csv file of data, which is then processed to generate a report (from a .Rmd template), which the user can then download as an editable Word .doc.
The .Rmd works fine if I render it in a normal R session. However, if done from my Shiny app, I get the following error:
Warning: Error in unique: object 'report.data' not found
[No stack trace available]
report.data should be the dataframe produced by reading the input .csv file. Confusingly, the app does sometimes work (I think this occurs if report.data is already available in the global environment.).
I've tried defining the params in the header of the .Rmd file (see the commented out lines below.) - if I do this then the code runs without an error, but the resulting word document is blank, except for the title.
Can anyone see where I'm going wrong? Thank you, as ever, for your time in reading this and replying.
And apologies, I feel like I'm making a lot of threads asking for help with what seem to be quite basic things in Shiny, but I do search for similar questions and never find things that are quite right! But once I have these basic things in place I should be able to make progress by myself.
Code to generate a .csv file of example input for report.data:
library(dplyr)
set.seed(1234)
product1.parameter1.location1 <- data.frame(
result = rnorm(25, mean = 2.5, sd = 0.2),
product = c("Red Aeroplanes"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 1")
)
product1.parameter1.location2 <- data.frame(
result = rnorm(25, mean = 2.6, sd = 0.1),
product = c("Red Aeroplanes"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 2")
)
product1 <- rbind(product1.parameter1.location1, product1.parameter1.location2)
product2.parameter1.location1 <- data.frame(
result = rnorm(25, mean = 10, sd = 2),
product = c("Blue Trollies"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 1")
)
product2.parameter1.location2 <- data.frame(
result = rnorm(25, mean = 9.5, sd = 0.75),
product = c("Blue Trollies"),
parameter = c("Parameter 1"),
sample.no = c(1:25),
location = c("Factory 2"))
product2.parameter1 <- rbind(product2.parameter1.location1, product2.parameter1.location2)
product2.parameter2.location1 <- data.frame(
result = rnorm(25, mean = 30, sd = 1.8),
product = c("Blue Trollies"),
parameter = c("Parameter 2"),
sample.no = c(1:25),
location = c("Factory 1")
)
product2.parameter2.location2 <- data.frame(
result = rnorm(25, mean = 25, sd = 0.75),
product = c("Blue Trollies"),
parameter = c("Parameter 2"),
sample.no = c(1:25),
location = c("Factory 2"))
product2.parameter2 <- rbind(product2.parameter2.location1, product2.parameter2.location2)
product2 <- rbind(product2.parameter1, product2.parameter2)
product3.parameter1.location1 <- data.frame(
result = rnorm(35, mean = 2, sd = 0.2),
product = c("Brown Carriages"),
parameter = c("Parameter 1"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter1.location2 <- data.frame(
result = rnorm(35, mean = 1.9, sd = 0.15),
product = c("Brown Carriages"),
parameter = c("Parameter 1"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter1 <- rbind(product3.parameter1.location1, product3.parameter1.location2)
product3.parameter2.location1 <- data.frame(
result = rnorm(35, mean = 4, sd = 0.4),
product = c("Brown Carriages"),
parameter = c("Parameter 2"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter2.location2 <- data.frame(
result = rnorm(35, mean = 3.8, sd = 0.5),
product = c("Brown Carriages"),
parameter = c("Parameter 2"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter2 <- rbind(product3.parameter2.location1, product3.parameter2.location2)
product3.parameter3.location1 <- data.frame(
result = rnorm(35, mean = 10, sd = 1.8),
product = c("Brown Carriages"),
parameter = c("Parameter 3"),
sample.no = c(1:35),
location = c("Factory 1")
)
product3.parameter3.location2 <- data.frame(
result = rnorm(35, mean = 10, sd = 2),
product = c("Brown Carriages"),
parameter = c("Parameter 3"),
sample.no = c(1:35),
location = c("Factory 2"))
product3.parameter3 <- rbind(product3.parameter3.location1, product3.parameter3.location2)
product3 <- rbind(product3.parameter1, product3.parameter2, product3.parameter3)
write.csv(product1, "product1.csv", row.names = FALSE)
write.csv(product2, "product2.csv", row.names = FALSE)
write.csv(product3, "product3.csv", row.names = FALSE)
report.data <- rbind(product1, product2, product3) %>% mutate(identifier = paste(product, parameter, sep = " "))
write.csv(report.data, "all.data.csv", row.names = FALSE)
The app.R code:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("R Shiny app"),
# Sidebar with file input
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "file1",
label = "Select file(s)",
multiple = TRUE,
accept = NULL,
width = NULL,
buttonLabel = "Browse...",
placeholder = "No file(s) selected"
),
downloadButton("report", "Generate report")
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$report <- downloadHandler(
reactive(file <- input$file1),
# For PDF output, change this to "report.pdf"
filename = "report.doc",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "wordreport.Rmd")
file.copy("wordreport.Rmd", tempReport, overwrite = TRUE)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
params <- list(report.data = input$file1)
rmarkdown::render(tempReport, output_file = "wordreport.doc",
params = params,
envir = new.env(parent = globalenv()))
file.copy("wordreport.doc", file)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
The .Rmd file (with the lines relating to params declaration commented out):
---
title: "Comparison Report for [CATEGORY] in [MONTH/YEAR]"
output: word_document
toc: yes
#params:
#report.data: report.data
---
```{r setup, include=FALSE, comment = "", results = 'asis', echo = FALSE}
library(dplyr)
library(ggplot2)
library(purrr)
knitr::opts_chunk$set(echo = FALSE)
```
#report.data <- params$report.data
```
my_plot <- function(df) {
ggplot(df, aes(x = sample.no, y = result)) +
geom_point(aes(colour = location)) +
geom_hline(aes(yintercept = mean(result)), colour = "black", linetype = "dotted") +
geom_hline(aes(yintercept = mean(result) + 1.96 * sd(result)), colour = "red", linetype = "dashed") +
geom_hline(aes(yintercept = mean(result) - 1.96 * sd(result)), colour = "red", linetype = "dashed") +
theme_classic() +
theme(legend.title = element_blank()) +
labs(
title = paste0("Comparison for ", unique(df$identifier)),
x = "Sample number",
y = "Result") +
#caption = paste0("Caption here.")) +
expand_limits(y = 0) +
coord_cartesian(xlim = c(0, max(df$sample.no) + 2)) +
theme(
plot.caption=element_text(size=12, hjust = 0, margin = margin(t=20)),
plot.margin = margin(b=50)
)
}
```
```{r, comment = "", results = 'asis', echo = FALSE}
purrr::map(unique(report.data$identifier),
function(x) {
#section heading
cat("#", (x), "\n")
cat("\n\n")
# filter data before passing it to the plot function
report.data %>%
dplyr::filter(identifier == x) %>%
my_plot() %>% print()
cat("\n\n")
no.outofbounds <- report.data %>%
dplyr::filter(identifier == x) %>%
mutate(outofbounds = ifelse(result > mean(result)+1.96*sd(result)|result < mean(result)-1.96*sd(result), TRUE, FALSE)) %>%
dplyr::filter(outofbounds == TRUE) %>%
nrow()
ifelse(no.outofbounds > 0, paste(cat(no.outofbounds), " results greater than 1.96 standard deviations away from the mean."), "All results within 1.96 standard deviations of the mean.") %>%
cat()
cat("\n\n")
CV <- report.data %>%
dplyr::filter(identifier == x) %>%
summarise(CV = sd(result)/mean(result) * 100) %>%
round(2)
cat("\n\n")
paste("The all-site/factor CV for this parameter is ", CV, "%.") %>%
cat()
cat("\n\n")
cat("APPROVED/REJECTED.")
cat("\n\n")
}
) -> results
```
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的代码有几个问题。我将一一检查一下
downloadHandler() 中的无效参数
您正在将
reactive
类的对象传递给downloadHandler()
contentType 参数代码>.这似乎搞乱了 downloadHandler() 的整个逻辑,并导致客户端出现“服务器错误”消息,而闪亮的没有错误或警告。
需要删除此行才能成功下载文件
正确引用 Rmd 参数
当您想要从 Rmd 报告访问参数时,您将需要使用
params$report.data
。仅使用report.data
将导致以下错误:object 'report.data' not found
。修复生成文件的路径
您正在临时目录中编织 Rmd,这通常是一个好主意。然而,找到正确的路径并不总是那么容易。在您的情况下,我使用了以下版本。
您的版本不起作用的原因是生成的报告是在临时目录 alogside
tmpReport
内创建的。有关更多详细信息,请参阅?rmarkdown::render
的参考文档。我使用 rmarkdown::render() 的返回值来代替,它保存生成文件的绝对路径。这不容易出错,如果您事先不知道生成文件的文件扩展名,则特别有用
使用 read.csv 将上传的文件转换为 data.frame
Shiny 不会自动将上传的 csv 文件转换为 dataframe。您需要定义一个解析逻辑来做到这一点。
最后一句话
尝试让你的编码项目更有条理,并将未来 SO 问题的范围限制为一次一个问题。创建“最小的可重现示例”一开始可能看起来很乏味,但是这样做有几个优点:
There are several issues with your code. I'll go over them one by one
Invalid parameter in downloadHandler()
You are passing an object of class
reactive
to thecontentType
parameter ofdownloadHandler()
.It seems that this messes up the whole logic of
downloadHandler()
and leads to "server error" messages on the client side with no errors or warnings from shiny.This line needs to be removed in order to download files successfully
Reference the Rmd-parameter correctly
When you want to access the parameter from the Rmd report, you will need to use
params$report.data
. Just usingreport.data
will lead to the following error:object 'report.data' not found
.Fix the path to the generated file
You are knitting the Rmd inside the temporary directory, which is generally a good idea. However, getting the paths right is not always that easy. In your case, I used the following
The reason your version didn't work is that the generated report is created inside the temporary directory alogside
tmpReport
. See the reference documentation of?rmarkdown::render
for more details.I used the return value of
rmarkdown::render()
instead which holds an absolute path to the generated file. This is less error prone and especially useful if you do not know the file extension of the generated file in advanceUse read.csv to convert the uploaded file into a data.frame
Shiny doesn't automatically convert uploaded csv files into dataframes. You need to define a parsing logic to do that.
One final word
Try to get more organized with your coding projects and limit the scope of future SO questions to one issue at a time. Creating "minimal reproducible examples" might seem tedious at first, but there are several advantages in doing that