使用 dplyr::group_by() 查找具有 NA 的最短日期
Using dplyr::group_by() to find min dates with NAs
我正在查找一组中的最短日期。很多时候,该组只包含缺少的日期(在这种情况下,我更喜欢分配 NA
之类的内容)。
NA
似乎分配正确,但它们没有像我预期的那样响应 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()
以支持已建立且经过测试的 function/approach ,其行为更像 SQL.
(尽管我很感谢@alistaire 对 base:min()
的封装,如果已建立的 function/approach 不存在,我将使用它。)
问题是 min
与 na.rm = TRUE
和全部 NA
值 returns Inf
(max
等同于 returns -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
它不是那么简洁,但其行为是完全可以预测的。
我正在查找一组中的最短日期。很多时候,该组只包含缺少的日期(在这种情况下,我更喜欢分配 NA
之类的内容)。
NA
似乎分配正确,但它们没有像我预期的那样响应 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()
以支持已建立且经过测试的 function/approach ,其行为更像 SQL.
(尽管我很感谢@alistaire 对 base:min()
的封装,如果已建立的 function/approach 不存在,我将使用它。)
问题是 min
与 na.rm = TRUE
和全部 NA
值 returns Inf
(max
等同于 returns -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
它不是那么简洁,但其行为是完全可以预测的。