Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use with/within inside a function?

Tags:

r

I'm struggling to understand why does this doesn't work.

df <- data.frame(a=1:10, b=1:10)

foo <- function(obj, col) {
   with(obj, ls())
   with(obj, print(col))
}
foo(df, a)

[1] "a" "b"

Error in print(col) : object 'a' not found

If this does work:

with(df, print(a))
like image 764
rafalotufo Avatar asked May 19 '11 15:05

rafalotufo


2 Answers

with is handy and improves readability in an interactive context but can hurt your brain in a programming context where you are passing things back and forth to functions and dealing with things in different environments. In general within R, using symbols rather than names is a sort of "semantic sugar" that is convenient and readable in interactive use but mildly deprecated for programming [e.g. $, subset]). If you're willing to compromise as far as using a name ("a") rather than a symbol (a) then I would suggest falling back to the simpler obj[[col]] rather than using with here ...

So, as a self-contained answer:

foo <- function(object,col) {
   print(names(object))
   print(object[[col]])
}

If you wanted to allow for multiple columns (i.e. a character vector)

foo <- function(object,col) {
   print(names(object))
   print(object[col])
}

edit: refraining from using subset with a function, at @hadley's suggestion

(this will print the answer as a data frame, even if a single column is selected, which may not be what you want).

like image 110
Ben Bolker Avatar answered Oct 20 '22 04:10

Ben Bolker


Anything that is passed to a function must be an object, a string or a number. There are two problems with this:

  1. In this case you're trying to pass "a" as an object, when you should really be passing it like a string.
  2. with(obj, ls())) will return to the functions environment (function scoping) and not the screen unless you tell it to print.

What you want is more like:

foo <- function(obj, col) {
       print(with(obj, ls()))
       with(obj, print(obj[[col]]))
    }

foo(df, "a")

Or if you're only looking for the one column to be printed:

foo <- function(obj, col) {
           with(obj, print(obj[[col]]))
        }

foo(df, "a")
like image 38
Brandon Bertelsen Avatar answered Oct 20 '22 03:10

Brandon Bertelsen