Reputation: 37913
I'm trying to get the number of rows for a data
argument in the tidy eval framework. Any suggestions what I should put in the quo()
function to let the expression evaluate to the number of rows of the data? I'm aware that the .data
pronoun isn't actually a data.frame, and a solution doesn't need to use it.
In the example below, I want q
to evaluate to nrow(my_data)
, which should be 20 in the example below.
library(rlang)
my_data <- data.frame(x = rnorm(20))
# Doesn't work
q <- quo(nrow(.data))
eval_tidy(q, my_data)
#> NULL
# Doesn't work
q <- quo(length(.data[[1]]))
eval_tidy(q, my_data)
#> Error: Must subset the data pronoun with a string.
# Works but requires prior knowledge of data
q <- quo(length(.data[["x"]]))
eval_tidy(q, my_data)
#> [1] 20
Created on 2021-12-20 by the reprex package (v2.0.1)
Ideally, this should work with data.frame
s of any dimension, including those with 0 rows or 0 columns.
EDIT: To clarify, I have no control over the tidy evaluation itself, which is why I'm looking for a solution that modifies what is put inside the quo()
function. The data part of the tidy evaluation is guaranteed to be a data.frame though.
Upvotes: 1
Views: 80
Reputation: 13691
One solution is to use a pre-designated variable name in the unevaluated expression captured by quo()
, then assign your data frame to that variable name in evaluation:
myquo <- quo(nrow(.df)) # Choosing .df as my variable name
eval_tidy(myquo, list(.df=my_data)) # Assigning my_data to that variable name
# [1] 20
# Works with empty data frames
data0 <- data.frame()
eval_tidy(myquo, list(.df=data0))
# [1] 0
EDIT: To address your comment, accessing the data frame from within the expression is tricky, because the environment where the expression is being evaluated doesn't know about it. So, you have to step up through the environments until you get to where eval_tidy()
is being called, which is where the data frame exists.
Here's a representative example:
myquo <- quo(
environment() %>% # The quosure itself
rlang::env_parent() %>% # The data frame, x is defined here
rlang::env_parent() %>% # The calling env of eval_tidy(), which
# knows of the quosure and the data frame
ls()
)
eval_tidy(myquo, my_data)
# [1] "my_data" "myquo"
So, if you can't modify the eval_tidy()
expression, but you know for certain what the data variable is called (my_data
in this case), you can access it directly in the calling environment:
myquo <- quo(
nrow(get("my_data"))
)
eval_tidy(myquo, my_data)
# [1] 20
Here, get()
will walk up the calling stack until it finds the variable with the appropriate name, so extra care may be needed to avoid name collisions.
Upvotes: 1