Tibbles 拒绝 lubridate 的 duration 和 period 对象

Tibbles reject lubridate's duration and period objects

有效代码:durationperiod 对象

下面的代码分别成功生成了一个duration对象和一个period对象。

> lubridate::as.duration(1)
[1] "1s"

> lubridate::seconds(1)
[1] "1S"

不起作用的代码:tibbles

中的 durationperiod 对象

但是,当我尝试使用 durationperiod 对象创建 tibble 时,我收到了无意义的错误消息。

> tibble::tibble(y = lubridate::as.duration(1))
Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.

> tibble::tibble(y = lubridate::seconds(1))
Error in x < 0 : cannot compare Period to Duration:
coerce with 'as.numeric' first.

有效的代码:data.frames

中的 durationperiod 个对象

base::data.frame 替换 tibble::tibble 有效。

> data.frame(y = lubridate::as.duration(1))
   y
1 1s

> data.frame(y = lubridate::seconds(1))
   y
1 1S

无效的代码 - 将这些 data.frame 强制转换为 tibbles

使用 tibble::as_tibble 将这些 data.frame 强制转换为 tibbles 会产生与之前相同的错误。

> tibble::as_tibble(data.frame(y = lubridate::as.duration(1)))
Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.

> tibble::as_tibble(data.frame(y = lubridate::seconds(1)))
Error in x < 0 : cannot compare Period to Duration:
coerce with 'as.numeric' first.

可能的解释

Hadley 在这个 Github 问题 - https://github.com/tidyverse/tibble/issues/326 - 中提到了一些关于 S4 列的内容,其中包括 as.durationas.period。没有特别提到不兼容。

深入研究源代码,我发现以下依赖链给出了相同的错误消息:as_tibble.data.frame --> list_to_tibble --> new_tibble

tibble:::list_to_tibble 中,传递给 tibble::new_tibble 的唯一参数是 x。因此,subclass 被赋予默认值 NULL,并且 tibble::new_tibble 的倒数第二行变为

class(x) <- c("tbl_df", "tbl", "data.frame")

对象具有结构,但尝试直接调用它们会产生错误。

> x <- data.frame(y = lubridate::as.duration(1))
> class(x) <- c("tbl_df", "tbl", "data.frame")
> str(x)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   1 obs. of  1 variable:
 $ x:Formal class 'Duration' [package "lubridate"] with 1 slot
  .. ..@ .Data: num 1
> x
Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.

> x <- data.frame(y = lubridate::seconds(1))
> class(x) <- c("tbl_df", "tbl", "data.frame")
> str(x)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   1 obs. of  1 variable:
 $ y:Formal class 'Period' [package "lubridate"] with 6 slots
  .. ..@ .Data : num 1
  .. ..@ year  : num 0
  .. ..@ month : num 0
  .. ..@ day   : num 0
  .. ..@ hour  : num 0
  .. ..@ minute: num 0
> x 
Error in x < 0 : cannot compare Period to Duration:
coerce with 'as.numeric' first.

因此,分配 data.frame x 向量 c("tbl_df", "tbl", "data.frame") 的 class 似乎导致 R 试图强制 x 以引发错误的方式。

此外,考虑到 tibble::tibble 也调用了 as_tibble(尽管不在 data.frame 上),我会冒险猜测我与 tibble::tibble 的问题具有相同的原因。

包版本

此问题现已从 pillar v.1.2.1 (https://github.com/r-lib/pillar/issues/88) 开始解决。