列表上的< - NULL行为与用于删除数据的data.frames行为

A5C*_*2T1 14 r dataframe

许多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值呢?或者我们依赖于反复试验?

jub*_*uba 8

免责声明:这是一个比较长的答案,不是很清楚,而且不是很有趣,可以随意跳过或只读的(那种)的结论.

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

案例1:value不是列表

然后它被认为是一个向量.矩阵和数组被视为一个向量,如帮助页面所示:

注意,当替换值是一个数组(包括矩阵)这是视为一系列的列(如"data.frame"和"as.data.frame"做),但插入作为一列.

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

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

确切的测试如下:

(m < n * p && (m == 0L || (n * p)%%m))
Run Code Online (Sandbox Code Playgroud)

np数,列数和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

案例2: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作为一种特殊情况.我无法确定它是否被处理,因为它会破坏函数实现的逻辑,或者因为它会产生我不知道的副作用.

  • +1.它*有趣并且有点像我假设的那样(特别是关于回收),但我没有真正挖掘任何代码来看看发生了什么. (2认同)