3

I want to use the ICC::ICCbare function within a loop. However, the ICCbare uses the concrete variable names as input, e.g.:

ICCbare(x = group, y = variable1, data = dat)

whereby both "group" and "variable1" are columns of the data.frame "dat" (i.e., dat$variable1); ICCbarecannot be used with y = dat[, i].

In order to program a loop I therefore need to evaluate some R code within the function call of ICCbare. My idea was the following:

for(i in 1:10){
  ICCbare(group, names(dat)[i], data = dat)  
}

However, this does not work. The following error is printed:

Error in '[.data.frame`(data, yc) : undefined columns selected'

Is there a way to evaluate the statement names(dat)[i]) first before it is passed to the function call?

Here is a minimum working example for my problem:

# Create data set
dat <- data.frame(group=c(rep("A",5), 
                  rep("B",5)), 
                  variable1=1:10, 
                  variable2=rnorm(10))

# Loop
for (i in names(dat)[2:3]){
  ICCbare("group", i, data = dat)
} 
phx
  • 332
  • 4
  • 13
  • I suspect you are overthinking this. If I understand the documentation of `ICCbare` correctly (didn't install the package), you are supposed to pass `character`s for the column names. Is that not the case? Your example of usage outside a loop seems to indicate that non-standard evaluation is used instead. Is that really the case and doesn't `ICCbare(x = "group", y = "variable1", data = dat)` work? – Roland Nov 05 '14 at 08:46
  • Yeah `ICCbare(x = "group", y = "variable1", data = dat)` does indeed work. However, I am not quite sure how that helps for my "loop problem"? – phx Nov 05 '14 at 08:50
  • So, have you tried `for(i in 1:10) ICCbare("group", names(dat)[i], data = dat)`? – Roland Nov 05 '14 at 08:56
  • If @Roland's solution works, `for (i in names(dat)) ICCbare("group", i, data = dat)` should work as well. – Roman Luštrik Nov 05 '14 at 08:58
  • Both solutions are -- unfortunately -- also not working for me. They give me the same error message as above. – phx Nov 05 '14 at 08:59
  • At which `i` does the loop result in the error? Does `dat[, names(dat)[i]]` exist? You might have to create a [minimal reproducible example](http://stackoverflow.com/a/5963610/1412059). – Roland Nov 05 '14 at 09:02
  • I added a minimum working example. `dat[, names(dat)[i]]` exists for both variables in the example. Furthermore, this is not a loop problem. `ICCbare("group", names(dat)[2], data = dat)` also gives the above error message. – phx Nov 05 '14 at 09:11

3 Answers3

4

I agree with @agstudy. This is a bad example of non-standard evaluation. You can use this as a workaround:

v <- "variable1"
ICCbare("group",  v, data = dat)
#Error in `[.data.frame`(data, yc) : undefined columns selected

eval(bquote(ICCbare("group",  .(v), data = dat)))
#$ICC
#[1] 0.8275862
Roland
  • 127,288
  • 10
  • 191
  • 288
3

It is a bug in ICCbare that try to to manage arguments as name in a bad manner.

function (x, y, data) 
{
  ICCcall <- Call <- match.call()
  xc <- as.character(ICCcall[[2L]])  ## this is ugly!
  yc <- as.character(ICCcall[[3L]])  
  inds <- unique(data[xc])[[1]]
  tdata <- data.frame(data[yc], data[xc])

Personally I would remove the first lines and just use assume that arguments are just column names.

ICCbare_simple <- 
function (xc, yc, data) 
{
  ## remove lines before this one 
  inds <- unique(data[xc])[[1]]
  ## the rest of the code 
  .....
}
Roland
  • 127,288
  • 10
  • 191
  • 288
agstudy
  • 119,832
  • 17
  • 199
  • 261
  • Both answers are a good solution. However, I accepted Roland's one since it is a good work around and does not need any changes made within ICCbare. Thanks nevertheless! – phx Nov 05 '14 at 09:30
  • 1
    @phx You should report this to the package maintainer. They should fix this. – Roland Nov 05 '14 at 09:35
  • I also already mailed the package maintainer. He might change the argument management in his functions in the future. – phx Nov 05 '14 at 09:35
3

I'm the maintainer of ICC and I want to thank you for the excellent discussion. I know this is a very late reply, but I just updated the package and the new version (v2.3.0) should fix the "ugly" code and the problem encountered by the OP. See examples in this gist.

I just wanted to post this here in case anyone was searching with a similar problem. Thanks again, sorry for the delay.

Here is the content of the gist:


ICC non-standard evaluation examples

The ICC package for R calculates the intraclass correlation coefficient (ICC) from a one-way analysis of variance. Recently, the package was updated to better execute R's non-standard evaluation within each function (version 2.3.0 and higher). The package functions should now be able to handle a range of possible scenarios for calling the functions in, what I hope, is a less grotesque and more standard way of writing R functions. To demonstrate, below are some of those scenarios. Note, the examples use the ICCbare function, but the way in which the function arguments are supplied will apply to all of the functions in ICC.

First, load the package (and make sure the version is >2.3.0)

library(ICC)
packageVersion("ICC")

Columns of a data.frame

Here we supply the column names and the data.frame that contains the data to calculate the ICC. We will use the ChickWeight data fame.

data(ChickWeight)
ICCbare(x = Chick, y = weight, data = ChickWeight)
#$ICC
#[1] 0.1077609

Iterating through columns of a data.frame

In this case, we might have a data.frame in which we want to estimate the ICC for a number of different types of measurements that each has the same grouping or factor variable (e.g., x). The extreme of this might be in a simulation or bootstrapping scenario or even with some fancy high-throughput phenotyping/data collection. The point being, we want to automate the calculation of the ICC for each column.

First, we will simulate our own dataset with 3 traits to use in the example:

set.seed(101)
n <- 15                                    # number of individuals/groups/categories/factors
k <- 3                                     # number of measures per 'n'
va <- 1                                    # variance among
icc <- 0.6                                 # expected ICC
vw <- (va * (1 - icc)) / icc               # solve for variance within
simdf <- data.frame(ind = rep(LETTERS[1:n], each = k),
   t1 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)),
   t2 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)),
   t3 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)))

Two ways to run through the columns come to mind: iteratively pass the name of each column or iteratively pass the column index. I will demonstrate both below. I do these in for loops so it is easier to see, but an easy extension would be to vectorise this by using something from the apply family of functions. First, passing the name:

for(i in names(simdf)[-1]){
   cat(i, ":")
   tmp.icc <- ICCbare(x = ind, y = i, data = simdf)
   cat(tmp.icc, "\n")
}
#t1 : 0.60446 
#t2 : 0.6381197 
#t3 : 0.591065 

or even like this:

for(i in 1:3){
   cat(paste0("t", i), ": ")
   tmp.icc <- ICCbare(x = ind, y = paste0("t", i), data = simdf)
   cat(tmp.icc, "\n")
}
#t1 : 0.60446 
#t2 : 0.6381197 
#t3 : 0.591065 

Alternatively, pass the column index:

for(i in 2:ncol(simdf)){
   cat(names(simdf)[i], ": ")
   tmp.icc <- ICCbare(x = ind, y = simdf[, i], data = simdf)
   cat(tmp.icc, "\n")
}
#t1 : 0.60446 
#t2 : 0.6381197 
#t3 : 0.591065 

Passing a character as an argument is deprecated

Note that the function will still work if a character is passed directly (e.g., "t1"), albeit with a warning. The warning just means that this may no longer work in future versions of the package. For example:

ICCbare(x = ind, y = "t1", data = simdf)
#[1] 0.60446
#Warning message:
#In ICCbare(x = ind, y = "t1", data = simdf) :
#  passing a character string to 'y' is deprecated since ICC version
#  2.3.0 and will not be supported in future versions. The argument 
#  to 'y' should either be an unquoted column name of 'data' or an object

Note, however, that an expression evaluating to a character (e.g., paste0("t", 1)) doesn't throw the warning, which is nice!

Analytics

kdopen
  • 8,032
  • 7
  • 44
  • 52
mwolak
  • 31
  • 3
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment). – Pino Jun 19 '15 at 15:05
  • I wanted to provide a comment, but because I just signed up I cannot comment on other posts (as @Pino notes). I fully realized it was not an explicit answer, but want the information available in case someone finds this post later on. Sorry if I didn't follow protocol, but in my situation writing an 'answer' was the only way I could do anything! – mwolak Jun 19 '15 at 16:54
  • 1
    I have pasted and formatted the gist contents to make this a more complete answer – kdopen Jun 21 '15 at 02:21