R - %in% 中使用的样本修改正在子集化的数据帧
R - sample used in %in% modify dataframe which is being subsetted
不确定我的问题标题是否正确,因为我不完全理解以下行为的原因:
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% 中直接使用样本会修改数据框?
虽然我不是 100% 确定,但我怀疑 R 是 运行 sample
的两倍。当您在 R 中进行子集化和赋值时,例如:
x[i:j,]$v1 <- 1
它被评估为 "take out rows i to j from x as a temporary data frame, assign 1 to the v1 column of that data frame, then copy the temporary data frame back into rows i to j in x"。
所以也许索引表达式 (i:j) 被执行了两次(一次提取,一次放回),如果它是一个随机变量,它将把结果放回到与之前不同的行中最初选择。
考虑这个更简单的例子:
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
问题似乎在于,当您为函数 return 值赋值时,R 做了一些棘手的事情。例如,
a <- c(1,3)
names(a) <- c("one", "three")
在大多数语言中看起来很奇怪。如何为函数的 return 值赋值?真正发生的是定义了一个名为 names<-
的函数。基本上这是 returning 原始对象的转换版本,然后可用于替换传递给该函数的值。所以它真的看起来像这样
.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
注意 "testing" 是如何打印两次的。怎么回事真的更像
.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 被替换,因为 $<-
运算符不只是 return 一个新列,它 return 是一个新的 data.frame,该列已更新为任何值你分配的。这意味着更新的行与分配发生时的 ID 一起 returned。这保存在 .temp1.
变量中。然后,当您执行 [<-
分配时,您正在选择一组新的行来换出。这些行的所有列的值都替换为 .temp1.
中的值。这意味着您将覆盖替换行的 ID,并且它们可能不同,因此您可能会得到给定 ID 的两个或更多副本。
不确定我的问题标题是否正确,因为我不完全理解以下行为的原因:
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% 中直接使用样本会修改数据框?
虽然我不是 100% 确定,但我怀疑 R 是 运行 sample
的两倍。当您在 R 中进行子集化和赋值时,例如:
x[i:j,]$v1 <- 1
它被评估为 "take out rows i to j from x as a temporary data frame, assign 1 to the v1 column of that data frame, then copy the temporary data frame back into rows i to j in x"。
所以也许索引表达式 (i:j) 被执行了两次(一次提取,一次放回),如果它是一个随机变量,它将把结果放回到与之前不同的行中最初选择。
考虑这个更简单的例子:
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
问题似乎在于,当您为函数 return 值赋值时,R 做了一些棘手的事情。例如,
a <- c(1,3)
names(a) <- c("one", "three")
在大多数语言中看起来很奇怪。如何为函数的 return 值赋值?真正发生的是定义了一个名为 names<-
的函数。基本上这是 returning 原始对象的转换版本,然后可用于替换传递给该函数的值。所以它真的看起来像这样
.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
注意 "testing" 是如何打印两次的。怎么回事真的更像
.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 被替换,因为 $<-
运算符不只是 return 一个新列,它 return 是一个新的 data.frame,该列已更新为任何值你分配的。这意味着更新的行与分配发生时的 ID 一起 returned。这保存在 .temp1.
变量中。然后,当您执行 [<-
分配时,您正在选择一组新的行来换出。这些行的所有列的值都替换为 .temp1.
中的值。这意味着您将覆盖替换行的 ID,并且它们可能不同,因此您可能会得到给定 ID 的两个或更多副本。