How to generalize a function?

Morning all,

I have two custom functions that I wrote this morning to make the process of building tables for my Bayes factor tests easier, but I could use help tweaking the code to generalize them. Specifically, what I want to do is change their input to somehow allow for any number of comparison tests, instead of the temporary two I have now.

In the paper I'm currently writing I compared three models, but obviously the number of models will change from paper to paper. So instead of changing the functions for every paper, I would like a way to make the code adaptable, and apply my first function to "model i" in the list of models I give it, and then use the section function to glue all given models together. Not sure how to do this though...I assume it involves a for loop?

If anyone has any tips on how to tweak the function to handle a list of inputted models I'd be very grateful. Here is a picture of the (unfinishedflextable in Word to show you what I'm after:

Annotation 2020-08-03 084928

# functions

BF_table=function(BF_output){
  BF_output=BF_output
  BF_output$BF=round(BF_output$BF,2)
  BF_output=add_row(BF_output,Model="Test X", .before=1)
  BF_output$Evidence=c("")
  return(BF_output)
}


glue_BFs=function(test_1, test_2){
  test_1=BF_table(test_1)
  test_2=BF_table(test_2)
  new_table=rbind(test_1,test_2)
  return(new_table)
}


# original code for creating the tables without the above functions
 #Bayes factors
    #Test 1
    BFtesttable=ModelsBF
    BFtesttable$BF=round(BFtesttable$BF,2)
    BFtesttable=add_row(BFtesttable, Model="Test 1", .before = 1)
    BFtesttable$Evidence=c("")

    #test 2
    BFtesttable2=ModelsBF2
    BFtesttable2$BF=round(BFtesttable2$BF,2)
    BFtesttable2=add_row(BFtesttable2, Model="Test 2", .before = 1)
    BFtesttable2$Evidence=c("")

    #test 3
    BFtesttable3=ModelsBF3
    BFtesttable3$BF=round(BFtesttable3$BF,2)
    BFtesttable3=add_row(BFtesttable3, Model="Test 3", .before = 1)
    BFtesttable3$Evidence=c("")

    #Bind together
    Bayesfactors=rbind(BFtesttable,BFtesttable2, BFtesttable3)
    rm(BFtesttable,BFtesttable2,BFtesttable3)



Hi,

I can't run your code, as you did not supply a real reprex, but I think I know what you were looking for. For future reference: A reprex consists of the minimal code and data needed to recreate the issue/question you're having. You can find instructions how to build and share one here:

library(dplyr)
library(purrr)

BF_table=function(BF_output){
  
  #Put code here, now returns random data frame
  myTable = data.frame(x = BF_output, y = runif(5))
  return(myTable)
}

glue_BFs=function(...){
  
  #No need to replace any code (should work...)
  newTable = map_df(list(...), BF_table)
  return(newTable)
}


glue_BFs(1,2,3)
#>    x          y
#> 1  1 0.34722334
#> 2  1 0.48636603
#> 3  1 0.50046496
#> 4  1 0.15819100
#> 5  1 0.55372418
#> 6  2 0.30276565
#> 7  2 0.63276609
#> 8  2 0.76074751
#> 9  2 0.51630210
#> 10 2 0.91425049
#> 11 3 0.36733847
#> 12 3 0.54266198
#> 13 3 0.59673181
#> 14 3 0.09400021
#> 15 3 0.52223592

Created on 2020-08-03 by the reprex package (v0.3.0)

The clue to the whole thing is the (...) argument in the glue_BFs function. These three dots mean it will take and number of trailing arguments and put them in a vector. So this can go from 0 to Inf (in theory). If you need to pass any named arguments, you can combine the two, but remember that the position of placing the ... will define which arguments go where.

... last

test = function(arg1, ...){
  print(arg1)
  print(c(...))
}

test(1,2,3)

In this function, the first argument will always be arg1 (e.g. it is mandatory and must be set) In the example case arg1 = 1, and ... is 2 and 3. This way of writing is best when your function has one or more mandatory arguments, followed by the list of trailing arguments.

... first

test = function(..., arg1 = NULL){
  print(arg1)
  print(c(...))
}

test(1,2,3)
test(1,2, arg1 = 3)

In this case, all arguments are considered trailing, thus will go in the ... unless you specify the argument explicitly. So in the first case, 1,2 and 3 are all ... and arg1 is undefined, in the second 1 and 2 are in ... and arg1 = 3. This approach is best if the arguments that have a name are optional, and thus are not often set.

Hope this helps,
PJ

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

Your code worked perfectly! Thanks so much!!!

Sorry about not including a reprex; it's hard asking for tips when you're working with data from an unpublished study and don't want to pass out the data. But your answer was exactly what I was hoping for--an explanation of the code itself! Working through it with your examples is going to help me learn this faster...much appreciated.

1 Like