R - 在%in%中使用的样本修改正在进行子集化的数据帧

时间:2016-08-03 19:55:40

标签: r subset sample

不确定我是否正确标题,因为我不完全理解以下行为的原因:

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%中直接使用样本会修改数据框?

3 个答案:

答案 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