<- NULL 在列表上的行为与用于删除数据的 data.frames 的行为
Posted
技术标签:
【中文标题】<- NULL 在列表上的行为与用于删除数据的 data.frames 的行为【英文标题】:Behavior of <- NULL on lists versus data.frames for removing data 【发布时间】:2013-10-26 10:02:56 【问题描述】:许多 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
值?或者我们是否依赖于反复试验?
【问题讨论】:
我注意到这并不是专门用 NULL 替换 data.frame/list 中的多个项目:cars1$mpg = NULL
与 cars3$mpg = NULL
也可以证明这个问题
我注意到 `[data.frames 有自己的分配方法。
@AriB.Friedman,谢谢。我也会更仔细地研究该代码。
【参考方案1】:
免责声明:这是一个相对较长的答案,不是很清楚,也不是很有趣,所以请随意跳过它或只阅读(某种)结论。
我尝试了一些追踪
[<-.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,则条件为真m==0
,函数停止。
请注意,每个长度为 0 的 value
都会出现此问题。例如,
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
替换为1s的向量,cars1$disp
替换为2s的向量。
这里发生了一种“双重回收”:
首先,value
列表的长度必须小于或等于要替换的列数。如果它较少,则完成经典回收。
其次,对于value
列表中的每个元素,其长度必须等于、大于或者是要替换的行数的倍数。如果小于,则对每个列表元素进行另一次回收以匹配行数。如果超过,则会显示警告。
当 RHS 中的 value
是 list(NULL)
时,什么都不会发生,因为回收是不可能的(rep(NULL, 10)
始终是 NULL
)。但代码继续,最后每列被替换为NULL
,即被删除。
总结和(某种)结论
data.frame
和 list
的行为不同,因为数据帧有特定的限制,其中每个元素必须具有相同的长度。通过分配 NULL
删除几列失败不是因为 NULL
值本身,而是因为 NULL
的长度为 0。错误来自验证分配值的长度是否是要替换的元素数(行数 * 列数)。
处理多个列的value=NULL
的情况似乎并不困难(通过添加大约四行简单代码),但它需要将NULL
视为一种特殊情况。我无法确定它是否没有被处理,因为它会破坏函数实现的逻辑,或者因为它会产生我不知道的副作用。
【讨论】:
+1。这很很有趣,并且有点符合我的假设(特别是关于回收),但我并没有真正挖掘任何代码来查看发生了什么。以上是关于<- NULL 在列表上的行为与用于删除数据的 data.frames 的行为的主要内容,如果未能解决你的问题,请参考以下文章
sqlite 行 ID 与列表视图不匹配 - ANDROID
removeChild() 方法从子节点列表中删除某个节点。如删除成功,此方法可返回被删除的节点,如失败,则返回 NULL。