问题 使用by-operator拆分data.table:返回数值和/或NAs的函数失败


我有一个 data.table 有两列:一列 ID 专栏和一个 value 柱。我想把桌子分开 ID 列并运行一个函数 foo 在...上 value 柱。这个工作正常 foo 没有返回NAs。在这种情况下,我收到一个错误,告诉我组的类型不一致。我的假设是 - 从那以后 is.logical(NA) 等于 TRUE 和 is.numeric(NA) 等于 FALSEdata.table 内部假设我想将逻辑值与数字值组合并返回错误。但是,我发现这种行为很特殊。对此有何评论?我是否会错过这里显而易见的事情或者确实是预期的行为?如果是这样,简短的解释会很棒。 (请注意,我确实知道一个解决办法:只是让 foo2 返回一个完全不可能的数字并在以后过滤。但是,这看起来很糟糕)。

这是一个例子:

library(data.table)
foo1 <- function(x) {if (mean(x) < 5) {return(1)} else {return(2)}}
foo2 <- function(x) {if (mean(x) < 5) {return(1)} else {return(NA)}}
DT <- data.table(ID=rep(c("A", "B"), each=5), value=1:10)
DT[, foo1(value), by=ID] #Works perfectly
     ID V1
[1,]  A  1
[2,]  B  2
DT[, foo2(value), by=ID] #Throws error
Error in `[.data.table`(DT, , foo2(value), by = ID) : 
columns of j don't evaluate to consistent types for each group: result for group 2 has column 1 type 'logical' but expecting type 'numeric'

7663
2017-10-31 23:01


起源



答案:


你可以通过指定你的函数应该返回一个来解决这个问题 NA_real_而不是一个 NA 默认类型。

foo2 <- function(x) {if (mean(x) < 5) {return(1)} else {return(NA)}}
DT[, foo2(value), by=ID] #Throws error
# Error in `[.data.table`(DT, , foo2(value), by = ID) : 
# columns of j don't evaluate to consistent types for each group: 
# result for group 2 has column 1 type 'logical' but expecting type 'numeric'

foo3 <- function(x) {if (mean(x) < 5) {return(1)} else {return(NA_real_)}}
DT[, foo3(value), by=ID] #Works
#      ID V1
# [1,]  A  1
# [2,]  B NA

顺便提一下那个消息 foo2() 在失败时提供的信息非常丰富。它基本上告诉你你的NA是错误的类型。要解决这个问题,你只需要寻找 NA 正确类型(或类)的常量:

NAs <- list(NA, NA_integer_, NA_real_, NA_character_, NA_complex_)
data.frame(contantName = sapply(NAs, deparse), 
           class       = sapply(NAs, class),
           type        = sapply(NAs, typeof))

#     contantName     class      type
# 1            NA   logical   logical
# 2   NA_integer_   integer   integer
# 3      NA_real_   numeric    double
# 4 NA_character_ character character
# 5   NA_complex_   complex   complex

11
2017-10-31 23:11



我工作的越多 R我越发现自己知道多少东西。这个'NA_real_'技巧绝对是其中之一。谢谢@Josh O'Brien,很棒的回答。 - Christoph_J
谢谢。我补充了一点 NA 我的答案常数,因为这对我来说经常有用,而且这是我的一个方面 NA 通常对用户不可见的值。这是应该的! - Josh O'Brien
如果你不提前知道课程怎么办?使用时,data.table对我来说一直是个大问题 by= 论据。你遇到过像这样的问题吗? - rbatt
@rbatt在这种情况下的解决方案(如果我正确理解你的问题)将确保函数返回的结果应用于每个 by 组总是属于同一类型。 这是一个可以帮助您了解我的意思的示例。向下滚动到我结束通话的部分 median(X) (有时产生一个 "integer" 有时一个 "numeric" 类对象)在调用中 as.double(),确保结果始终如一 "numeric"。 - Josh O'Brien
@JoshO'Brien Gotcha,谢谢。所以你会建议只使用一个 switch(class(x), double = as.numeric(...), character = as.character(...), ... 类型方法?对于某些功能,很明显它应该是特定类型。我正在使用一个函数 unique 在其核心,所以输出可以是任何类。我最终做了一个解决方法,所以我不必指定返回的NA类型(只允许 unique() 为我这样做)。但它仍然为我提出了问题。我可以创建一个更详细的新问题。 - rbatt


答案:


你可以通过指定你的函数应该返回一个来解决这个问题 NA_real_而不是一个 NA 默认类型。

foo2 <- function(x) {if (mean(x) < 5) {return(1)} else {return(NA)}}
DT[, foo2(value), by=ID] #Throws error
# Error in `[.data.table`(DT, , foo2(value), by = ID) : 
# columns of j don't evaluate to consistent types for each group: 
# result for group 2 has column 1 type 'logical' but expecting type 'numeric'

foo3 <- function(x) {if (mean(x) < 5) {return(1)} else {return(NA_real_)}}
DT[, foo3(value), by=ID] #Works
#      ID V1
# [1,]  A  1
# [2,]  B NA

顺便提一下那个消息 foo2() 在失败时提供的信息非常丰富。它基本上告诉你你的NA是错误的类型。要解决这个问题,你只需要寻找 NA 正确类型(或类)的常量:

NAs <- list(NA, NA_integer_, NA_real_, NA_character_, NA_complex_)
data.frame(contantName = sapply(NAs, deparse), 
           class       = sapply(NAs, class),
           type        = sapply(NAs, typeof))

#     contantName     class      type
# 1            NA   logical   logical
# 2   NA_integer_   integer   integer
# 3      NA_real_   numeric    double
# 4 NA_character_ character character
# 5   NA_complex_   complex   complex

11
2017-10-31 23:11



我工作的越多 R我越发现自己知道多少东西。这个'NA_real_'技巧绝对是其中之一。谢谢@Josh O'Brien,很棒的回答。 - Christoph_J
谢谢。我补充了一点 NA 我的答案常数,因为这对我来说经常有用,而且这是我的一个方面 NA 通常对用户不可见的值。这是应该的! - Josh O'Brien
如果你不提前知道课程怎么办?使用时,data.table对我来说一直是个大问题 by= 论据。你遇到过像这样的问题吗? - rbatt
@rbatt在这种情况下的解决方案(如果我正确理解你的问题)将确保函数返回的结果应用于每个 by 组总是属于同一类型。 这是一个可以帮助您了解我的意思的示例。向下滚动到我结束通话的部分 median(X) (有时产生一个 "integer" 有时一个 "numeric" 类对象)在调用中 as.double(),确保结果始终如一 "numeric"。 - Josh O'Brien
@JoshO'Brien Gotcha,谢谢。所以你会建议只使用一个 switch(class(x), double = as.numeric(...), character = as.character(...), ... 类型方法?对于某些功能,很明显它应该是特定类型。我正在使用一个函数 unique 在其核心,所以输出可以是任何类。我最终做了一个解决方法,所以我不必指定返回的NA类型(只允许 unique() 为我这样做)。但它仍然为我提出了问题。我可以创建一个更详细的新问题。 - rbatt