简体   繁体   English

R中函数内的函数

[英]Function within Function in R

Can you please explain to me why the code complains saying that Samdat is not found? 你可以向我解释为什么代码抱怨说没有找到Samdat

I am trying to switch between models, so I declared a function that contains these specific models and I just need to call this function as one of the argument in the get.f function where the resampling will change the structure for each design matrix in the model. 我试图在模型之间切换,所以我声明了一个包含这些特定模型的函数,我只需要将此函数作为get.f函数中的一个参数调用,其中重新采样将改变每个设计矩阵的结构。模型。 The code complains that Samdat is not found when it is found. 代码抱怨Samdat找不到时找不到。

Also, is there a way I can make the conditional statement if(Model == M1()) instead of having to create another argument M to set if(M==1) ? 另外,有没有办法我可以创建条件语句if(Model == M1())而不必创建另一个参数M来设置if(M==1)

Here is my code: 这是我的代码:

dat <-  cbind(Y=rnorm(20),rnorm(20),runif(20),rexp(20),rnorm(20),runif(20), rexp(20),rnorm(20),runif(20),rexp(20))
nam <- paste("v",1:9,sep="")
colnames(dat) <- c("Y",nam)

M1 <- function(){
    a1 = cbind(Samdat[,c(2:5,7,9)])
    b1 = cbind(Samdat[,c(2:4,6,8,7)])
    c1 = b1+a1
    list(a1=a1,b1=b1,c1=c1)}

M2 <- function(){
    a1= cbind(Samdat[,c(2:5,7,9)])+2
    b1= cbind(Samdat[,c(2:4,6,8,7)])+2
    c1 = a1+b1
    list(a1=a1,b1=b1,c1=c1)}

M3 <- function(){
    a1= cbind(Samdat[,c(2:5,7,9)])+8
    b1= cbind(Samdat[,c(2:4,6,8,7)])+8
    c1 = a1+b1
    list(a1=a1,b1=b1,c1=c1)}
#################################################################
get.f <- function(asim,Model,M){
    sse <-c()
    for(i in 1:asim){
        set.seed(i)
        Samdat <- dat[sample(1:nrow(dat),nrow(dat),replace=T),]
        Y <- Samdat[,1]
        if(M==1){
            a2 <- Model$a1
            b2 <- Model$b1
            c2 <- Model$c1
            s<- a2+b2+c2
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            coff <-Model$cof
            sse <-c(sse,coff)
        }
        else if(M==2){
            a2 <- Model$a1
            b2 <- Model$b1
            c2 <- Model$c1
            s<- c2+12
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            coff <-Model$cof
            sse <-c(sse,coff)
        }
        else {
            a2 <- Model$a1
            b2 <- Model$b1
            c2 <- Model$c1
            s<- c2+a2
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            coff <- Model$cof
            sse <-c(sse,coff)
        }
    }
    return(sse)
}

get.f(10,Model=M1(),M=1)
get.f(10,Model=M2(),M=2)
get.f(10,Model=M3(),M=3)

You might want to have a look at the R scoping rules . 您可能想要查看R范围规则 In particular, there's no reason to expect that variables you define in a function are visible in other functions. 特别是,没有理由期望在函数中定义的变量在其他函数中可见。

You might be confused because the global environment (ie the top-level outside all functions) is an exception from this rule. 您可能会感到困惑,因为全局环境(即所有函数外的顶层)是此规则的一个例外。 I'm not going to go into your other questions, but let me note that the entire script looks very messed up to me - ie M1 to M3 are essentially one function, and the wad of copy/paste in get.f is definitely terrible. 我不打算讨论你的其他问题,但让我注意到整个脚本看起来很混乱 - 即M1M3基本上是一个功能,而get.f的复制/粘贴一get.f绝对是可怕的。 Whatever it is that you're trying to do can definitely be written in a less convoluted way. 无论你想做什么,都可以用一种不太复杂的方式写出来。

Let's have a look at the M s first - why not one function with a parameter? 让我们先看一下M s - 为什么不带一个带参数的函数? Including the solution to your scope problem, that makes two parameters - 包括您的范围问题的解决方案,这使得两个参数 -

M <- function(sampleData, offset) { 
    a1 = sampleData[,c(2:5,7,9)] + offset
    b1 = sampleData[,c(2:4,6,8,7)] + offset
    c1 = b1+a1
    list(a1=a1,b1=b1,c1=c1)
}

If you insist on defining aliases, you can also do something like 如果你坚持定义别名,你也可以做类似的事情

M1 <- function(sampleData) M(sampleData, 0) 
M2 <- function(sampleData) M(sampleData, 2) 
M3 <- function(sampleData) M(sampleData, 8) 

This is already less repetitive, but ideally you want the computer to do the repetition for you ( DRY! ): 这已经不那么重复,但理想情况下你希望计算机为你做重复( 干! ):

offsets <- c(0,2,8)
Models <- sapply(offsets, FUN=function(offset) function(sampleData) M(sampleData, offset))

Looking at get.f , it's not quite clear what you're trying to do - you're trying to fit something and collect something from the results, but the part about Model$cof refers to an undefined variable (your Model just has a1 , b1 and c1 entries). 看看get.f ,你不是很清楚你想要做什么 - 你试图适应某些东西并从结果中收集一些东西,但是关于Model$cof的部分是指未定义的变量(你的Model只有a1b1c1条目)。 Assuming you want to actually collect cof and discarding the interim code, get.f probably looks like this: 假设您想要实际收集cof并丢弃临时代码, get.f可能如下所示:

M <- function(sampleData, offset) { 
    a1 = sampleData[,c(2:5,7,9)] + offset
    b1 = sampleData[,c(2:4,6,8,7)] + offset
    c1 = b1+a1
    list(a1=a1,b1=b1,c1=c1)
}

get.f <- function(asim,Model,M){
    sse <-c()
    for(i in 1:asim){
        set.seed(i)
        Samdat <- dat[sample(1:nrow(dat),nrow(dat),replace=T),]
        Y <- Samdat[,1]
        model <- Model()
        if(M==1){
            a2 <- model$a1
            b2 <- model$b1
            c2 <- model$c1
            s<- a2+b2+c2
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            sse <-c(sse,cof)
        }
        else if(M==2){
            a2 <- model$a1
            b2 <- model$b1
            c2 <- model$c1
            s<- c2+12
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            sse <-c(sse,cof)
        }
        else {
            a2 <- model$a1
            b2 <- model$b1
            c2 <- model$c1
            s<- c2+a2
            fit <- lm(Y~s)
            cof <- sum(summary(fit)$coef[,1])
            sse <-c(sse,cof)
        }
    }
    return(sse)
}


get.f(10,Model=M1,M=1) 
get.f(10,Model=M2,M=2)
get.f(10,Model=M3,M=3)

That's still terribly repetitive, so why don't we think about it for a minute? 那仍然非常重复,那么我们为什么不考虑一下呢? All you're doing with your samples is to calculate one column from them to use in your fit. 您对样品所做的一切就是从它们中计算出一列用于您的样品。 I don't see why you need to do the calculation in an M function and then do the extraction of the single value in get.f (dependent on which particular M you were using) - this seems indicative that the extraction should much rather be part of M... but if you need to keep them separate, okay, let's use separate extraction functions. 我不明白为什么你需要在M函数中进行计算,然后在get.f提取单个值(取决于你使用的是哪个特定的M) - 这似乎表明提取更应该是M的一部分...但是如果你需要将它们分开,那么,让我们使用单独的提取功能。 Still comes in under half of your code size in reasonably-written R: 在合理编写的R中仍然有一半的代码大小:

# Set up test data
dat <-  cbind(Y=rnorm(20),rnorm(20),runif(20),rexp(20),rnorm(20),runif(20), rexp(20),rnorm(20),runif(20),rexp(20))
nam <- paste("v",1:9,sep="")
colnames(dat) <- c("Y",nam)

# calculate a1..c1 from a sample
M <- function(sampleData, offset) { 
    a1 = sampleData[,c(2:5,7,9)] + offset
    b1 = sampleData[,c(2:4,6,8,7)] + offset
    c1 = b1+a1
    list(a1=a1,b1=b1,c1=c1)
}

# create a fixed-offset model from the variable offset model by fixing offset
makeModel <- function(offset) function(sampleData) M(sampleData, offset)   

# run model against asim subsamples of data and collect coefficients
get.f <- function(asim,model,expected) 
    sapply(1:asim,  function (i){
        set.seed(i)
        Samdat <- dat[sample(1:nrow(dat),nrow(dat),replace=T),]
        Y <- Samdat[,1]
        s <- expected(model(Samdat))
        fit <- lm(Y~s)
        sum(summary(fit)$coef[,1])
    })

# list of models to run and how to extract the expectation values from the model reslts
todo <- list(
        list(model=makeModel(0), expected=function(data) data$a1+data$b1+data$c1),
        list(model=makeModel(2), expected=function(data) data$c1+12),
        list(model=makeModel(8), expected=function(data) data$c1+data$a1))

sapply(todo, function(l) { get.f(10, l$model, l$expected)})

When you call 你打电话时

get.f(10, Model=M1(), M=1)

your M1 function is immediately called. 你的M1功能立即被调用。 It dies because inside the body of M1 you are using Samdat which is only defined later, in the body of get.f . 它死了,因为在M1的主体内你使用的是Samdat ,后者仅在get.f的主体中get.f

Somehow, you need to call M1 after Samdat is defined. 不知何故,你需要 Samdat定义调用M1 One way of doing that is to make M1 (the function) an argument to get.f and call the function from inside get.f : 一种方法是将M1 (函数)作为get.f的参数并从get.f调用函数:

get.f <- function(asim, Model.fun, M) {
   ...
   Sambat <- ...
   Model  <- Model.fun()
   ...
}
get.f(10, Model.fun = M1, M=1)

Also, in general, it is bad programming to use global variables, ie, make your function use variables that are defined outside their scope. 此外,一般来说,使用全局变量编程是不好的,即使您的函数使用在其范围之外定义的变量。 Instead, it is recommended that everything a function uses be passed as input arguments. 相反,建议将函数使用的所有内容作为输入参数传递。 You have two such cases in your code: M1 ( M2 , and M3 ) use Samdat and get.f uses dat . 您的代码中有两个这样的情况: M1M2M3 )使用Samdatget.f使用dat They should be arguments to your functions. 它们应该是你的功能的参数。 Here is a nicer version of your code. 这是一个更好的代码版本。 I have not fixed everything, so you'll have to do a little more to get it to work: 我没有修复所有内容,所以你必须多做一些才能让它工作:

M1 <- function(sampled.data) {
   a1 <- sampled.data[, c("v1", "v2", "v3", "v4", "v6", "v8")]
   b1 <- sampled.data[, c("v1", "v2", "v3", "v5", "v7", "v6")]
   c1 <- a1 + b1
   list(a1 = a1, b1 = b1, c1 = c1)
}

get.f <- function(dat, asim, Model.fun, offset, M) {
   sse <- c()
   for(i in 1:asim){
      set.seed(i)
      Samdat <- dat[sample(1:nrow(dat), nrow(dat), replace = TRUE), ]
      Y      <- Samdat[, "Y"]
      Model  <- Model.fun(sampled.data = Samdat)
      a2     <- Model$a1
      b2     <- Model$b1
      c2     <- Model$c1      
      s      <- switch(M, a2 + b2 + c2, c2 + 12, c2 + a2)
      fit    <- lm(Y ~ s)
      cof    <- sum(summary(fit)$coef[,1])
      coff   <- Model$cof        # there is a problem here...
      sse    <- c(sse, coff)     # this is not efficient
   }
   return(sse)
}

dat <- cbind(Y = rnorm(20), v1 = rnorm(20), v2 = runif(20), v3 = rexp(20),
                            v4 = rnorm(20), v5 = runif(20), v6 = rexp(20),
                            v7 = rnorm(20), v8 = runif(20), v9 = rexp(20))

get.f(dat, 10, Model.fun = M1, M = 1)

One last thing that jumps out: if the definition of s (what I gathered under switch() is related to the Model you use, then consider merging the definitions of Model and s together: add s to the list output of your M1 , M2 , M3 functions so that s can just be defined as s <- Model$s , and you can then drop the M input to get.f . 跳出来的最后一件事:如果s的定义(我在switch()下收集的内容与你使用的Model相关,那么考虑将Models的定义合并在一起:将s添加到M1M2的列表输出中, M3功能使s可以定义为s <- Model$s ,然后你可以将M输入删除到get.f

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM