许多R用户最终想出了很多方法来从他们的数据中删除元素.一种方法是使用NULL
,特别是当你想要做一些事情,比如从a中删除一个列data.frame
或从一个元素中删除一个元素list
.
最终,用户遇到他们想要立即从一个列中删除多个列的data.frame
情况,并且他们将其<- list(NULL)
作为解决方案(因为使用<- NULL
将导致错误).
A data.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
Run Code Online (Sandbox Code Playgroud)
切换到将相同的概念应用于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
Run Code Online (Sandbox Code Playgroud)
我的主要问题是,如果a data.frame
是a list
,为什么它在这种情况下表现得如此不同?有一种万无一失的方法可以知道什么时候会丢弃一个元素,何时会产生错误,什么时候会给它一个NULL
值呢?或者我们依赖于反复试验?
免责声明:这是一个比较长的答案,不是很清楚,而且不是很有趣,可以随意跳过或只读的(那种)的结论.
我[<-.data.frame
按照Ari B. Friedman的建议尝试了一些追踪
.调试从函数的第162行开始,其中有一个测试来确定value
(替换值参数)是否不是列表.
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))
Run Code Online (Sandbox Code Playgroud)
n
行p
数,列数和m
长度在哪里value
.如果条件为FALSE,则将value
其转换为n x p
矩阵(如有必要,则进行回收),并将矩阵按列拆分为列表.
如果value
为NULL,则条件为TRUE m==0
,并且函数停止.请注意,每个value
长度为0 的问题都会出现.例如,
cars1[,c("mpg")] <- numeric(0)
Run Code Online (Sandbox Code Playgroud)
工作,而:
cars1[,c("mpg","disp")] <- numeric(0)
Run Code Online (Sandbox Code Playgroud)
以同样的方式失败 cars1[,c("mpg","disp")] <- NULL
value
是一个列表如果value
是列表,则它用于同时替换多个列.例如 :
cars1[,c("mpg","disp")] <- list(1,2)
Run Code Online (Sandbox Code Playgroud)
将替换cars1$mpg
为1s的向量,并cars1$disp
使用2s的向量.
这里有一种"双重回收":
value
列表的长度必须小于或等于要替换的列数.如果它更少,那么就完成了经典的回收.value
列表的每个元素,其长度必须等于,大于或等于要替换的行数的倍数.如果它更少,则为每个列表元素进行另一次回收以匹配行数.如果更多,则会显示警告.当value
在RHS是list(NULL)
,没有真的发生,因为回收是不可能的(rep(NULL, 10)
总是NULL
).但代码仍在继续,最后每个要替换的列都被分配NULL
,即被删除.
data.frame
并且list
由于对数据帧的特定约束而表现不同,其中每个元素必须具有相同的长度.通过分配删除多个列NULL
失败不是因为NULL
它本身的值,而是因为NULL
长度为0.错误来自一个测试,它验证指定值的长度是否是要替换的元素数量的倍数(行*列数).
处理value=NULL
多列的情况似乎并不困难(通过添加大约四行简单代码),但它需要考虑NULL
作为一种特殊情况.我无法确定它是否被处理,因为它会破坏函数实现的逻辑,或者因为它会产生我不知道的副作用.