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

ggplot fails with duration objects. #2414

Closed
ghost opened this issue Jan 20, 2018 · 4 comments · Fixed by tidyverse/lubridate#696
Closed

ggplot fails with duration objects. #2414

ghost opened this issue Jan 20, 2018 · 4 comments · Fixed by tidyverse/lubridate#696
Labels
bug an unexpected problem or unintended behavior scales 🐍

Comments

@ghost
Copy link

ghost commented Jan 20, 2018

Trying to print a ggplot object created using a tibble with a duration object inside yields the same error.

df <- tibble::tibble(                                                       
  a = 1:10,                                                                   
  b = lubridate::as.duration(1:10)                                            
)                                                                           
                                                                            
p <- ggplot2::ggplot(df, ggplot2::aes(x = a, y = b)) + ggplot2::geom_point()
p                                                                           
#> Error: Incompatible duration classes (Duration, integer). Please coerce with `as.duration`.

Initially, I thought this might be caused by the bug at r-lib/pillar#88, due to the same error message. Indeed, we encounter the same error message if df is printed, and this is caused by the pillar issue.

However, trying to print a ggplot object created using a data.frame with a duration object inside still yields the error, even though we're not using tibbles anymore.

df <- data.frame(                                                           
  a = 1:10,                                                                   
  b = lubridate::as.duration(1:10)                                            
)                                                                           
                                                                            
p <- ggplot2::ggplot(df, ggplot2::aes(x = a, y = b)) + ggplot2::geom_point()
p                                                                           
#> Error: Incompatible duration classes (Duration, integer). Please coerce with `as.duration`.

Given that ggplot2 depends on tibble, this is still likely caused by r-lib/pillar#88, even though we're not actually printing tibbles here. Perhaps the data.frame is being coerced to a tibble?

@ghost
Copy link
Author

ghost commented Mar 12, 2018

Curiously, the fix at r-lib/pillar/issues/88 did not resolve the above error messages.

Package versions:

  • ggplot2_2.2.1
  • tibble_1.4.2
  • pillar_1.2.1

I don't know enough about the internals of ggplot2 to comment further on why this might be the case.

Edit: Upon further investigation, using period objects works:

df1 <- tibble::tibble(
  a = 1:10
  b = lubridate::seconds(1:10)
)

p <- ggplot2::ggplot(df1, ggplot2::aes(x = a, y = b)) + ggplot2::geom_point()
p

@hadley hadley added bug an unexpected problem or unintended behavior scales 🐍 labels Apr 26, 2018
@dpseidel
Copy link
Collaborator

So this is genuine bug but I think it may be best solved in lubridate rather than ggplot2. All it would take is specifying a Compare method for duration and numeric objects, which currently doesn't exist and thus returns the above error. There may be a very good reason why this is not already specified but none seem immediately obvious to me.

Basically what is happening is ggplot2 is trying to check to make sure none of your data is outside the range/limits of the scale, but it's running into issues because duration objects don't maintain their class when range() is calculated. See below:

library(ggplot2)

df <- data.frame(                                                           
  a = 1:10,                                                                   
  b = lubridate::duration(1:10)                                            
)                                                                           

(x <- df$b)
#>  [1] "1s"  "2s"  "3s"  "4s"  "5s"  "6s"  "7s"  "8s"  "9s"  "10s"
class(x)
#> [1] "Duration"
#> attr(,"package")
#> [1] "lubridate"

(r <- range(df$b))
#> [1]  1 10
class(r)
#> [1] "numeric"

x < r[2]
#> Error: Incompatible duration classes (Duration, numeric). Please coerce with `as.duration`.

x < as.duration(r[2])
#> [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

For now, an easy way to get around this is to call the numeric .Data of your Duration object explicitly:

ggplot(df, aes(x = a, y = b@.Data)) + geom_point()

As you note, period objects don't have this problem because they do have a specified Compare method.

@dpseidel
Copy link
Collaborator

This is now fixed. The dev version of lubridate now includes a compare method for numeric and duration objects.

@lock
Copy link

lock bot commented Jan 17, 2019

This old issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with reprex) and link to this issue. https://reprex.tidyverse.org/

@lock lock bot locked and limited conversation to collaborators Jan 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug an unexpected problem or unintended behavior scales 🐍
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants