qenv
A qenv
is an R object which contains code and an
environment and can be used to create reproducible outputs.
The qenv()
function serves as the gateway to create an
initial qenv
object:
library(teal.code)
# create a new qenv object
<- qenv()
empty_qenv print(empty_qenv)
## <environment: 0x7fa67d3ae9b0> [L]
## Parent: <environment: package:teal.code>
qenv
basic usageThe eval_code()
function executes code within a
qenv
environment, yielding a new qenv
object
as the output.
library(magrittr)
# evaluate code in qenv
<- eval_code(empty_qenv, "x <- 2")
my_qenv print(my_qenv)
## <environment: 0x7fa67d4e8d98> [L]
## Parent: <environment: package:magrittr>
## Bindings:
## • x: <dbl> [L]
get_env(my_qenv)
## <environment: 0x7fa67d4e8d98>
<- eval_code(my_qenv, "y <- x * 2") %>% eval_code("z <- y * 2")
q1
# my_qenv still contains only x
print(my_qenv)
## <environment: 0x7fa67d4e8d98> [L]
## Parent: <environment: package:magrittr>
## Bindings:
## • x: <dbl> [L]
ls(get_env(my_qenv))
## [1] "x"
# q1 contains x, y and z
print(q1)
## <environment: 0x7fa685c59a20> [L]
## Parent: <environment: package:magrittr>
## Bindings:
## • x: <dbl> [L]
## • y: <dbl> [L]
## • z: <dbl> [L]
ls(get_env(q1))
## [1] "x" "y" "z"
The same result can be achieved with the within
method
for the qenv
class.
<- within(my_qenv, y <- x * 2) %>% within(z <- y * 2)
q2 print(q2)
## <environment: 0x7fa6865de940> [L]
## Parent: <environment: package:magrittr>
## Bindings:
## • x: <dbl> [L]
## • y: <dbl> [L]
## • z: <dbl> [L]
To extract objects from a qenv
, use [[
;
this is particularly useful for displaying them in a shiny
app. You can retrieve the code used to generate the qenv
using the get_code()
function.
print(q2[["y"]])
## [1] 4
cat(get_code(q2))
## x <- 2
## y <- x * 2
## z <- y * 2
In some cases, one may want to substitute some elements of the code
before evaluation. Consider a case when a subset of iris
is
defined by an input value.
<- qenv()
q <- eval_code(q, quote(i <- subset(iris, Species == "setosa")))
q <- eval_code(q, substitute(
q <- subset(iris, Species == species),
ii env = list(species = "versicolor")
))<- "virginica"
input_value <- eval_code(q, substitute(
q <- subset(iris, Species == species),
iii env = list(species = input_value)
))
summary(q[["i"]]$Species)
## setosa versicolor virginica
## 50 0 0
summary(q[["ii"]]$Species)
## setosa versicolor virginica
## 0 50 0
summary(q[["iii"]]$Species)
## setosa versicolor virginica
## 0 0 50
A more convenient way to pass code with substitution is to use the
within
method.
<- qenv()
qq <- within(qq, i <- subset(iris, Species == "setosa"))
qq <- within(qq, ii <- subset(iris, Species == species), species = "versicolor")
qq <- "virginica"
input_value <- within(qq, iii <- subset(iris, Species == species), species = input_value)
qq
summary(qq[["i"]]$Species)
## setosa versicolor virginica
## 50 0 0
summary(qq[["ii"]]$Species)
## setosa versicolor virginica
## 0 50 0
summary(qq[["iii"]]$Species)
## setosa versicolor virginica
## 0 0 50
See ?qenv
for more details.
qenv
objectsGiven a pair of qenv
objects, you may be able to “join”
them, creating a new qenv
object encompassing the union of
both environments, along with the requisite code for reproduction:
<- eval_code(qenv(), quote(x <- 1))
common_q
<- eval_code(common_q, quote(y <- 5))
x_q <- eval_code(common_q, quote(z <- 5))
y_q
<- join(x_q, y_q)
join_q
print(join_q)
## <environment: 0x7fa682b521f0>
## Parent: <environment: package:magrittr>
## Bindings:
## • x: <dbl>
## • y: <dbl>
## • z: <dbl>
ls(get_env(join_q))
## [1] "x" "y" "z"
The feasibility of joining qenv
objects hinges on the
contents of the environments and the code’s order. Refer to the function
documentation for further details.
qenv
objectsIn cases where warnings or messages arise while evaluating code
within a qenv
environment, these are captured and stored
within the qenv
object. Access these messages and warnings
using the @
operator.
<- eval_code(qenv(), quote(message("this is a message")))
q_message @messages q_message
## [1] "> this is a message\n"
<- eval_code(qenv(), quote(warning("and this is a warning")))
q_warning @warnings q_warning
## [1] "> and this is a warning\n"
If a particular line of code doesn’t trigger any warnings or messages, the corresponding message/warning value will be an empty string.
@warnings q_message
## [1] ""
@messages q_warning
## [1] ""
Additionally, a helper function, get_warnings()
, is
available to generate a formatted string comprising the warnings and the
code responsible for generating them. It returns NULL
when
no warnings are present.
qenv
inside shiny
applicationsThese functions can be seamlessly integrated into shiny
applications to produce reproducible outputs. In the example below, the
rcode
section showcases the code employed for generating
the output.
When employing a qenv
to evaluate code, should an error
occur, an object of type qenv.error
is generated. This
object can be utilized wherever a qenv
object is used,
alleviating the need for code alterations to handle these errors. Select
the error_option
in the example below to witness
qenv
error handling in action.
library(shiny)
library(magrittr)
# create an initial qenv with the data in
<- qenv() %>% eval_code("iris_data <- iris")
data_q
<- fluidPage(
ui radioButtons(
"option", "Choose a column to plot:",
c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "error_option")
),verbatimTextOutput("rcode"),
plotOutput("plot")
)
<- function(input, output, session) {
server # create a qenv containing the reproducible output
<- reactive({
output_q req(input$option)
eval_code(
data_q,bquote(p <- hist(iris_data[, .(input$option)]))
)
})
# display output
$plot <- renderPlot(output_q()[["p"]])
output# display code
$rcode <- renderText(get_code(output_q()))
output
}
if (interactive()) {
shinyApp(ui, server)
}
qenv
and teal
applicationsThe versatile qenv
object can seamlessly integrate into
teal modules. Explore the teal vignette Creating
Custom Modules for detailed guidance.