在 dplyr::mutate 中执行函数并忽略缺失值

Perform function within dplyr::mutate and ignore missing values

提问人:Sam 提问时间:11/16/2023 更新时间:11/16/2023 访问量:34

问:

我正在努力寻找解决应该(并且可能是)简单问题的解决方案。

给定以下数据(代码如下):

# A tibble: 10 × 2
      id datetime           
   <dbl> <dttm>             
 1     1 NA                 
 2     2 2023-01-16 10:00:00
 3     3 NA                 
 4     4 2023-01-18 20:00:00
 5     5 2023-01-28 21:00:00
 6     6 2023-01-17 19:00:00
 7     7 NA                 
 8     8 2023-01-16 04:00:00
 9     9 2023-01-15 06:00:00
10    10 2023-01-13 21:00:00

我想将小时从 POSIXct 向量提取到一个新变量,但缺少值给我带来了问题。

预期输出:

# A tibble: 10 × 3
      id datetime            hour           
   <dbl> <dttm>              <dbl>
 1     1 NA                  NA
 2     2 2023-01-16 10:00:00 10
 3     3 NA                  NA
 4     4 2023-01-18 20:00:00 20
 5     5 2023-01-28 21:00:00 21
 6     6 2023-01-17 19:00:00 19
 7     7 NA                  NA
 8     8 2023-01-16 04:00:00 4
 9     9 2023-01-15 06:00:00 6
10    10 2023-01-13 21:00:00 21

我尝试了各种版本的 if_else 或 case_when 来管理缺失值,但不断出现错误。我的当前版本抛出错误:

Error in `mutate()`:
ℹ In argument: `hour = if_else(...)`.
Caused by error in `map_chr()`:
ℹ In index: 1.
Caused by error:
! Result must be length 1, not 0.
Run `rlang::last_trace()` to see where the error occurred.

建议它仍然在if_else的 FALSE 部分包含缺失值。

回复:

library(tidyverse)

dat <- tibble(
  id = seq(1, 10, 1),
  datetime = sample(seq(as.POSIXct("2023/1/1"), as.POSIXct("2023/1/31"), by = "hour"), 10)
)
dat[c(1, 3, 7), 2] <- NA

dat

dat <- dat %>%
  mutate(
    hour = if_else(
      is.na(datetime),
      NA,
      datetime %>% str_split(" ") %>% map_chr(2) %>% hms() %>% hour()
      )
    )
R 日期时间 dplyr

评论

1赞 Peter 11/16/2023
润滑剂是您的朋友,请尝试:mutate(dat, hour = lubridate::hour(datetime) )

答:

1赞 Peter 11/16/2023 #1

lubridate::hour()是你的朋友...

library(tibble)
library(dplyr)
library(lubridate)

dat <- tibble(
  id = seq(1, 10, 1),
  datetime = sample(seq(as.POSIXct("2023/1/1"), as.POSIXct("2023/1/31"), by = "hour"), 10)
)
dat[c(1, 3, 7), 2] <- NA


mutate(dat, hour = lubridate::hour(datetime) )
#> # A tibble: 10 × 3
#>       id datetime             hour
#>    <dbl> <dttm>              <int>
#>  1     1 NA                     NA
#>  2     2 2023-01-11 05:00:00     5
#>  3     3 NA                     NA
#>  4     4 2023-01-05 15:00:00    15
#>  5     5 2023-01-04 00:00:00     0
#>  6     6 2023-01-26 06:00:00     6
#>  7     7 NA                     NA
#>  8     8 2023-01-12 21:00:00    21
#>  9     9 2023-01-20 12:00:00    12
#> 10    10 2023-01-05 04:00:00     4

创建于 2023-11-16 with reprex v2.0.2

评论

0赞 Sam 11/16/2023
确实是一个简单的解决方案!
1赞 jeffreyohene 11/16/2023 #2

我认为该错误可能是由使用 hms() 引起的。hms() 更适合使用持续时间,而不是使用您拥有的时间戳或日期时间对象。解决此问题的最佳函数是使用 lubridate 包中的 hour() 函数从 datetime 列的 datetime 值中提取小时,如下所示:

library(tidyverse)

dat <- tibble(
  id = seq(1, 10, 1),
  datetime = sample(seq(as.POSIXct("2023/1/1"), as.POSIXct("2023/1/31"), by = "hour"), 10)
)

dat[c(1, 3, 7), 2] <- NA


dat <- dat %>%
  mutate(
    hour = if_else(
      is.na(datetime),
      NA,
      lubridate::hour(datetime)
    )
  )

dat
#> # A tibble: 10 × 3
#>       id datetime             hour
#>    <dbl> <dttm>              <int>
#>  1     1 NA                     NA
#>  2     2 2023-01-15 22:00:00    22
#>  3     3 NA                     NA
#>  4     4 2023-01-02 19:00:00    19
#>  5     5 2023-01-13 06:00:00     6
#>  6     6 2023-01-18 00:00:00     0
#>  7     7 NA                     NA
#>  8     8 2023-01-21 00:00:00     0
#>  9     9 2023-01-14 10:00:00    10
#> 10    10 2023-01-13 17:00:00    17

或者,您可以在 mutate 中使用 case_when 函数而不是 ifelse 来修改它,同时在新列中保持所有 NA 值不变

dat <- tibble(
  id = seq(1, 10, 1),
  datetime = sample(seq(as.POSIXct("2023/1/1"), as.POSIXct("2023/1/31"), by = "hour"), 10)
)
dat[c(1, 3, 7), 2] <- NA

dat <- dat %>%
  mutate(
    hour = case_when(
      is.na(datetime) ~ NA_real_,
      TRUE ~ datetime %>% lubridate::hour()
    )
  )