Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

r-studio: is there a "strict mode"?

Tags:

r

Say that instead of writing

sort(tapply(CPS$C == "S", CPS$M, sum, na.rm=TRUE))

I write

sort(tapply(CPS$C == "S", CPS$M, sum, rm.na=TRUE))

It seems that r-studio will happily run the function, ignoring the crucial parameter na.rm

That it ignores na.rm is expected (I did suply the function with the wrong parameter name). What I find surprising is that it runs at all.

Is there any way to convince R/R-studio to throw some sort of error, showing me that I made a typo, instead of running the function?

like image 880
josinalvo Avatar asked Sep 22 '20 00:09

josinalvo


2 Answers

There is no "strict" mode, especially for functions that use ellipses to pass parameters through to other functions. Here sum doesn't care about parameter names. For example

sum(1:4, whatever=4)
sum(1:4, 4)

run just fine and both return the same value. Rather than using such functions, you could write your own wrapper functions that more aggressively check parameter values. Here's a "safe" version of sum

safe_sum <- function(...) {
  cx <- match.call()
  cn <- names(cx)[-1]
  if (!is.null(cn)) {
    bad_names <- cn[cn!="" & cn!="na.rm"]
    if(length(bad_names)) {
      stop(paste("unexpected named parameter:", paste(bad_names, collapse=", ")))
    }
  }
  cx[[1]] <- quote(sum)
  eval(cx)
}

safe_sum(1:5, rm.na=TRUE)
# Error in safe_sum(1:5, rm.na = TRUE) : unexpected named parameter: rm.na

Which you can use with tapply

set.seed(10)
CPS <- data.frame(
  C = sample(c("S","T"), 50, replace=TRUE),
  M = sample(LETTERS[1:4], 50, replace=TRUE)
)
sort(tapply(CPS$C == "S", CPS$M, safe_sum , na.rm=TRUE))
# D A B C 
# 3 5 8 9 
sort(tapply(CPS$C == "S", CPS$M, safe_sum , rm.na=TRUE))
# Error in FUN(X[[i]], ...) : unexpected named parameter: rm.na 

If you wanted to do this with other functions, you could create a factory to check all parameter names. Here's such a function

check_match_names <- function(fun) {
  args <- formalArgs(args(sum))
  args <- args[args!="..."]
  function(...) {
    cx <- match.call()
    cn <- names(cx)[-1]
    if (!is.null(cn)) {
      bad_names <- cn[cn!="" & !(cn %in% args)]
      if(length(bad_names)) {
        stop(paste("unexpected named parameter:", paste(bad_names, collapse=", ")))
      }
    }
    cx[[1]] <- fun
    eval.parent(cx)
  }
}

And then you would create your function with

safe_sum <- check_match_names(sum)
like image 57
MrFlick Avatar answered Oct 11 '22 09:10

MrFlick


You can also use the package {ellipsis} to make a safe_sum function :

safe_sum <- function(..., na.rm = FALSE) {
  ellipsis::check_dots_unnamed() 
  sum(..., na.rm)
}

CPS<- data.frame(C = NA ,M = "A")
sort(tapply(CPS$C == "S", CPS$M, safe_sum, rm.na=TRUE))
#> Error: 1 components of `...` had unexpected names.
#> 
#> We detected these problematic arguments:
#> * `rm.na`
#> 
#> Did you misspecify an argument?

Created on 2020-09-28 by the reprex package (v0.3.0)

like image 30
Moody_Mudskipper Avatar answered Oct 11 '22 08:10

Moody_Mudskipper