【问题标题】:Functionally creating variables using string names使用字符串名称在功能上创建变量
【发布时间】:2014-12-13 09:45:10
【问题描述】:

我正在尝试生成一个函数来在具有相同命名约定并使用相同逻辑的数据框上创建一堆列。不幸的是,我在创建变量时遇到了一些奇怪的行为,我希望其他人可以解释这里发生了什么。

df <- data.frame(var1 = c(1,2,3), var2 = c(3,4,5), var3 = c("foo", "bar", "baz"))

DoesNotWork <- function(df, varname){
  df[paste(varname, "_square", sep = "")] <- df[varname]^2
  return(df)
}

dfBad <- DoesNotWork(df, "var1")

dfBad
      var1 var2 var3 var1
  1    1    3  foo    1
  2    2    4  bar    4
  3    3    5  baz    9

dfBad 这里有两个变量var1,而不是我希望的一个变量var1 和一个变量var1_squared

下面的函数通过将原始变量的所有值分配给新变量名来解决这个问题,然后只对新变量执行相同的操作,但这有点令人讨厌,我不确定如果我需要使用来自多个变量的逻辑会发生什么。

Works <- function(df, varname){
   df[paste(varname, "_square", sep = "")] <- df[varname]
   df[paste(varname, "_square", sep = "")] <- df[paste(varname, "_square", sep = "")]^2
   return(df)
}

dfGood <- Works(df, "var1")

dfGood
      var1 var2 var3 var1_square
  1    1    3  foo           1
  2    2    4  bar           4
  3    3    5  baz           9

这里的任何指导都将不胜感激,特别是如果有更好的方法可以在变量名的字符串和对列对象的引用之间进行切换。

【问题讨论】:

    标签: r string function variables


    【解决方案1】:

    您缺少逗号。

    df <- data.frame(var1 = c(1,2,3), var2 = c(3,4,5), var3 = c("foo", "bar", "baz"))
    
    NowItWorks <- function(df, varname){
      df[,paste(varname, "_square", sep = "")] <- df[,varname]^2
      return(df)
    }
    
    NowItWorks(df, "var1")
    
    >  var1 var2 var3 var1_square
     1    1    3  foo           1
     2    2    4  bar           4
     3    3    5  baz           9
    

    编辑:好的,所以我上面的答案确实有效,但它并没有真正回答为什么第二个有效的问题。

    例如:

    MultiplicationWorks <- function(df, varname){
      df[paste(varname, "_square", sep = "")] <- df[varname]*2
      return(df)
    }
    

    与所有其他非指数运算符一样。如果我们查看 data.frame Operators 源代码,我们会在底部看到这个有趣的部分:

    Ops.data.frame
    
    ...
    if (.Generic %in% c("+", "-", "*", "/", "%%", "%/%")) {
        names(value) <- cn
        data.frame(value, row.names = rn, check.names = FALSE,
            check.rows = FALSE)
    }
    else matrix(unlist(value, recursive = FALSE, use.names = FALSE),
        nrow = nr, dimnames = list(rn, cn))
    ...
    

    基本上这是说,如果运算符是列出的运算符之一,则返回具有给定名称的 data.frame,否则返回具有给定名称的矩阵。出于某种原因,“^”运算符是唯一未列出的运算符。我们可以很容易地确认这一点:

    df <- data.frame(var1 = c(1,2,3), var2 = c(3,4,5), var3 = c("foo", "bar", "baz"))
    
    class(df["var1"]*2)
    
    >[1] "data.frame"
    
    class(df["var1"]^2)
    
    >[1] "matrix"
    

    使用指数,并且 使用指数,当您分配它时,矩阵的暗名称会覆盖 data.frame 的新列名称。 R 很奇怪。有趣的是,这意味着您还可以通过将 as.data.frame() 包裹在取幂部分周围来使代码正常工作。

    如果您想使用初始函数查看真正奇怪的东西:

    ❥ names(dfBad)
    [1] "var1"        "var2"        "var3"        "var1_square"
    ❥ dfBad
      var1 var2 var3 var1
    1    1    3  foo    1
    2    2    4  bar    4
    3    3    5  baz    9
    ❥ str(dfBad)
    'data.frame':   3 obs. of  4 variables:
     $ var1       : num  1 2 3
     $ var2       : num  3 4 5
     $ var3       : Factor w/ 3 levels "bar","baz","foo": 3 1 2
     $ var1_square: num [1:3, 1] 1 4 9
      ..- attr(*, "dimnames")=List of 2
      .. ..$ : NULL
      .. ..$ : chr "var1"
    

    R 知道列的正确名称,但会显示您插入其中的矩阵的名称。

    【讨论】:

      【解决方案2】:

      我认为您只需要使用[[ 而不是[。试试这个。

      ThisWorks <- function(df, varname){
        df[[paste(varname, "_square", sep = "")]] <- df[[varname]]^2
        return(df)
      }
      

      问题其实出在df[varname];这将返回一个具有原始列名的数据框,当您添加它时会保留该数据框。正如@jed 建议的那样,使用[[ 或使用逗号指定您想要该列将返回一个没有名称的向量。

      【讨论】:

      • 天哪。惊人的。为什么[[ 有效而[ 无效,有什么理由吗?
      • @TuringMachin [[ 用于选择列表的元素[ 用于选择(或切片)子列表
      【解决方案3】:

      这个表达式:

      df[varname]^2
      

      使用旧名称带来列表,现在看起来 R 可以选择 - 使用哪个名称。由于您将新创建的列表 df[new_name] 替换为另一个列表 - 名称 get 也将被替换。

      【讨论】:

        猜你喜欢
        • 2013-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        相关资源
        最近更新 更多