Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure to print a tibble with an S4 column #358

Closed
hplieninger opened this issue Jan 11, 2018 · 6 comments
Closed

Failure to print a tibble with an S4 column #358

hplieninger opened this issue Jan 11, 2018 · 6 comments

Comments

@hplieninger
Copy link

When I want to print a tibble containing an S4 column, I get an error with tibble 1.4.1 as well as with the current GitHub version, but not with version 1.3.4.

library(tibble)
#> Warning: package 'tibble' was built under R version 3.4.3
a1 <- nycflights13::flights[, c("flight", "air_time")]
a1$duration <- lubridate::dminutes(a1$air_time)
str(a1)
#> Classes 'tbl_df', 'tbl' and 'data.frame':    336776 obs. of  3 variables:
#>  $ flight  : int  1545 1714 1141 725 461 1696 507 5708 79 301 ...
#>  $ air_time: num  227 227 160 183 116 150 158 53 140 138 ...
#>  $ duration:Formal class 'Duration' [package "lubridate"] with 1 slot
#>   .. ..@ .Data: num  13620 13620 9600 10980 6960 ...
print(a1)
#> Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.
@ghost
Copy link

ghost commented Jan 13, 2018

I posted a superset of the following on StackOverflow in trying to get to the bottom of this issue (https://stackoverflow.com/questions/48087358/tibbles-reject-lubridates-duration-and-period-objects). It may be useful - and the reprexes (reprices?) are more minimal.

The following code successfully produces a duration object.

lubridate::as.duration(1)
#> "1s"

However, when I try to print tibbles using a duration object, I get an error message.

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

Replacing tibble::tibble with base::data.frame works.

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

Using tibble::as_tibble to coerce these data.frames to tibbles yields the same error as before.

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

Digging in the source code, I find the following chain of dependencies that give the same error message: as_tibble.data.frame --> list_to_tibble --> new_tibble

In tibble:::list_to_tibble, the only argument passed to tibble::new_tibble is x. Consequently, subclass is assigned the default value of NULL, and the penultimate line of tibble::new_tibble becomes

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

The objects have a structure, but trying to print them yields an error.

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`.

Consequently, it appears that assigning the class of the data.frame x the vector c("tbl_df", "tbl", "data.frame") causes R to try to coerce x in a way that throws an error when x is printed.

Furthermore, given that tibble::tibble also calls tibble::as_tibble (albeit not on a data.frame), I would hazard a guess that my issues with tibble::tibble have the same cause.

Package versions

  • Tibble: 1.4.1
  • Lubridate: 1.7.1
  • R: 3.4.3

@krlmlr
Copy link
Member

krlmlr commented Jan 14, 2018

Thanks for the analysis. It looks like creating the tibble succeeds, only printing fails:

x <- tibble::tibble(y = lubridate::as.duration(1))
print(x)
## Error: ...

The source of the error seems to be the pillar() constructor:

x <- pillar::pillar(lubridate::as.duration(1))
#> Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.

Created on 2018-01-14 by the reprex package (v0.1.1.9000).

Closing in favor of r-lib/pillar#88.

@krlmlr krlmlr closed this as completed Jan 14, 2018
@jschelbert
Copy link

Hi,
It seems that the same issue also occurs for intervals (with a slightly different error message).
Not sure this is also due to the pillar bug or something else...

library(tidyverse)
#> -- Attaching packages -------------------------------------------------------------------------------------------------------------------- tidyverse 1.2.1 --
#> v ggplot2 2.2.1     v purrr   0.2.4
#> v tibble  1.4.2     v dplyr   0.7.4
#> v tidyr   0.7.2     v stringr 1.2.0
#> v readr   1.1.1     v forcats 0.2.0
#> -- Conflicts ----------------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
#> x dplyr::filter() masks stats::filter()
#> x dplyr::lag()    masks stats::lag()
library(lubridate)
#> 
#> Attache Paket: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date

df <- data.frame(begin = c(ymd_hm("2018-01-01 00:00"),
                           ymd_hm("1900-01-01 00:00"),
                           ymd_hm("1900-01-01 00:00")), 
                 end = c(ymd_hm("2018-01-04 00:00"),
                         ymd_hm("3000-01-01 00:00"),
                         ymd_hm("3000-12-31 00:00")))

df %>% mutate(test = interval(start=begin, end=end))
#>                 begin                 end
#> 1 2018-01-01 01:00:00 2018-01-04 01:00:00
#> 2 1900-01-01 01:00:00 3000-01-01 01:00:00
#> 3 1900-01-01 01:00:00 3000-12-31 01:00:00
#>                                               test
#> 1 2018-01-01 01:00:00 CET--2018-01-04 01:00:00 CET
#> 2 1900-01-01 01:00:00 CET--3000-01-01 01:00:00 CET
#> 3 1900-01-01 01:00:00 CET--3000-12-31 01:00:00 CET

df <- tibble(begin = c(ymd_hm("2018-01-01 00:00"),
                           ymd_hm("1900-01-01 00:00"),
                           ymd_hm("1900-01-01 00:00")), 
                 end = c(ymd_hm("2018-01-04 00:00"),
                         ymd_hm("3000-01-01 00:00"),
                         ymd_hm("3000-12-31 00:00")))

df %>% mutate(test = interval(start=begin, end=end))
#> Note: method with signature 'Interval#ANY' chosen for function '-',
#>  target signature 'Interval#Interval'.
#>  "ANY#Interval" would also be valid
#> Error in round_x - lhs: Arithmetic operators undefined for 'Interval' and 'Interval' classes:
#>   convert one to numeric or a matching time-span class.

@ghost
Copy link

ghost commented Feb 3, 2018

@jschelbert

The following code yields the same error, so your error is also due to the pillar bug:

pillar::pillar(lubridate::interval(lubridate::ymd("2017-01-01"), lubridate::ymd("2018-01-01")))

@krlmlr
Copy link
Member

krlmlr commented Mar 1, 2018

Printing S4 objects should work with the latest version of pillar.

@github-actions
Copy link
Contributor

This old thread has been automatically locked. If you think you have found something related to this, please open a new issue and link to this old issue if necessary.

@github-actions github-actions bot locked and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants