不确定我是否正确标题,因为我不完全理解以下行为的原因:
dfSet <- data.frame(ID = sample(1:15, size = 15, replace = FALSE), va1 = NA, va3 = 0, stringsAsFactors = FALSE)
dfSet[1:10, ]$va1 <- 'o1'
dfSet[11:15, ]$va1 <- 'o2'
dfSet[dfSet$ID %in% sample(dfSet[dfSet$va1 == 'o1', ]$ID, 7, replace = FALSE), ]$va3 <- 1
print(length(unique(dfSet$ID)))
我希望最终的印刷品显示15,但它没有。而是出现13或14并且dfSet被修改,至少有两行具有相同的ID。看来这部分代码:
dfSet[dfSet$ID %in% sample(dfSet[dfSet$va1 == 'o1', ]$ID, 7, replace = FALSE), ]$va3 <- 1
修改$ ID列 - 我不知道为什么?
解决方法:
temp <- sample(dfSet[dfSet$va1 == 'o1', ]$ID, 7, replace = FALSE)
dfSet[dfSet$ID %in% temp, ]$va3 <- 1
在这种情况下,一切都按预期工作 - 有15行具有唯一ID。
问题是为什么在%in%中直接使用样本会修改数据框?
答案 0 :(得分:7)
似乎问题在于,当您分配函数返回值时,R会做一些棘手的事情。例如,像
a <- c(1,3)
names(a) <- c("one", "three")
在大多数语言中,看起来很奇怪。如何为函数的返回值赋值?真正发生的是有一个名为names<-
的函数被定义。基本上,它返回原始对象的转换版本,然后可以用来替换传递给该函数的值。所以看起来真的像这样
.temp. <- `names<-`(a, c("one","three"))
a <- .temp.
变量a
总是被完全替换,而不仅仅是它的名称。
当您执行类似
的操作时dfSet$a<-1
再次真正发生的是
.temp. <- "$<-"(dfSet, a, 1)
dfSet <- .temp.
现在,当您尝试同时执行[]
和$
子集时,事情会变得有点棘手。看看这个样本
#for subsetting
f <- function(x,v) {print("testing"); x==v}
x <- rep(0:1, length.out=nrow(dfSet))
dfSet$a <- 0
dfSet[f(x,1),]$a<-1
注意&#34;测试&#34;打印两次。发生的事情更像是
.temp1. <- "$<-"(dfSet[f(x,1),], a, 1)
.temp2. <- "[<-"(dfSet, f(x,1), , .temp1.)
dfSet <- .temp2.
因此f(x,1)
被评估两次。这意味着sample
也将被评估两次。
如果您尝试替换尚不存在的变量
,则错误更明显一些dfSet[f(x,1),]$b<-1
# Warning message:
# In `[<-.data.frame`(`*tmp*`, f(x, 1), , value = list(ID = c(6L, :
# provided 4 variables to replace 3 variables
您在此处收到警告,因为.temp1.
变量添加了列,现在有4列但是当您尝试对.temp2.
进行分配时,您现在遇到的问题是数据切片您要替换的框架大小不同。
由于$<-
运算符不会返回新列,因此会替换ID,它会返回一个新的data.frame,并将列更新为您指定的任何值。这意味着更新的行将与分配发生时的ID一起返回。这保存在.temp1.
变量中。然后,当您执行[<-
分配时,您将选择要换出的新行集。这些行的所有列的值将替换为.temp1.
中的值。这意味着您将覆盖替换行的ID,它们可能会有所不同,因此您最终可能会获得给定ID的两个或更多副本。
答案 1 :(得分:2)
虽然我不是100%肯定,但我怀疑R正在运行sample
两次。在R中进行子集化和赋值时,例如:
x[i:j,]$v1 <- 1
它被评估为“从x中取出行i到j作为临时数据帧,将1分配给该数据帧的v1列,然后将临时数据帧复制回x中的行i到j”。
所以索引表达式(i:j)可能会执行两次(一次提取,一次放回),如果它是一个随机变量,它会将结果放回到与最初选择的行不同的行中
答案 2 :(得分:1)
考虑这个更简单的例子:
x <- data.frame(a=1:10, b=10:1)
x$b <- 5
第二行实际上是做什么
x <- `$<-`(x, 'b', 5)
你可以看到$<-
只是一个带三个参数的函数,一个
对象,名称和值。 (注意,如果要直接使用$<-
,则必须使用反引号。)
我认为问题在于你的例子x
是一个表达式
由于呼叫,每次评估时评估不同的事物
sample
,所以你应该避免这种情况。
另一种方法是使用显然没有此问题的[<-
:
dfSet[dfSet$ID %in% sample(dfSet[dfSet$va1 == 'o1', ]$ID, 7, replace = FALSE), 'va3'] <- 1