thomasp85 / ggforce

Accelerating ggplot2
https://ggforce.data-imaginist.com
Other
916 stars 106 forks source link

`geom_mark_circle()` fails if `filter` has NA #306

Closed eliocamp closed 9 months ago

eliocamp commented 1 year ago

The filter aes in geom_mar_circle() (and probably its friends) fail with an obscure error if there are NAs.

library(ggplot2)

palmerpenguins::penguins |> 
  ggplot(aes(bill_length_mm, flipper_length_mm))+
  geom_point(aes(color = species))+
  ggforce::geom_mark_circle(aes(filter = body_mass_g == max(body_mass_g, na.rm = TRUE),
                       label = body_mass_g))
#> Error in `scale_apply()`:
#> ! `scale_id` must not contain any "NA"
#> Backtrace:
#>      ▆
#>   1. ├─base::tryCatch(...)
#>   2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   3. │   ├─base (local) tryCatchOne(...)
#>   4. │   │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   5. │   └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#>   6. │     └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   7. │       └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   8. ├─base::withCallingHandlers(...)
#>   9. ├─base::saveRDS(...)
#>  10. ├─base::do.call(...)
#>  11. ├─base (local) `<fn>`(...)
#>  12. └─global `<fn>`(input = base::quote("bad-grub_reprex.R"))
#>  13.   └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#>  14.     └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#>  15.       └─knitr:::process_file(text, output)
#>  16.         ├─base::withCallingHandlers(...)
#>  17.         ├─base::withCallingHandlers(...)
#>  18.         ├─knitr:::process_group(group)
#>  19.         └─knitr:::process_group.block(group)
#>  20.           └─knitr:::call_block(x)
#>  21.             └─knitr:::block_exec(params)
#>  22.               └─knitr:::eng_r(options)
#>  23.                 ├─knitr:::in_input_dir(...)
#>  24.                 │ └─knitr:::in_dir(input_dir(), expr)
#>  25.                 └─knitr (local) evaluate(...)
#>  26.                   └─evaluate::evaluate(...)
#>  27.                     └─evaluate:::evaluate_call(...)
#>  28.                       ├─evaluate (local) handle(...)
#>  29.                       │ └─base::try(f, silent = TRUE)
#>  30.                       │   └─base::tryCatch(...)
#>  31.                       │     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  32.                       │       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  33.                       │         └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  34.                       ├─base::withCallingHandlers(...)
#>  35.                       ├─base::withVisible(value_fun(ev$value, ev$visible))
#>  36.                       └─knitr (local) value_fun(ev$value, ev$visible)
#>  37.                         └─knitr (local) fun(x, options = options)
#>  38.                           ├─base::withVisible(knit_print(x, ...))
#>  39.                           ├─knitr::knit_print(x, ...)
#>  40.                           └─knitr:::knit_print.default(x, ...)
#>  41.                             └─evaluate (local) normal_print(x)
#>  42.                               ├─base::print(x)
#>  43.                               └─ggplot2:::print.ggplot(x)
#>  44.                                 ├─ggplot2::ggplot_build(x)
#>  45.                                 └─ggplot2:::ggplot_build.ggplot(x)
#>  46.                                   └─layout$train_position(data, scale_x(), scale_y())
#>  47.                                     └─ggplot2 (local) train_position(..., self = self)
#>  48.                                       └─self$facet$train_scales(...)
#>  49.                                         └─ggplot2 (local) train_scales(...)
#>  50.                                           └─ggplot2:::scale_apply(layer_data, x_vars, "train", SCALE_X, x_scales)
#>  51.                                             └─cli::cli_abort("{.arg scale_id} must not contain any {.val NA}")
#>  52.                                               └─rlang::abort(...)

This is because there are some missing values in body_mass_g, this can be worked around by explicitly removing the missing values.

palmerpenguins::penguins |> 
  subset(!is.na(body_mass_g)) |>
  ggplot(aes(bill_length_mm, flipper_length_mm))+
  geom_point(aes(color = species))+
  ggforce::geom_mark_circle(aes(filter = body_mass_g == max(body_mass_g, na.rm = TRUE),
                                label = body_mass_g))

Created on 2023-07-26 with reprex v2.0.2

It might be better if filter dropped missing values.

I think this should be solved by adding data$filter <- data$filter & !is.na(data$filter) here

https://github.com/thomasp85/ggforce/blob/9be635c582559f016254b111770a61e4b4aa0958/R/mark_circle.R#L142-L145

I can create a PR if you'd like.