使用字符串名称在功能上创建变量

时间:2014-10-17 18:31:53

标签: r string function variables

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

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

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

3 个答案:

答案 0 :(得分:5)

你错过了逗号。

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,否则返回具有给定名称的矩阵。出于某种原因,&#34; ^&#34;运营商是唯一未列出的运营商。我们可以很容易地证实这一点:

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"

使用exponention,使用取幂时,矩阵的dimnames会在分配时覆盖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 知道列的正确名称,但会显示您插入其中的矩阵的名称。

答案 1 :(得分:4)

我认为您只需要使用[[代替[。试试这个。

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

问题实际上在df[varname];这将返回一个包含原始列名的数据框,在添加时会保留该名称。使用[[或使用逗号指定您希望该列,如@jed建议的那样,将返回一个没有名称的向量。

答案 2 :(得分:2)

这个表达式:

df[varname]^2

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