【发布时间】:2018-07-06 08:20:12
【问题描述】:
我正在查找组内的最短日期。很多时候,该组只包含缺失的日期(在这种情况下,我希望分配像 NA 这样的东西)。
NAs 似乎已正确分配,但它们没有像我预期的那样响应 is.na()。 当单元格显示为NA 时,is.na() 输出意外为 FALSE。
library(magrittr)
ds_visit <- tibble::tribble(
~subject_id, ~date,
1L, as.Date("2017-01-01" ),
1L, as.Date("2017-02-01" ),
2L, as.Date(NA_character_),
2L, as.Date("2017-01-02" ),
3L, as.Date(NA_character_),
3L, as.Date(NA_character_),
4L, as.Date(NA_character_),
4L, as.Date(NA_character_)
)
ds_subject <- ds_visit %>%
# as.data.frame() %>%
dplyr::group_by(subject_id) %>%
dplyr::mutate(
date_na = is.na(date), # Works as expected
date_min = min(date, na.rm=T), # Works as expected
date_min_na = is.na(date_min) # Does NOT work as expected.
) %>%
dplyr::ungroup() # %>% as.data.frame()
ds_visit 看起来正确。 ds_subject 在我看来是正确的,除了最后一列。
ds_subject(最后一列的最后四行是意外的。)
# A tibble: 8 x 5
subject_id date date_na date_min date_min_na
<int> <date> <lgl> <date> <lgl>
1 1 2017-01-01 F 2017-01-01 F
2 1 2017-02-01 F 2017-01-01 F
3 2 NA T 2017-01-02 F
4 2 2017-01-02 F 2017-01-02 F
5 3 NA T NA F # Should be 'T'?
6 3 NA T NA F # Should be 'T'?
7 4 NA T NA F # Should be 'T'?
8 4 NA T NA F # Should be 'T'?
我抖动了几个维度都没有成功,包括:(a) OS,(b) R 版本(包括3.4.3 patched),
(c) dplyr & rlang 版本(包括 CRAN 和 GitHub 版本),以及 (d) tibble 与 data.frame。作为临时解决方法(此处未显示),我在找到最小值之前将日期转换为字符,然后再转换回日期。
警告消息(从主题 3 和 4 生成):即使警告消息表明返回了 Inf,但在打印数据集时会出现 NA。 (此行为与min(as.Date(NA), na.rm=T)一致)。
1: In min.default(c(NA_real_, NA_real_), na.rm = TRUE) :
no non-missing arguments to min; returning Inf
2: In min.default(c(NA_real_, NA_real_), na.rm = TRUE) :
no non-missing arguments to min; returning Inf
对日期列的进一步检查似乎与上面的数据集视图一致。类型是日期,最后四个单元格是NA,而不是无穷大。
> str(ds_subject$date_min)
Date[1:8], format: "2017-01-01" "2017-01-01" "2017-01-02" "2017-01-02" NA NA NA NA
这是一个错误,还是我误用了什么?这个相关的NA 是在产生而不是无穷大吗?
编辑 1
@eipi10 和@mtoto 下面的链接帮助我更好地理解。谢谢。我对打印出“NA”而不是“Inf”并不感到兴奋,但我会尽量记住这一点。
针对这种特定场景,有没有比base::min()更好的功能?
我想要一个可以包含在 dplyr::mutate()/dplyr::summarize() 子句中的函数,其行为类似于 SQL。 (当 summarize() 替换 mutate() 时,最初的 dplyr 示例仍然存在 is.na() 问题。
例如:
"
SELECT
subject_id,
MIN(date) AS date_min
--MIN(date) OVER (PARTITION BY subject_id) AS date_min --`OVER` not supported by sqlite
FROM ds_visit
GROUP BY subject_id
" %>%
sqldf::sqldf() %>%
tibble::as_tibble() %>%
dplyr::mutate(
# date_min_na_1 = is.na(date_min), #Before conversion back to date (from numeric); same result as below.
date_min = as.Date(date_min, "1970-01-01"),
date_min_na = is.na(date_min)
)
缺失组具有良好的 NA 值并按预期响应 is.na() 的结果:
# A tibble: 4 x 3
subject_id date_min date_min_na
<int> <date> <lgl>
1 1 2017-01-01 F
2 2 2017-01-02 F
3 3 NA T
4 4 NA T
编辑 2
我看到这个问题被标记为与R Inf when it has class Date is printing NA 重复。我看到了很多重叠(我从那个问题中学到了很多东西,以及它对我的初始代码有什么问题),但我相信它们是不同的问题。
此问题涉及分组,并在不存在非缺失值时返回 NA。我对base::min() 不感兴趣。如上所述,理想情况下,完全避免使用 base::min(),以支持行为更像 SQL 的已建立且经过测试的函数/方法。
(虽然我很感谢@alistaire 对base:min() 的包装,如果不存在已建立的功能/方法,我会使用它。)
【问题讨论】:
-
这很奇怪。如果您尝试
date_min == Inf,它会为最后四行返回TRUE。如果它确实按照它所暗示的那样将Inf放入那些值中,那很好,但它显示为NA。一些奇怪的事情正在发生。 -
更简单的表示:
x <- Sys.Date() + Inf; x; is.na(x); x == Inf -
另一个例子,这里展示了
min函数发生了什么:min(as.Date(NA_character_), na.rm=TRUE); as.numeric(min(as.Date(NA_character_), na.rm=TRUE))。 -
这五个 cmets 链接帮助我理解了。我已添加到问题中。