许多R用户最终想出了很多方法来从他们的数据中删除元素。一种方法是使用NULL
,特别是当您想要执行某项操作时,例如从data.frame
删除列或从list
删除元素。
最终,用户遇到他们希望同时从data.frame
删除多个列的情况,并且他们会在<- list(NULL)
上找到解决方案(因为使用<- NULL
会导致在一个错误)。
data.frame
是list
的一种特殊类型,因此想象从list
删除项目的方法并不算太难应与从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
切换到将相同的概念应用于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
我遇到的主要问题是,如果data.frame
是list
,为什么在这种情况下它的表现如此不同?有一种万无一失的方法可以知道什么时候会丢弃一个元素,什么时候会产生错误,什么时候会给它一个NULL
值呢?或者,我们是否依赖于反复试验?
答案 0 :(得分:8)
免责声明:这是一个相对较长的答案,不是很清楚,也不是很有趣,所以请随意跳过它或只阅读(某种)结论。
我尝试了一些跟踪
正如Ari B. Friedman所建议的[<-.data.frame
。调试从函数的第162行开始,其中有一个测试来确定value
(替换值参数)是否不是列表。
value
不是列表然后它被认为是一个向量。矩阵和数组被视为一个向量,如帮助页面所示:
注意当替换值是一个数组(包括一个矩阵)时 它不被视为一系列列(如'data.frame'和 'as.data.frame'do)但作为单个列插入。
如果在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,则条件为m==0
为TRUE,并且函数停止。
请注意,长度为0的每个value
都会出现问题。例如,
cars1[,c("mpg")] <- numeric(0)
有效,而:
cars1[,c("mpg","disp")] <- numeric(0)
以与cars1[,c("mpg","disp")] <- NULL
value
是一个列表如果value
是一个列表,那么它将用于同时替换多个列。例如:
cars1[,c("mpg","disp")] <- list(1,2)
会将cars1$mpg
替换为1s的向量,将cars1$disp
替换为2s的向量。
这里有一种“双重回收”:
value
列表的长度必须小于或等于要替换的列数。如果它更少,则完成经典回收。value
列表的每个元素,其长度必须等于,大于或等于要替换的行数的倍数。如果它更少,则为每个列表元素进行另一次回收以匹配行数。如果更多,则会显示警告。当RHS中的value
为list(NULL)
时,实际上没有任何事情发生,因为无法进行回收(rep(NULL, 10)
始终是NULL
)。但代码仍在继续,最后每个要替换的列都被分配NULL
,即被删除。
data.frame
和list
的行为不同,其中每个元素的长度必须相同。通过分配NULL
删除多个列失败不是因为NULL
值本身,而是因为NULL
的长度为0.错误来自测试,该测试验证分配的长度value是要替换的元素数量的倍数(行数*列数)。
为多列处理value=NULL
的情况似乎并不困难(通过添加大约四行简单代码),但它需要将NULL
视为一种特殊情况。我无法确定它是否被处理,因为它会破坏函数实现的逻辑,或者因为它会产生我不知道的副作用。