How can I use a variable to subset an R data.table when the variable is also a column in the data.table?

Viewed 42

I often want to subset a data.table using a variable that is also a column name in the data.table. For example, imagine I have a data.table with a column called "sex". I also have a variable "sex" defined in my environment (e.g., sex = "male"). If I want to subset the data.table using my sex variable, dt[sex==sex] returns all rows in the data.table because data.table thinks I'm comparing the "sex" column to itself.

Is there a way to tell data.table that I'm using a variable outside of the scope of the column names? I currently have a hacky workaround for this (see below), but there must be a more elegant solution.

library(data.table)
subset_metadata = function(sex){
    .sex = sex # hacky workaround 
    meta = data.table(sex=c("male","female","female","male","male"),
                      group=c("1w","2w","1w","2w","2w"),
                      var=rnorm(5))
    meta = meta[sex == .sex]
    return(meta)
}
subset_metadata("male")
1 Answers

This is mostly informed by Subsetting data.table using variables with same name as column, but some of them don't work cleanly so they can be rather frustrating.

This isn't much better, but it can be used universally if more than one variable is to be referenced:

subset_metadata = function(sex){
    .env <- environment()
    meta = data.table(sex=c("male","female","female","male","male"),
                      group=c("1w","2w","1w","2w","2w"),
                      var=rnorm(5))
    meta = meta[(sex == get("sex", envir = .env)),]
    return(meta)
}
subset_metadata("male")
#       sex  group        var
#    <char> <char>      <num>
# 1:   male     1w -0.9687163
# 2:   male     2w -0.2033112
# 3:   male     2w -0.4960741

Not yet, but when 1.14.3 is released, it will benefit from the use of env= (see https://rdatatable.gitlab.io/data.table/news/index.html#data-table-v1-14-3-in-development), where I suspect (without verifying yet) that one could use:

meta[ sex == .S, env = list(.S = I(sex)) ]

env is evaluated in a non-dt scope thus will catch expected argument. I() is used to have "male" character scalar rather than a name of variable as a symbol, otherwise meta[sex==male]. In this simple use case, env is used as kind of renaming variable on the fly.

Related