14

许多 R 用户最终想出了很多方法来从他们的数据中删除元素。一种方法是使用NULL,尤其是当您想做一些事情时,例如从 a 中删除一列data.frame或从 a 中删除一个元素list

最终,用户遇到了一种情况,他们想data.frame一次从 a 中删除几列,并且他们将其<- list(NULL)作为解决方案(因为使用<- NULL会导致错误)。

Adata.frame 的一种特殊类型list,因此不难想象从 a 中删除项目的方法list应该与从 a 中删除列相同data.frame。但是,它们会产生不同的结果,如下面的示例所示。

## Make some small data--two data.frames and two lists
cars1 <- cars2 <- head(mtcars)[1:4]
cars3 <- cars4 <- as.list(cars2)

## Demonstration that the `list(NULL)` approach works
cars1[c("mpg", "cyl")] <- list(NULL)
cars1
#                   disp  hp
# Mazda RX4          160 110
# Mazda RX4 Wag      160 110
# Datsun 710         108  93
# Hornet 4 Drive     258 110
# Hornet Sportabout  360 175
# Valiant            225 105

## Demonstration that simply using `NULL` does not work
cars2[c("mpg", "cyl")] <- NULL
# Error in `[<-.data.frame`(`*tmp*`, c("mpg", "cyl"), value = NULL) : 
#   replacement has 0 items, need 12

切换到将相同的概念应用于 a list,并比较行为上的差异。

## Does not fully drop the items, but sets them to `NULL`
cars3[c("mpg", "cyl")] <- list(NULL)
# $mpg
# NULL
# 
# $cyl
# NULL
# 
# $disp
# [1] 160 160 108 258 360 225
# 
# $hp
# [1] 110 110  93 110 175 105

## *Does* drop the `list` items while this would
##   have produced an error with a `data.frame`
cars4[c("mpg", "cyl")] <- NULL
# $disp
# [1] 160 160 108 258 360 225
# 
# $hp
# [1] 110 110  93 110 175 105

我的主要问题是,如果 adata.frame是 a list,为什么在这种情况下它的行为如此不同?是否有一种万无一失的方法可以知道何时删除一个元素,何时会产生错误以及何时会简单地给它一个NULL值?或者我们是否依赖于反复试验?

4

1 回答 1

8

免责声明:这是一个相对较长的答案,不是很清楚,也不是很有趣,所以请随意跳过它或只阅读(某种)结论。

[<-.data.frame正如 Ari B. Friedman 所建议的,我尝试了一些关于 的跟踪 。调试从函数的第 162 行开始,其中有一个测试来确定value(替换值参数)是否不是列表。

案例1:value不是列表

然后它被认为是一个向量。矩阵和数组被视为一个向量,就像帮助页面说的那样:

请注意,当替换值是一个数组(包括矩阵)时,它不会被视为一系列列(如“data.frame”和“as.data.frame”所做的那样),而是作为单列插入。

如果在 LHS 中只选择了数据框的一列,那么唯一的约束是要替换的行数必须等于或倍数length(value)。如果是这种情况,则在必要时value回收并转换为列表。rep如果length(value)==0,则没有回收(因为这是不可能的),并且value只是转换为列表。

如果在 LHS 中选择了数据框的几列,那么约束就有点复杂了:length(value)必须等于或者是要替换的元素总数的倍数,即行数 * 列数。

确切的测试如下:

(m < n * p && (m == 0L || (n * p)%%m))

其中n是行p数、列数和m长度value。如果条件为 FALSE,则将value其转换为n x p矩阵(必要时可循环使用),并将矩阵按列拆分为列表。

如果value为 NULL,则条件为 TRUE m==0,并且函数停止。请注意,每个value长度为 0 的都会出现问题。例如,

cars1[,c("mpg")] <- numeric(0)

有效,而:

cars1[,c("mpg","disp")] <- numeric(0)

以同样的方式失败cars1[,c("mpg","disp")] <- NULL

案例2:value是一个列表

如果value是列表,则用于同时替换多个列。例如 :

cars1[,c("mpg","disp")] <- list(1,2)

将替换cars1$mpg为 1scars1$disp的向量和 2s 的向量。

这里发生了一种“双重回收”:

  • 首先,value列表的长度必须小于或等于要替换的列数。如果它较少,则完成经典回收。
  • 其次,对于value列表的每个元素,它的长度必须等于、大于或者是要替换的行数的倍数。如果小于,则对每个列表元素进行另一次回收以匹配行数。如果更多,则会显示警告。

valueRHS 中的 是list(NULL)时,什么都没有发生,因为回收是不可能的(rep(NULL, 10)总是NULL)。但代码继续,最后每列被替换NULL,即被删除。

总结和(某种)结论

data.frame并且list由于数据帧的特定约束而表现不同,其中每个元素必须具有相同的长度。通过分配删除几列NULL失败不是因为NULL值本身,而是因为NULL它的长度为 0。错误来自一个测试,该测试验证分配值的长度是否是要替换的元素数量的倍数(数量行 * 列数)。

处理value=NULL多列的情况似乎并不困难(通过添加大约四行简单的代码),但它需要考虑NULL作为一种特殊情况。我无法确定它是否没有被处理,因为它会破坏函数实现的逻辑,或者因为它会产生我不知道的副作用。

于 2013-10-17T21:52:01.827 回答