【问题标题】:One-liner to create a data.frame and have columns depend on each other单线创建 data.frame 并让列相互依赖
【发布时间】:2012-12-06 03:14:02
【问题描述】:

生成 data.frames(尤其是对于不可重现的 SO 问题)的一种不常见的情况是,当一列依赖于另一列的(通常是随机的)值时。例如,如果想要一个 data.frame 来测试回归,最好有一些嘈杂的线性依赖:

n <- 100
x <- runif(n)
dat <- data.frame( x=x, y=x+runif(n) )
plot(y~x,data=dat)

但是,我想在一行中完成(上面将计为两行,第一行创建 x,第二行在 data.frame 分配中使用 x),理想情况下不在全局环境中存放任何东西。

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    这是within 的简单解决方案:

    within(data.frame(x = runif(n)), y <- x + runif(n))
    

    此命令y 分配给全局环境(或父框架)。

    【讨论】:

    • 很多有创意的答案,但我认为这个和@WojciechSobala 最接近我对世界的看法。
    【解决方案2】:

    这是一个解决方案,它不会受到您发现的两个缺点的影响。

    library(data.table)
    n <- 100
    
    dat <- data.table(x = runif(n))[, y := x + runif(n)]
    

    它自己的缺点是:

    1. 需要加载整个包。
    2. 变得有点丑陋(即data.frame(data.table(......)),如果你想返回一个“普通的”data.frame

    【讨论】:

      【解决方案3】:

      这是一种不违反许多程序员喜欢坚持的“每行一个分配”规则的方法:

      within(data.frame(row.names = 1:n), {x = runif(n); y = x + runif(n)})
      

      其中data.frame(row.names = 1:n) 用于创建具有正确行数的空data.frame,否则within 会报错。

      【讨论】:

      • 此解决方案依赖于已创建的x。否则,R 会抱怨。
      • @SvenHohenstein,你是对的,我错过了。我用没有问题的类似within 版本替换了transform
      • plyr::mutate 的工作方式与转换相同,只是您可以引用刚刚创建的列。
      【解决方案4】:

      set.seed 将需要使随机数在任何情况下都可以重现,但可以尝试以下模数:

      set.seed(123)
      dat <- transform(data.frame(x = runif(10)), y = x + runif(10))
      

      这给出了:

      > dat
                 x         y
      1  0.2875775 1.2444109
      2  0.7883051 1.2416393
      3  0.4089769 1.0865476
      4  0.8830174 1.4556508
      5  0.9404673 1.0433920
      6  0.0455565 0.9453815
      7  0.5281055 0.7741932
      8  0.8924190 0.9344786
      9  0.5514350 0.8793557
      10 0.4566147 1.4111184
      

      【讨论】:

        【解决方案5】:

        这是一个与transform(或plyr::mutate)类似的自定义函数,但不需要初始数据框。 (显然这对 OP 的问题没有真正的帮助,因为没有人会拥有这个功能,但我认为其他人可能会感兴趣)

        create <- function(...) {
          .data <- list()
        
          cols <- as.list(substitute(list(...))[-1])
          cols <- cols[names(cols) != ""] # Silently drop unnamed columns
        
          for(col in names(cols)) {
            .data[[col]] <- eval(cols[[col]], .data, parent.frame())
          }
          as.data.frame(.data)
        }
        create(x = runif(1:10), y = x + 1)
        

        【讨论】:

          【解决方案6】:

          这是我想出的最好的。它使用 R 初学者的常见错误作为编写更紧凑代码的技巧。

          dat <- data.frame( x<-runif(n), y=x+runif(n) )
          

          这在精神上类似于@Tommy's tip on CodeGolf.SE

          缺点是:

          1. 可能令人困惑。尤其如此,因为这是一个常见的错误,在这种情况下,专家代码审查员可能会将其与错误混淆。
          2. x 存放在父(全局,在大多数用例中)环境中,它可能会覆盖其他一些变量。

          编辑

          @WojciechSobala 在评论中的解决方案值得在这里强调。只需将上面的表达式包裹在local

          dat <- local( data.frame( x<-runif(n), y=x+runif(n) ) )
          

          由于local 的工作方式与evalq 一样(例如,它在给定环境中计算表达式),只是它默认通过new.env() 在新环境中计算,x 是在这个新环境中创建的,而不是在全球环境。

          【讨论】:

          • 您可以修改代码以消除缺点:dat
          猜你喜欢
          • 2015-08-09
          • 1970-01-01
          • 2020-01-16
          • 1970-01-01
          • 2013-09-02
          • 2015-02-15
          • 1970-01-01
          • 2017-10-12
          • 1970-01-01
          相关资源
          最近更新 更多