我正在尝试生成一个函数,在数据框上创建一堆具有相同命名约定并使用相同逻辑的列。不幸的是,在创建变量时我遇到了一些奇怪的行为,我希望其他人可以解释这里发生了什么。
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
这里的任何指导都将非常感激,特别是如果有更好的方法在字符串之间切换变量名和引用列对象。
答案 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也将被替换。