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

时间:2013-10-17 18:49:34

标签: r dataframe

许多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.framelist,为什么在这种情况下它的表现如此不同?有一种万无一失的方法可以知道什么时候会丢弃一个元素,什么时候会产生错误,什么时候会给它一个NULL值呢?或者,我们是否依赖于反复试验?

1 个答案:

答案 0 :(得分:8)

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

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

案例1: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是列数,mvalue的长度。如果条件为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

相同的方式失败

案例2:value是一个列表

如果value是一个列表,那么它将用于同时替换多个列。例如:

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

会将cars1$mpg替换为1s的向量,将cars1$disp替换为2s的向量。

这里有一种“双重回收”:

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

当RHS中的valuelist(NULL)时,实际上没有任何事情发生,因为无法进行回收(rep(NULL, 10)始终是NULL)。但代码仍在继续,最后每个要替换的列都被分配NULL,即被删除。

摘要和(某种)结论

由于对数据帧的特定约束,

data.framelist的行为不同,其中每个元素的长度必须相同。通过分配NULL删除多个列失败不是因为NULL值本身,而是因为NULL的长度为0.错误来自测试,该测试验证分配的长度value是要替换的元素数量的倍数(行数*列数)。

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