【问题标题】:dplyr::if_else changes datetime (POSIXct) valuesdplyr::if_else 更改日期时间 (POSIXct) 值
【发布时间】:2020-12-07 22:33:00
【问题描述】:

我正在处理一个有很多时间戳的数据集。我尝试识别一些无效的时间戳并将其设置为 NA。因为if_else() 强制我在双臂中使用相同的数据类型,所以我使用as.POSIXct(NA) 对这些缺失值进行编码。

有趣的是,当我在if_else() 中反转测试(并更改truefalse 参数)时,结果会有所不同。

这里有一些代码来说明我的问题:

x <- tibble(
  A = parse_datetime("2020-08-18 19:00"),
  B = if_else(TRUE,               A, as.POSIXct(NA)),
  C = if_else(FALSE, as.POSIXct(NA),              A)
)

> x
# A tibble: 1 x 3
  A                   B                   C                  
  <dttm>              <dttm>              <dttm>             
1 2020-08-18 19:00:00 2020-08-18 19:00:00 2020-08-18 21:00:00

任何想法,为什么 C 会晚两个小时?

跟进:

根据下面的优秀答案,我认为更易读的解决方案可能应该使用parse_datetime(NA_character_) 生成缺少的日期时间对象,并在代码中使用它而不是as.POSIXct()

R> NA_datetime_ <- parse_datetime(NA_character_)

R> x <- tibble(
  A = parse_datetime("2020-08-18 19:00"),
  B = if_else(TRUE,             A, NA_datetime_),
  C = if_else(FALSE, NA_datetime_,            A)
)

R> map(x, lubridate::tz)
$A
[1] "UTC"

$B
[1] "UTC"

$C
[1] "UTC"

【问题讨论】:

    标签: r dplyr posixct


    【解决方案1】:

    这是一个时区问题:

    lubridate::tz(x$A)
    [1] "UTC"
    lubridate::tz(x$B)
    [1] "UTC"
    lubridate::tz(x$C)
    [1] ""
    

    这是由于if_else &lt;- function (test, yes, no) 的工作方式:它使用yes 参数的属性,对于C 来说是NA

    【讨论】:

    • 你可以通过if_else(FALSE, as.POSIXct(NA), as.POSIXct(as.character(A)))来修复它。不过看起来确实是个错误。
    • @AllanCameron 你的方法使三个时间看起来相等,但实际上第三个时区不同。
    • @Waldi:我认为这甚至是另一个错误: if_else 不仅在将 A 复制到 C 时更改了它的值,而且还删除了 timezone 属性。您认为这是一项功能还是我应该尝试向 dplyr 人员发布错误报告?
    • @AxelFacius 它对 data.table::fifelse 的作用相同。似乎不仅仅是 dplyr::if_else 的问题。
    • @Alex Facius,查看 Darren Tsai 的回答
    【解决方案2】:

    首先,您需要知道parse_datetime() 返回一个日期时间对象,其tzone 属性默认为UTC。您可以使用lubridate::tz(x$A)attributes(x$A) 进行检查。

    if_else() 的文档中,它说truefalse 参数必须是相同的类型。 所有其他属性均取自true。因此,部分C 是您的小标题:

    C = if_else(FALSE, as.POSIXct(NA), A)
    

    as.POSIXct(NA) 没有tzone 属性,因此Atzone 被删除并重置为您所在地区的时区。实际上,C不是两个小时后。 三列的时间相等,但时区不相等。要解决这个问题,您可以调整as.POSIXct(NA) 以拥有tzone 属性,即将其替换为

    as.POSIXct(NA_character_, tz = "UTC")
    

    注意: 您必须使用NA_character_ 而不是NA,因为as.POSIXct() 中的tz 参数仅适用于字符对象。


    最后,修改你的代码

    x <- tibble(
      A = parse_datetime("2020-08-18 19:00"),
      B = if_else(TRUE, A, as.POSIXct(NA_character_, tz = "UTC")),
      C = if_else(FALSE, as.POSIXct(NA_character_, tz = "UTC"), A)
    )
    
    # # A tibble: 1 x 3
    #   A                   B                   C                  
    #   <dttm>              <dttm>              <dttm>             
    # 1 2020-08-18 19:00:00 2020-08-18 19:00:00 2020-08-18 19:00:00
    

    记得检查他们的时区。

    R > lubridate::tz(x$A)
    [1] "UTC"
    R > lubridate::tz(x$B)
    [1] "UTC"
    R > lubridate::tz(x$C)
    [1] "UTC"
    

    【讨论】:

    • 感谢您提供的许多详细信息。我想我理解这个问题,但仍然觉得这是一个很成问题的行为。我想当 if_else 更改属性时应该会生成一个警告。对我来说,更透明的解决方案是使用parse_datetime(NA_character) 而不是as.POSIXct(NA_character, tz = "UTC") 来生成缺失值。
    猜你喜欢
    • 2020-01-05
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 2021-10-22
    • 2021-09-04
    • 1970-01-01
    相关资源
    最近更新 更多