Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make row_number() and quosures work together in an R function?

I have results from a within-participants design with timeseries info about each trial. I want to reshuffle the conditions for some permutation testing. I need to write a function though and this is where I run into problems.

My data looks something like this:

library(tidyverse) 

sampel <- expand.grid(s0 = 1:5, r0 = 1:12)
sampel <- sampel %>% mutate(c0 = rep(c('A', 'B'), 30))

sampel <- sampel %>% 
  group_by(s0, c0, r0) %>% 
  nest() %>% 
  mutate(t0 = map(data, function(t) seq(1:8)), v0 = map(data, function(v) seq(from = 0, by = runif(1), length.out = 8))) %>% 
  unnest(cols = c(data, t0, v0)) %>% 
  ungroup() %>% 
  mutate(s0 = paste('s', s0, sep = ''))

head(sampel, n = 12)

(if you have any pointers how I could go about displaying this example in a better way I would much appreciate it too)

So to add some context, it's results of a within-subjects study. s0 stands for participant, c0 for condition, r0 for trial number (run). t0 is a timepoint and v0 a value of interest at this timepoint.

I am trying to reshuffle conditions within-participants

resampledSampel <- 
  sampel %>% 
  group_by(s0, r0, c0) %>% 
  nest() %>% 
  group_by(s0) %>% 
  mutate(c1 = c0[sample(row_number())])


resampledSampel %>% 
  head(n = 12)

This works as hoped, but when I try to make a function:

resample_within <- function(df, subject, trial, condition) {

  subject <- enquo(subject)
  trial <- enquo(trial) 
  condition <- enquo(condition)

  resampled <- 
    df %>% 
    group_by(!!subject, !!trial, !!condition) %>%
    nest() %>% 
    group_by(!!subject) %>% 
    mutate(condition = !!condition[sample(row_number())]) %>% 
    unnest(data)
  return(resampled)

}

resample_within(sampel, s0, r0, c0) 

throws an error:

Error: row_number() should only be called in a data context
Run `rlang::last_error()` to see where the error occurred.
In addition: Warning message:
Subsetting quosures with `[` is deprecated as of rlang 0.4.0
Please use `quo_get_expr()` instead.
This warning is displayed once per session. 

Any idea how I can use mutate(condition = !!condition[sample(row_number())]) in the function? Or how I could do all this without dplyr (it makes me realise that I probably rely on dplyr a bit too much...)

Thank you in advance. And, also in advance, I apologise if the way I presented the question is not ideal (I will gladly take any pointers on how to better formulate questions on stack exchange too. For instance I can't seem to figure out how to display the data structure)

like image 889
pippi_calzaslargas Avatar asked Mar 02 '23 18:03

pippi_calzaslargas


2 Answers

Very nice first question!

This is actually just a matter of operator precedence. When you call !!condition[sample(row_number())], it is interpreted as !!(condition[sample(row_number())]) i.e. you are trying to subset the quosure then apply double bang, but you mean (!!condition)[sample(row_number())], that is, you want to subset the result of the double-bang. So just apply brackets to fix the order of evaluation and it works as expected:

resample_within <- function(df, subject, trial, condition) {

  subject <- enquo(subject)
  trial <- enquo(trial) 
  condition <- enquo(condition)

  resampled <- 
    df %>% 
    group_by(!!subject, !!trial, !!condition) %>%
    nest() %>% 
    group_by(!!subject) %>% 
    mutate(condition = (!!condition)[sample(row_number())]) %>% 
    unnest(data)
  return(resampled)

}

Now:

resample_within(sampel, s0, r0, c0) 
#> # A tibble: 480 x 6
#> # Groups:   s0 [5]
#>    s0       r0 c0       t0    v0 condition
#>    <chr> <int> <chr> <int> <dbl> <chr>    
#>  1 s1        1 A         1 0     B        
#>  2 s1        1 A         2 0.981 B        
#>  3 s1        1 A         3 1.96  B        
#>  4 s1        1 A         4 2.94  B        
#>  5 s1        1 A         5 3.93  B        
#>  6 s1        1 A         6 4.91  B        
#>  7 s1        1 A         7 5.89  B        
#>  8 s1        1 A         8 6.87  B        
#>  9 s2        1 B         1 0     A        
#> 10 s2        1 B         2 0.976 A        
#> # ... with 470 more rows
like image 65
Allan Cameron Avatar answered Mar 05 '23 15:03

Allan Cameron


We can use the curly-curly ({{}}) operator

library(dplyr)
library(tidyr)
resample_within <- function(df, subject, trial, condition) {



    df %>% 
    group_by({{subject}}, {{trial}}, {{condition}}) %>%
    nest() %>% 
    group_by({{subject}}) %>% 
    mutate(condition = ({{condition}})[sample(row_number())]) %>% 
    unnest(data)


}

resample_within(sampel, s0, r0, c0) 
like image 45
akrun Avatar answered Mar 05 '23 16:03

akrun