【问题标题】:Using dplyr::group_by() to find min dates with NAs [duplicate]使用 dplyr::group_by() 查找具有 NA 的最小日期 [重复]
【发布时间】: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) tibbledata.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 &lt;- 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 链接帮助我理解了。我已添加到问题中。

标签: r date dplyr na


【解决方案1】:

问题是 minna.rm = TRUE 和 all-NA 值返回 Infmax 等效返回 -Inf),但 print.Date 没有显示这些值的方法,因此它会将其打印为NA,即使这不是存储的值。

min(NA, na.rm = TRUE)
#> Warning in min(NA, na.rm = TRUE): no non-missing arguments to min;
#> returning Inf
#> [1] Inf

x <- min(as.Date(NA), na.rm = TRUE)
#> Warning in min.default(structure(NA_real_, class = "Date"), na.rm = TRUE):
#> no non-missing arguments to min; returning Inf

x
#> [1] NA

is.na(x)
#> [1] FALSE

x == Inf
#> [1] TRUE

如果您愿意,您可以重新定义打印方法,以便打印您喜欢的任何内容,例如

print.Date <- function(x, ...){
    if(x == Inf | x == -Inf) {
        print(as.numeric(x))
    } else {
        base::print.Date(x, ...)
    }
}

x
#> [1] Inf

要真正得到你想要的结果,指定如果所有的值都是NA应该返回什么:

library(tidyverse)

ds_visit <- data_frame(subject_id = c(1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L), 
                       date = as.Date(c("2017-01-01", "2017-02-01", NA, "2017-01-02", NA, NA, NA, NA)))

ds_visit %>% 
    group_by(subject_id) %>% 
    summarise(date_min = if(all(is.na(date))) NA else min(date, na.rm = TRUE), 
              date_min_na = is.na(date_min))
#> # A tibble: 4 x 3
#>   subject_id date_min   date_min_na
#>        <int> <date>     <lgl>      
#> 1          1 2017-01-01 FALSE      
#> 2          2 2017-01-02 FALSE      
#> 3          3 NA         TRUE       
#> 4          4 NA         TRUE

它没有那么简洁,但其行为完全可以预测。

【讨论】:

  • 为什么这被否决了?它实现了我的目标(并且作为奖励,不会发出警告)。是否存在我没​​有意识到的危险?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
  • 2019-03-19
  • 2021-04-17
  • 1970-01-01
  • 2022-08-18
相关资源
最近更新 更多