slowkow / ggrepel

:round_pushpin: Repel overlapping text labels away from each other in your ggplot2 figures.
https://ggrepel.slowkow.com
GNU General Public License v3.0
1.22k stars 96 forks source link

issue with geomnet #262

Closed iago-pssjd closed 2 months ago

iago-pssjd commented 2 months ago

Sorry for lack of specificity on title.

Summary

I am trying to update an archived package which calls to ggrepel. Although it passes all the tests, certain example working either with ggplot2::GeomText$draw_panel(labels, panel_scales, coord) or ggplot2::GeomTextRepel$draw_panel(labels, panel_scales, coord) (or with label instead of text), it works with the ggplot2 version, but it gives an error with the ggrepel option.

First, I am working with current R 4.4.1 and ggplot2 release 3.4.3 on Debian 12.

I see that until commit 1a267f4 (2018-10-30) (and/or CRAN release 0.8.2), it works. After commit f4ebe30 (2019-01-02), it does not print labels and it produces

Error: Cannot create zero-length unit vector ("unit" subsetting)

Same until commit 306c074 (2019-04-30). From commit b8352c1 (2019-05-01) to commit e02dfdf (2019-11-03) I cannot even install the package. Then, with commit 8d16037 (2019-11-03) I can install again, the used code print no labels and then, when printing the produced plot, I get the error

Error in `$<-.data.frame`(`*tmp*`, "point.size", value = numeric(0)) : 
  replacement has 0 rows, data has 53

where labels is a data.frame with 53 rows (see below); and this is the same as for most recent versions of both ggplot2 and ggrepel (from CRAN release 0.9.0) installed.

I tried using ggplot2::GeomTextRepel$draw_panel(labels, panel_scales, coord, max.overlaps = Inf) with the same result.

May you give me some hint on how should I proceed?

Thanks a lot!

The objects passed to `GeomTextRepel`:
Browse[1]> str(labels)
'data.frame':   53 obs. of  11 variables:
 $ x     : num  0.298 0.905 0.86 0.998 0.382 ...
 $ y     : num  0.355 0.509 0.594 0.529 0.751 ...
 $ label : Factor w/ 53 levels "aspect.ratio",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ colour: chr  "black" "black" "black" "black" ...
 $ shape : num  19 19 19 19 19 19 19 19 19 19 ...
 $ size  : num  3.32 4.58 3.32 3.32 4.58 ...
 $ angle : num  0 0 0 0 0 0 0 0 0 0 ...
 $ alpha : logi  NA NA NA NA NA NA ...
 $ hjust : num  0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 ...
 $ fill  : chr  "grey40" "grey40" "grey40" "grey40" ...
 $ vjust : num  0 0 0 0 0 0 0 0 0 0 ...
Browse[1]> str(panel_scales)
List of 6
 $ x      :Classes 'ViewScale', 'ggproto', 'gg' 
    aesthetics: x xmin xmax xend xintercept xmin_final xmax_final xlower ...
    break_positions: function
    break_positions_minor: function
    breaks: 0 0.25 0.5 0.75 1
    continuous_range: -0.105 1.105
    dimension: function
    get_breaks: function
    get_breaks_minor: function
    get_labels: function
    get_limits: function
    guide: waiver
    is_discrete: function
    is_empty: function
    limits: -0.05 1.05
    make_title: function
    map: function
    minor_breaks: 0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1
    name: waiver
    position: bottom
    rescale: function
    scale: 
        aesthetics: x xmin xmax xend xintercept xmin_final xmax_final xlower ...
        axis_order: function
        break_info: function
        break_positions: function
        breaks: waiver
        call: call
        clone: function
        dimension: function
        expand: waiver
        get_breaks: function
        get_breaks_minor: function
        get_labels: function
        get_limits: function
        guide: waiver
        is_discrete: function
        is_empty: function
        labels: waiver
        limits: -0.05 1.05
        make_sec_title: function
        make_title: function
        map: function
        map_df: function
        minor_breaks: waiver
        n.breaks: NULL
        na.value: NA
        name: waiver
        oob: function
        palette: function
        position: bottom
        print: function
        range: environment
        rescale: function
        rescaler: function
        reset: function
        scale_name: position_c
        sec_name: function
        secondary.axis: waiver
        train: function
        train_df: function
        trans: transform
        transform: function
        transform_df: function
        super:  
    scale_is_discrete: FALSE
    super:   
 $ x.sec  :Classes 'ViewScale', 'ggproto', 'gg' 
    aesthetics: x xmin xmax xend xintercept xmin_final xmax_final xlower ...
    break_positions: function
    break_positions_minor: function
    breaks: 0 0.25 0.5 0.75 1
    continuous_range: -0.105 1.105
    dimension: function
    get_breaks: function
    get_breaks_minor: function
    get_labels: function
    get_limits: function
    guide: guide, guide_none
    is_discrete: function
    is_empty: function
    limits: -0.05 1.05
    make_title: function
    map: function
    minor_breaks: 0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1
    name: waiver
    position: top
    rescale: function
    scale: 
        aesthetics: x xmin xmax xend xintercept xmin_final xmax_final xlower ...
        axis_order: function
        break_info: function
        break_positions: function
        breaks: waiver
        call: call
        clone: function
        dimension: function
        expand: waiver
        get_breaks: function
        get_breaks_minor: function
        get_labels: function
        get_limits: function
        guide: waiver
        is_discrete: function
        is_empty: function
        labels: waiver
        limits: -0.05 1.05
        make_sec_title: function
        make_title: function
        map: function
        map_df: function
        minor_breaks: waiver
        n.breaks: NULL
        na.value: NA
        name: waiver
        oob: function
        palette: function
        position: bottom
        print: function
        range: environment
        rescale: function
        rescaler: function
        reset: function
        scale_name: position_c
        sec_name: function
        secondary.axis: waiver
        train: function
        train_df: function
        trans: transform
        transform: function
        transform_df: function
        super:  
    scale_is_discrete: FALSE
    super:   
 $ x.range: num [1:2] -0.105 1.105
 $ y      :Classes 'ViewScale', 'ggproto', 'gg' 
    aesthetics: y ymin ymax yend yintercept ymin_final ymax_final lower  ...
    break_positions: function
    break_positions_minor: function
    breaks: 0 0.25 0.5 0.75 1
    continuous_range: -0.05 1.05
    dimension: function
    get_breaks: function
    get_breaks_minor: function
    get_labels: function
    get_limits: function
    guide: waiver
    is_discrete: function
    is_empty: function
    limits: 0 1
    make_title: function
    map: function
    minor_breaks: 0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1
    name: waiver
    position: left
    rescale: function
    scale: 
        aesthetics: y ymin ymax yend yintercept ymin_final ymax_final lower  ...
        axis_order: function
        break_info: function
        break_positions: function
        breaks: waiver
        call: call
        clone: function
        dimension: function
        expand: waiver
        get_breaks: function
        get_breaks_minor: function
        get_labels: function
        get_limits: function
        guide: waiver
        is_discrete: function
        is_empty: function
        labels: waiver
        limits: NULL
        make_sec_title: function
        make_title: function
        map: function
        map_df: function
        minor_breaks: waiver
        n.breaks: NULL
        na.value: NA
        name: waiver
        oob: function
        palette: function
        position: left
        print: function
        range: environment
        rescale: function
        rescaler: function
        reset: function
        scale_name: position_c
        sec_name: function
        secondary.axis: waiver
        train: function
        train_df: function
        trans: transform
        transform: function
        transform_df: function
        super:  
    scale_is_discrete: FALSE
    super:   
 $ y.sec  :Classes 'ViewScale', 'ggproto', 'gg' 
    aesthetics: y ymin ymax yend yintercept ymin_final ymax_final lower  ...
    break_positions: function
    break_positions_minor: function
    breaks: 0 0.25 0.5 0.75 1
    continuous_range: -0.05 1.05
    dimension: function
    get_breaks: function
    get_breaks_minor: function
    get_labels: function
    get_limits: function
    guide: guide, guide_none
    is_discrete: function
    is_empty: function
    limits: 0 1
    make_title: function
    map: function
    minor_breaks: 0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1
    name: waiver
    position: right
    rescale: function
    scale: 
        aesthetics: y ymin ymax yend yintercept ymin_final ymax_final lower  ...
        axis_order: function
        break_info: function
        break_positions: function
        breaks: waiver
        call: call
        clone: function
        dimension: function
        expand: waiver
        get_breaks: function
        get_breaks_minor: function
        get_labels: function
        get_limits: function
        guide: waiver
        is_discrete: function
        is_empty: function
        labels: waiver
        limits: NULL
        make_sec_title: function
        make_title: function
        map: function
        map_df: function
        minor_breaks: waiver
        n.breaks: NULL
        na.value: NA
        name: waiver
        oob: function
        palette: function
        position: left
        print: function
        range: environment
        rescale: function
        rescaler: function
        reset: function
        scale_name: position_c
        sec_name: function
        secondary.axis: waiver
        train: function
        train_df: function
        trans: transform
        transform: function
        transform_df: function
        super:  
    scale_is_discrete: FALSE
    super:   
 $ y.range: num [1:2] -0.05 1.05
Browse[1]> str(coord)
Classes 'CoordCartesian', 'Coord', 'ggproto', 'gg' 
    aspect: function
    backtransform_range: function
    clip: on
    default: TRUE
    distance: function
    expand: TRUE
    is_free: function
    is_linear: function
    labels: function
    limits: list
    modify_scales: function
    range: function
    render_axis_h: function
    render_axis_v: function
    render_bg: function
    render_fg: function
    setup_data: function
    setup_layout: function
    setup_panel_guides: function
    setup_panel_params: function
    setup_params: function
    train_panel_guides: function
    transform: function
    super:   
iago-pssjd commented 2 months ago

Reproducible example with R 4.0.5 (and probably newer). I use library checkout to a sane and compatible installation of libraries before geomnet being archived (therefore ggplot2 3.3.3, ggrepel 0.9.0, geomnet 0.3.1): (GeomTextRepel used in https://github.com/sctyner/geomnet/blob/master/R/geom-net.r)

library(checkpoint)
checkpoint("2021-01-01")
options(checkpoint.mranUrl="https://packagemanager.posit.co/")
install.packages('ggplot2')
install.packages('ggrepel')
install.packages('geomnet')
library(dplyr)
library(geomnet)
data(theme_elements)
# data step
TEnet <- fortify(as.edgedf(theme_elements$edges[,c("parent", "child")]), theme_elements$vertices)
# create a degree variable for use later
TEnet <- TEnet %>%
group_by(from_id) %>%
mutate(degree = sqrt(10 * n() + 1))
# plot
ggplot(data = TEnet,
aes(from_id = from_id, to_id = to_id)) +
geom_net(layout.alg = "fruchtermanreingold",
aes(fontsize = degree), directed = TRUE,
labelon = TRUE, size = 1, labelcolour = 'black',
ecolour = "grey70", arrowsize = 0.5,
linewidth = 0.5, repel = TRUE) +
theme_net() +
xlim(c(-0.05, 1.05))

Error in `$<-.data.frame`(`*tmp*`, "point.size", value = numeric(0)) : 
  replacement has 0 rows, data has 53

rlang::last_trace() 
<error/rlang_error>
Can't show last error because no error was recorded yet
Backtrace:
    █
 1. └─rlang::last_trace()
 2.   └─rlang::last_error()

traceback()
23: stop(sprintf(ngettext(N, "replacement has %d row, data has %d", 
        "replacement has %d rows, data has %d"), N, nrows), domain = NA)
22: `$<-.data.frame`(`*tmp*`, "point.size", value = numeric(0))
21: `$<-`(`*tmp*`, "point.size", value = numeric(0))
20: makeContent.textrepeltree(x)
19: makeContent(x)
18: drawGTree(x)
17: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
16: grid.draw.gTree(x$children[[i]], recording = FALSE)
15: grid.draw(x$children[[i]], recording = FALSE)
14: drawGTree(x)
13: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
12: grid.draw.gTree(x$children[[i]], recording = FALSE)
11: grid.draw(x$children[[i]], recording = FALSE)
10: drawGTree(x)
9: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
8: grid.draw.gTree(x$children[[i]], recording = FALSE)
7: grid.draw(x$children[[i]], recording = FALSE)
6: drawGTree(x)
5: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
4: grid.draw.gTree(gtable)
3: grid.draw(gtable)
2: print.ggplot(x)
1: (function (x, ...) 
   UseMethod("print"))(x)
slowkow commented 2 months ago

When you encounter an error, consider using rlang::last_trace() to help reveal which lines of code are related to the error. Could you share the traceback?

aphalo commented 2 months ago

Keep also in mind that changes introduced in 'ggplot2' updates have made changes in extensions like 'ggrepel' necessary, so newer versions of 'ggplot2' can fail with older versions of extensions and vice versa. Kamil's suggestion is a very good one! In the tidyverse blog posts have been published for the major updates to 'ggplot2' describing the changes required in extensions.

iago-pssjd commented 2 months ago

@slowkow @aphalo Thanks for your answers.

I added to the reprex the tracebacks.

I checked mainly the NEWS for ggrepel, since it seems an issue depending only on this package because of two reasons: on one hand, with ggplot2 release fixed (with both either 3.3.3 and 3.4.3), I got geomnet working or not depending on ggrepel release; on the other hand; it just fails with GeomTextRepel, but not with GeomText.

However I also checked ggplot2 last NEWS, but I couldn't detect any hint to this issue.

slowkow commented 2 months ago

Is there some reason that you must use old (unsupported) versions?

With the latest versions, I don't get the same error you do. I get an error with geomnet code:

#library(checkpoint)
#checkpoint("2021-01-01")
#options(checkpoint.mranUrl="https://packagemanager.posit.co/")
# install.packages('geomnet')

devtools::install_github("sctyner/geomnet")
#> Skipping install of 'geomnet' from a github remote, the SHA1 (030537d5) has not changed since last install.
#>   Use `force = TRUE` to force installation

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(geomnet)
#> Loading required package: ggplot2

data(theme_elements)

# data step
TEnet <- fortify(as.edgedf(theme_elements$edges[,c("parent", "child")]), theme_elements$vertices)
#> Using parent as the from node column and child as the to node column.
#> If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
#> Joining edge and node information by from_id and name respectively.

# create a degree variable for use later
TEnet <- TEnet %>%
  group_by(from_id) %>%
  mutate(degree = sqrt(10 * n() + 1))

# plot
ggplot(data = TEnet,
       aes(from_id = from_id, to_id = to_id)) +
  geom_net(layout.alg = "fruchtermanreingold",
           aes(fontsize = degree), directed = TRUE,
           labelon = TRUE, size = 1, labelcolour = 'black',
           ecolour = "grey70", arrowsize = 0.5,
           linewidth = 0.5, repel = TRUE) +
  theme_net() +
  xlim(c(-0.05, 1.05))
#> Error in `geom_net()`:
#> ! Problem while computing stat.
#> ℹ Error occurred in the 1st layer.
#> Caused by error:
#> ! The first two columns of `x` must be of the same type.
#> 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("used-aidi_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. │       ├─xfun:::handle_error(...)
#>  17. │       ├─base::withCallingHandlers(...)
#>  18. │       └─knitr:::process_group(group)
#>  19. │         └─knitr:::call_block(x)
#>  20. │           └─knitr:::block_exec(params)
#>  21. │             └─knitr:::eng_r(options)
#>  22. │               ├─knitr:::in_input_dir(...)
#>  23. │               │ └─knitr:::in_dir(input_dir(), expr)
#>  24. │               └─knitr (local) evaluate(...)
#>  25. │                 └─evaluate::evaluate(...)
#>  26. │                   └─evaluate:::evaluate_call(...)
#>  27. │                     ├─evaluate (local) handle(...)
#>  28. │                     │ └─base::try(f, silent = TRUE)
#>  29. │                     │   └─base::tryCatch(...)
#>  30. │                     │     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  31. │                     │       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  32. │                     │         └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  33. │                     ├─base::withCallingHandlers(...)
#>  34. │                     ├─base::withVisible(value_fun(ev$value, ev$visible))
#>  35. │                     └─knitr (local) value_fun(ev$value, ev$visible)
#>  36. │                       └─knitr (local) fun(x, options = options)
#>  37. │                         ├─base::withVisible(knit_print(x, ...))
#>  38. │                         ├─knitr::knit_print(x, ...)
#>  39. │                         └─knitr:::knit_print.default(x, ...)
#>  40. │                           └─evaluate (local) normal_print(x)
#>  41. │                             ├─base::print(x)
#>  42. │                             └─ggplot2:::print.ggplot(x)
#>  43. │                               ├─ggplot2::ggplot_build(x)
#>  44. │                               └─ggplot2:::ggplot_build.ggplot(x)
#>  45. │                                 └─ggplot2:::by_layer(...)
#>  46. │                                   ├─rlang::try_fetch(...)
#>  47. │                                   │ ├─base::tryCatch(...)
#>  48. │                                   │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  49. │                                   │ │   └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  50. │                                   │ │     └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  51. │                                   │ └─base::withCallingHandlers(...)
#>  52. │                                   └─ggplot2 (local) f(l = layers[[i]], d = data[[i]])
#>  53. │                                     └─l$compute_statistic(d, layout)
#>  54. │                                       └─ggplot2 (local) compute_statistic(..., self = self)
#>  55. │                                         └─self$stat$compute_layer(data, self$computed_stat_params, layout)
#>  56. │                                           └─geomnet (local) compute_layer(..., self = self)
#>  57. │                                             └─self$compute_panel(...)
#>  58. │                                               └─geomnet (local) compute_panel(..., self = self)
#>  59. │                                                 └─self$compute_network(...)
#>  60. │                                                   └─geomnet (local) compute_network(...)
#>  61. │                                                     ├─network::as.network(na.omit(edges[, 1:2]), matrix.type = "edgelist")
#>  62. │                                                     └─network::as.network.data.frame(na.omit(edges[, 1:2]), matrix.type = "edgelist")
#>  63. │                                                       └─network:::.validate_edge_df(...)
#>  64. │                                                         └─base::stop(...)
#>  65. └─base::.handleSimpleError(...)
#>  66.   └─rlang (local) h(simpleError(msg, call))
#>  67.     └─handlers[[1L]](cnd)
#>  68.       └─cli::cli_abort(...)
#>  69.         └─rlang::abort(...)

Created on 2024-08-06 with reprex v2.0.2

Session info ``` r sessioninfo::session_info() #> ─ Session info ─────────────────────────────────────────────────────────────── #> setting value #> version R version 4.2.3 (2023-03-15) #> os macOS Ventura 13.4 #> system aarch64, darwin20 #> ui X11 #> language (EN) #> collate en_US.UTF-8 #> ctype en_US.UTF-8 #> tz America/New_York #> date 2024-08-06 #> pandoc 3.1.11 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown) #> #> ─ Packages ─────────────────────────────────────────────────────────────────── #> package * version date (UTC) lib source #> cachem 1.1.0 2024-05-16 [1] CRAN (R 4.2.3) #> cli 3.6.2 2023-12-11 [1] CRAN (R 4.2.3) #> coda 0.19-4.1 2024-01-31 [1] CRAN (R 4.2.3) #> colorspace 2.1-0 2023-01-23 [1] CRAN (R 4.2.0) #> curl 5.2.1 2024-03-01 [1] CRAN (R 4.2.3) #> data.table 1.14.8 2023-02-17 [1] CRAN (R 4.2.0) #> devtools 2.4.5 2022-10-11 [1] CRAN (R 4.2.0) #> digest 0.6.35 2024-03-11 [1] CRAN (R 4.2.3) #> dplyr * 1.1.4 2023-11-17 [1] CRAN (R 4.2.3) #> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0) #> evaluate 0.23 2023-11-01 [1] CRAN (R 4.2.0) #> fansi 1.0.6 2023-12-08 [1] CRAN (R 4.2.3) #> fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.2.3) #> fs 1.6.4 2024-04-25 [1] CRAN (R 4.2.3) #> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0) #> geomnet * 0.3.1 2024-08-05 [1] Github (sctyner/geomnet@030537d) #> ggplot2 * 3.5.1 2024-04-23 [1] CRAN (R 4.2.3) #> glue 1.7.0 2024-01-09 [1] CRAN (R 4.2.3) #> gtable 0.3.4 2023-08-21 [1] CRAN (R 4.2.0) #> hms 1.1.3 2023-03-21 [1] CRAN (R 4.2.0) #> htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.2.3) #> htmlwidgets 1.6.2 2023-03-17 [1] CRAN (R 4.2.0) #> httpuv 1.6.11 2023-05-11 [1] CRAN (R 4.2.0) #> httr 1.4.6 2023-05-08 [1] CRAN (R 4.2.0) #> jsonlite 1.8.8 2023-12-04 [1] CRAN (R 4.2.3) #> knitr 1.47 2024-05-29 [1] CRAN (R 4.2.3) #> later 1.3.1 2023-05-02 [1] CRAN (R 4.2.0) #> lattice 0.20-45 2021-09-22 [1] CRAN (R 4.2.3) #> lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.2.0) #> lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.2.3) #> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) #> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) #> mime 0.12 2021-09-28 [1] CRAN (R 4.2.0) #> miniUI 0.1.1.1 2018-05-18 [1] CRAN (R 4.2.0) #> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) #> network 1.18.2 2023-12-05 [1] CRAN (R 4.2.3) #> pillar 1.9.0 2023-03-22 [1] CRAN (R 4.2.0) #> pkgbuild 1.4.4 2024-03-17 [1] CRAN (R 4.2.3) #> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) #> pkgload 1.3.4 2024-01-16 [1] CRAN (R 4.2.3) #> plotly 4.10.4 2024-01-13 [1] CRAN (R 4.2.3) #> profvis 0.3.7 2020-11-02 [1] CRAN (R 4.2.0) #> promises 1.2.0.1 2021-02-11 [1] CRAN (R 4.2.0) #> purrr 1.0.2 2023-08-10 [1] CRAN (R 4.2.0) #> R.cache 0.16.0 2022-07-21 [1] CRAN (R 4.2.0) #> R.methodsS3 1.8.2 2022-06-13 [1] CRAN (R 4.2.0) #> R.oo 1.26.0 2024-01-24 [1] CRAN (R 4.2.3) #> R.utils 2.12.3 2023-11-18 [1] CRAN (R 4.2.3) #> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0) #> Rcpp 1.0.11 2023-07-06 [1] CRAN (R 4.2.0) #> readr 2.1.4 2023-02-10 [1] CRAN (R 4.2.0) #> remotes 2.4.2 2021-11-30 [1] CRAN (R 4.2.0) #> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0) #> rlang 1.1.3 2024-01-10 [1] CRAN (R 4.2.3) #> rmarkdown 2.27 2024-05-17 [1] CRAN (R 4.2.3) #> rstudioapi 0.16.0 2024-03-24 [1] CRAN (R 4.2.3) #> scales 1.3.0 2023-11-28 [1] CRAN (R 4.2.3) #> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) #> shiny 1.7.4 2022-12-15 [1] CRAN (R 4.2.0) #> sna 2.7-2 2023-12-06 [1] CRAN (R 4.2.3) #> statnet.common 4.9.0 2023-05-24 [1] CRAN (R 4.2.0) #> stringi 1.8.4 2024-05-06 [1] CRAN (R 4.2.3) #> stringr 1.5.1 2023-11-14 [1] CRAN (R 4.2.3) #> styler 1.10.2 2023-08-29 [1] CRAN (R 4.2.0) #> tibble 3.2.1 2023-03-20 [1] CRAN (R 4.2.0) #> tidyr 1.3.1 2024-01-24 [1] CRAN (R 4.2.3) #> tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.2.3) #> tzdb 0.4.0 2023-05-12 [1] CRAN (R 4.2.3) #> urlchecker 1.0.1 2021-11-30 [1] CRAN (R 4.2.0) #> usethis 2.2.3 2024-02-19 [1] CRAN (R 4.2.3) #> utf8 1.2.4 2023-10-22 [1] CRAN (R 4.2.0) #> vctrs 0.6.5 2023-12-01 [1] CRAN (R 4.2.3) #> viridisLite 0.4.2 2023-05-02 [1] CRAN (R 4.2.0) #> withr 3.0.0 2024-01-16 [1] CRAN (R 4.2.3) #> xfun 0.44 2024-05-15 [1] CRAN (R 4.2.3) #> xtable 1.8-4 2019-04-21 [1] CRAN (R 4.2.0) #> yaml 2.3.8 2023-12-11 [1] CRAN (R 4.2.3) #> #> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library #> #> ────────────────────────────────────────────────────────────────────────────── ```
slowkow commented 2 months ago

Have you considered using ggnet2 or ggnetwork or ggraph or any other packages?

For example, ggnet2 seems to work just fine (after removing NA from your network):

library(dplyr)
library(geomnet)
data(theme_elements)

# data step
TEnet <- fortify(as.edgedf(theme_elements$edges[,c("parent", "child")]), theme_elements$vertices)
#> Using parent as the from node column and child as the to node column.
#> If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
#> Joining edge and node information by from_id and name respectively.

# create a degree variable for use later
TEnet <- TEnet %>%
  group_by(from_id) %>%
  mutate(degree = sqrt(10 * n() + 1))

# install.packages("GGally")
library(GGally)
#> Registered S3 method overwritten by 'GGally':
#>   method from   
#>   +.gg   ggplot2

ix <- !is.na(TEnet[,2])
net <- TEnet[ix,]
net[[1]] <- as.character(net[[1]])
net[[2]] <- as.character(net[[2]])
net <- network::network(net, loops = TRUE)

ggnet2(net) +
  geom_text(aes(label = label))
#> Warning in ggnet2(net): ggnet2 does not know how to handle self-loops

Created on 2024-08-06 with reprex v2.0.2

Session info ``` r sessioninfo::session_info() #> ─ Session info ─────────────────────────────────────────────────────────────── #> setting value #> version R version 4.2.3 (2023-03-15) #> os macOS Ventura 13.4 #> system aarch64, darwin20 #> ui X11 #> language (EN) #> collate en_US.UTF-8 #> ctype en_US.UTF-8 #> tz America/New_York #> date 2024-08-06 #> pandoc 3.1.11 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown) #> #> ─ Packages ─────────────────────────────────────────────────────────────────── #> package * version date (UTC) lib source #> cachem 1.1.0 2024-05-16 [1] CRAN (R 4.2.3) #> cli 3.6.2 2023-12-11 [1] CRAN (R 4.2.3) #> coda 0.19-4.1 2024-01-31 [1] CRAN (R 4.2.3) #> colorspace 2.1-0 2023-01-23 [1] CRAN (R 4.2.0) #> curl 5.2.1 2024-03-01 [1] CRAN (R 4.2.3) #> data.table 1.14.8 2023-02-17 [1] CRAN (R 4.2.0) #> devtools 2.4.5 2022-10-11 [1] CRAN (R 4.2.0) #> digest 0.6.35 2024-03-11 [1] CRAN (R 4.2.3) #> dplyr * 1.1.4 2023-11-17 [1] CRAN (R 4.2.3) #> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.2.0) #> evaluate 0.23 2023-11-01 [1] CRAN (R 4.2.0) #> fansi 1.0.6 2023-12-08 [1] CRAN (R 4.2.3) #> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.0) #> fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.2.3) #> fs 1.6.4 2024-04-25 [1] CRAN (R 4.2.3) #> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0) #> geomnet * 0.3.1 2024-08-05 [1] Github (sctyner/geomnet@030537d) #> GGally * 2.2.1 2024-02-14 [1] CRAN (R 4.2.3) #> ggplot2 * 3.5.1 2024-04-23 [1] CRAN (R 4.2.3) #> ggstats 0.6.0 2024-04-05 [1] CRAN (R 4.2.3) #> glue 1.7.0 2024-01-09 [1] CRAN (R 4.2.3) #> gtable 0.3.4 2023-08-21 [1] CRAN (R 4.2.0) #> highr 0.11 2024-05-26 [1] CRAN (R 4.2.3) #> hms 1.1.3 2023-03-21 [1] CRAN (R 4.2.0) #> htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.2.3) #> htmlwidgets 1.6.2 2023-03-17 [1] CRAN (R 4.2.0) #> httpuv 1.6.11 2023-05-11 [1] CRAN (R 4.2.0) #> httr 1.4.6 2023-05-08 [1] CRAN (R 4.2.0) #> jsonlite 1.8.8 2023-12-04 [1] CRAN (R 4.2.3) #> knitr 1.47 2024-05-29 [1] CRAN (R 4.2.3) #> later 1.3.1 2023-05-02 [1] CRAN (R 4.2.0) #> lattice 0.20-45 2021-09-22 [1] CRAN (R 4.2.3) #> lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.2.0) #> lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.2.3) #> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0) #> memoise 2.0.1 2021-11-26 [1] CRAN (R 4.2.0) #> mime 0.12 2021-09-28 [1] CRAN (R 4.2.0) #> miniUI 0.1.1.1 2018-05-18 [1] CRAN (R 4.2.0) #> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0) #> network 1.18.2 2023-12-05 [1] CRAN (R 4.2.3) #> pillar 1.9.0 2023-03-22 [1] CRAN (R 4.2.0) #> pkgbuild 1.4.4 2024-03-17 [1] CRAN (R 4.2.3) #> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0) #> pkgload 1.3.4 2024-01-16 [1] CRAN (R 4.2.3) #> plotly 4.10.4 2024-01-13 [1] CRAN (R 4.2.3) #> plyr 1.8.8 2022-11-11 [1] CRAN (R 4.2.0) #> profvis 0.3.7 2020-11-02 [1] CRAN (R 4.2.0) #> promises 1.2.0.1 2021-02-11 [1] CRAN (R 4.2.0) #> purrr 1.0.2 2023-08-10 [1] CRAN (R 4.2.0) #> R.cache 0.16.0 2022-07-21 [1] CRAN (R 4.2.0) #> R.methodsS3 1.8.2 2022-06-13 [1] CRAN (R 4.2.0) #> R.oo 1.26.0 2024-01-24 [1] CRAN (R 4.2.3) #> R.utils 2.12.3 2023-11-18 [1] CRAN (R 4.2.3) #> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0) #> RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.2.0) #> Rcpp 1.0.11 2023-07-06 [1] CRAN (R 4.2.0) #> readr 2.1.4 2023-02-10 [1] CRAN (R 4.2.0) #> remotes 2.4.2 2021-11-30 [1] CRAN (R 4.2.0) #> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0) #> rlang 1.1.3 2024-01-10 [1] CRAN (R 4.2.3) #> rmarkdown 2.27 2024-05-17 [1] CRAN (R 4.2.3) #> rstudioapi 0.16.0 2024-03-24 [1] CRAN (R 4.2.3) #> scales 1.3.0 2023-11-28 [1] CRAN (R 4.2.3) #> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.2.0) #> shiny 1.7.4 2022-12-15 [1] CRAN (R 4.2.0) #> sna 2.7-2 2023-12-06 [1] CRAN (R 4.2.3) #> statnet.common 4.9.0 2023-05-24 [1] CRAN (R 4.2.0) #> stringi 1.8.4 2024-05-06 [1] CRAN (R 4.2.3) #> stringr 1.5.1 2023-11-14 [1] CRAN (R 4.2.3) #> styler 1.10.2 2023-08-29 [1] CRAN (R 4.2.0) #> tibble 3.2.1 2023-03-20 [1] CRAN (R 4.2.0) #> tidyr 1.3.1 2024-01-24 [1] CRAN (R 4.2.3) #> tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.2.3) #> tzdb 0.4.0 2023-05-12 [1] CRAN (R 4.2.3) #> urlchecker 1.0.1 2021-11-30 [1] CRAN (R 4.2.0) #> usethis 2.2.3 2024-02-19 [1] CRAN (R 4.2.3) #> utf8 1.2.4 2023-10-22 [1] CRAN (R 4.2.0) #> vctrs 0.6.5 2023-12-01 [1] CRAN (R 4.2.3) #> viridisLite 0.4.2 2023-05-02 [1] CRAN (R 4.2.0) #> withr 3.0.0 2024-01-16 [1] CRAN (R 4.2.3) #> xfun 0.44 2024-05-15 [1] CRAN (R 4.2.3) #> xml2 1.3.6 2023-12-04 [1] CRAN (R 4.2.3) #> xtable 1.8-4 2019-04-21 [1] CRAN (R 4.2.0) #> yaml 2.3.8 2023-12-11 [1] CRAN (R 4.2.3) #> #> [1] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library #> #> ────────────────────────────────────────────────────────────────────────────── ```
iago-pssjd commented 2 months ago

@slowkow

Is there some reason that you must use old (unsupported) versions?

I was looking for the version (release/commit) either of ggplot2 or ggrepel from which geomnet stopped working. And to test geomnet with a version of ggrepel working, I need a ggplot2 release compatible with ggrepel release.

With the latest versions, I don't get the same error you do. I get an error with geomnet code:

Indeed, those errors are part of the cause geomnet becoming archived. Try

remotes::install_github("iago-pssjd/geomnet@archivedCRANissues")

Have you considered using ggnet2 or ggnetwork or ggraph or any other packages?

No, my goal was not to use such code or an equivalent one, but got geomnet again on CRAN after solving all the issues.

iago-pssjd commented 2 months ago

Debugging makeContent.textrepeltree(x) (see line numbered 20 in traceback shown above), I have just found that the issue occurs when

https://github.com/slowkow/ggrepel/blob/0f34805b28abc239cc96fc497e7461910c15086a/R/geom-text-repel.R#L406-L407

(line added in commit cc34e6d) where the data.frame x$data does not contain a variable point.size.

Do you have any suggestion on changes I should do on https://github.com/sctyner/geomnet/blob/master/R/geom-net.r to solve this issue? Or is it fixable on your side?

slowkow commented 2 months ago

I'm not sure. Maybe the GeomTextRepel$draw_panel() function needs to check for the presence of a point.size column and then add the column if it is missing?

iago-pssjd commented 2 months ago

I believe I solved the issue adding this column as NA, so I close. Thanks!

iago-pssjd commented 2 months ago

Current situation. I thought I had solved the issue, however I did not have installed the most recent version of ggrepel, but a commit previous to 0.9.0.

Now, with commit 8d16037, the reproducible example "sometimes works, most doesn't"... and I was not able to find till now the rule to describe how to reproduce when it works.

But, in general, the error it produces is

library(dplyr)
library(geomnet)
data(theme_elements)
# data step
TEnet <- fortify(as.edgedf(theme_elements$edges[,c("parent", "child")]), theme_elements$vertices)
# create a degree variable for use later
TEnet <- TEnet %>%
  group_by(from_id) %>%
  mutate(degree = sqrt(10 * n() + 1))
# plot
ggplot(data = TEnet,
       aes(from_id = from_id, to_id = to_id)) +
  geom_net(layout.alg = "fruchtermanreingold",
    aes(fontsize = degree), directed = TRUE,
    labelon = TRUE, size = 1, labelcolour = 'black',
    ecolour = "grey70", arrowsize = 0.5,
    linewidth = 0.5, repel = TRUE) +
  theme_net() +
  xlim(c(-0.05, 1.05))
Error in check.length(gparname) : 
  'gpar' element 'lwd' must not be length 0
> traceback()
34: stop(gettextf("'gpar' element '%s' must not be length 0", gparname), 
        domain = NA)
33: check.length(gparname)
32: numnotnull("lwd")
31: validGP(list(...))
30: gpar(col = scales::alpha(row$segment.colour %||% row$colour, 
        row$segment.alpha %||% row$alpha), lwd = row$segment.size * 
        .pt, lty = row$segment.linetype %||% 1)
29: gTree(x1 = x1, y1 = y1, x2 = x2, y2 = y2, curvature = curvature, 
        angle = angle, ncp = ncp, shape = shape, square = square, 
        squareShape = squareShape, inflect = inflect, arrow = arrow, 
        open = open, debug = debug, name = name, gp = gp, vp = vp, 
        cl = "curve")
28: curveGrob(x1 = int[1], y1 = int[2], x2 = point_int[1], y2 = point_int[2], 
        default.units = "native", curvature = segment.curvature, 
        angle = segment.angle, ncp = segment.ncp, gp = segment.gp, 
        name = sprintf("segmentrepelgrob%s", i), arrow = arrow)
27: makeTextRepelGrobs(i, x$lab[i], x = unit(repel$x[i], "native"), 
        y = unit(repel$y[i], "native"), x.orig = row$x, y.orig = row$y, 
        rot = row$angle, box.padding = x$box.padding, point.size = point_size[i], 
        point.padding = x$point.padding, segment.curvature = row$segment.curvature, 
        segment.angle = row$segment.angle, segment.ncp = row$segment.ncp, 
        text.gp = gpar(col = scales::alpha(row$colour, row$alpha), 
            fontsize = row$size * .pt, fontfamily = row$family, fontface = row$fontface, 
            lineheight = row$lineheight), segment.gp = gpar(col = scales::alpha(row$segment.colour %||% 
            row$colour, row$segment.alpha %||% row$alpha), lwd = row$segment.size * 
            .pt, lty = row$segment.linetype %||% 1), arrow = x$arrow, 
        min.segment.length = x$min.segment.length, hjust = row$hjust, 
        vjust = row$vjust)
26: FUN(X[[i]], ...)
25: lapply(seq_along(valid_strings), function(i) {
        row <- x$data[i, , drop = FALSE]
        makeTextRepelGrobs(i, x$lab[i], x = unit(repel$x[i], "native"), 
            y = unit(repel$y[i], "native"), x.orig = row$x, y.orig = row$y, 
            rot = row$angle, box.padding = x$box.padding, point.size = point_size[i], 
            point.padding = x$point.padding, segment.curvature = row$segment.curvature, 
            segment.angle = row$segment.angle, segment.ncp = row$segment.ncp, 
            text.gp = gpar(col = scales::alpha(row$colour, row$alpha), 
                fontsize = row$size * .pt, fontfamily = row$family, 
                fontface = row$fontface, lineheight = row$lineheight), 
            segment.gp = gpar(col = scales::alpha(row$segment.colour %||% 
                row$colour, row$segment.alpha %||% row$alpha), lwd = row$segment.size * 
                .pt, lty = row$segment.linetype %||% 1), arrow = x$arrow, 
            min.segment.length = x$min.segment.length, hjust = row$hjust, 
            vjust = row$vjust)
    })
24: makeContent.textrepeltree(x)
23: makeContent(x)
22: drawGTree(x)
21: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
20: grid.draw.gTree(x$children[[i]], recording = FALSE)
19: grid.draw(x$children[[i]], recording = FALSE)
18: drawGTree(x)
17: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
16: grid.draw.gTree(x$children[[i]], recording = FALSE)
15: grid.draw(x$children[[i]], recording = FALSE)
14: drawGTree(x)
13: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
12: grid.draw.gTree(x$children[[i]], recording = FALSE)
11: grid.draw(x$children[[i]], recording = FALSE)
10: drawGTree(x)
9: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
8: grid.draw.gTree(x$children[[i]], recording = FALSE)
7: grid.draw(x$children[[i]], recording = FALSE)
6: drawGTree(x)
5: recordGraphics(drawGTree(x), list(x = x), getNamespace("grid"))
4: grid.draw.gTree(gtable)
3: grid.draw(gtable)
2: print.ggplot(x)
1: (function (x, ...) 
   UseMethod("print"))(x)
slowkow commented 2 months ago

You need to share what is inside the geom_net() function.

iago-pssjd commented 2 months ago

Currently

geom_net <- function (
  mapping = NULL, data = NULL, stat = "net", position = "identity", show.legend = NA, na.rm = FALSE, inherit.aes = TRUE,
  layout.alg="kamadakawai", layout.par=list(), directed = FALSE, fiteach=FALSE,  selfloops = FALSE, singletons = TRUE,
  alpha = 0.25, ecolour=NULL, ealpha=NULL, arrow=NULL, arrowgap=0.01, arrowsize=1,
  labelon=FALSE, labelcolour=NULL, labelgeom = 'text', repel = FALSE,
  vertices=NULL, ...) {
##browser()
    ggplot2::layer(
    geom = GeomNet, mapping = mapping,  data = data, stat = stat,
    position = position, show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, layout.alg=layout.alg, layout.par=layout.par, fiteach=fiteach, labelon=labelon, labelgeom=labelgeom,
                  ecolour = ecolour, ealpha=ealpha, arrow=arrow, arrowgap=arrowgap, directed=directed, repel=repel,
                  arrowsize=arrowsize, singletons=singletons,
                  labelcolour=labelcolour, vertices=vertices, selfloops = selfloops,
                  ...)
  )
}

and

GeomNet <- ggplot2::ggproto("GeomNet", ggplot2::Geom,
  required_aes = c("x", "y"),

  default_aes = ggplot2::aes(width = 0.75, linetype = "solid", fontsize=5, label = NULL,
                             shape = 19, colour = "grey40", arrowsize = 1,
                             size = 4, fill = NA, alpha = NA, stroke = 0.5,
                             linewidth=1, angle=0, vjust=0, hjust=0.5, curvature = 0),

  draw_key = function(data, params, size)  {
# #browser()
    draw_arrow <-  NULL
    if (params$directed) {
      if (any(data$curvature != 0)){
        draw_arrow <- arrow(length = unit(params$arrowsize*5,"points"), type="open")
        }
      else{
        draw_arrow <- arrow(length = unit(params$arrowsize*5,"points"), type="closed")
      }
   }

    with(data, grobTree(
      grid::pointsGrob(x = c(.15, .85), y = c(.85, .15),
                       pch = data$shape, size = unit(data$size, "points"),
                       gp = grid::gpar(col = alpha(data$colour, data$alpha),
                                       fill = alpha(data$fill, data$alpha),
                                       fontsize = data$size * .pt + data$stroke * .stroke/2,
                                       lwd = data$stroke * .stroke/2)
                       ),
     grid::segmentsGrob(x0 = .15, y0 = .85 ,
                        x1 = ifelse(is.null(draw_arrow), .85, .82),
                        y1 = ifelse(is.null(draw_arrow), .15, .18),
                     gp = grid::gpar(
                       col = alpha(data$colour, data$alpha),
                       fill = alpha(data$colour, data$alpha),
                       lwd = data$linewidth,
                       lty = data$linetype,
                       lineend="butt"),
                     arrow = draw_arrow
                     )

    ))
  },

  setup_data = function(data, params, mapping) {
#    cat("setup_data geom_net\n")

##BROWSER()
    data$from <- as.character(data$from)
    data$to <- as.character(data$to)
    selfie <- (data$from == data$to) & (params$selfloops == TRUE)
  # maximum radius is at the moment hard coded to 0.05
    data$ymax = max(with(data, pmax(y, yend) + 2*0.05*selfie))
    data$xmax = with(data, pmax(x, xend) + 2*0.05*selfie)

    data$from <- factor(data$from)
    data$to <- factor(data$to)

    data
  },

  draw_panel = function(data, panel_scales, coord,  ecolour=NULL, ealpha=NULL, arrow=NULL, arrowgap=0.01,
                        directed=FALSE, arrowsize=1, repel = FALSE, singletons = TRUE,
                        labelon=FALSE, labelgeom='text', labelcolour=NULL, selfloops = FALSE) {

#     browser()
#    data$self <- as.character(data$to) == as.character(data$from)
    edges <- data.frame(
      x = data$x,
      xend = data$xend,
      y = data$y,
      yend = data$yend,
      weight = data$weight,
      colour = ecolour %||% ifelse(data$.samegroup, data$colour, "grey40"),
      linewidth = data$linewidth %||% (data$size / 4),
      nodesize = data$size,
      alpha = ealpha %||% data$alpha,
      linetype=data$linetype,
      stroke = data$stroke,
      selfie = data$.selfie,
      stringsAsFactors = FALSE
    )

    selfy <- subset(edges, selfie == TRUE)
    edges <- subset(edges, selfie != TRUE) # what are we going to do with self references?
    edges <- subset(edges, !is.na(xend))

#   if (singletons) {
    vertices <- data.frame(
      x = data$x,
      y = data$y,
      colour = data$colour,
      shape = data$shape,
      size = data$size,
      fill = NA,
      alpha = data$alpha,
      stroke = 0.5,
      stringsAsFactors = FALSE
    )
    vertices <- unique(vertices)
   # } else {
   #   vertices <- data.frame(
   #     from = data$from,
   #     to = data$to,
   #     x = data$x,
   #     y = data$y,
   #     colour = data$colour,
   #     shape = data$shape,
   #     size = data$size,
   #     fill = NA,
   #     alpha = data$alpha,
   #     stroke = 0.5,
   #     stringsAsFactors = FALSE
   #   )
   #   all_singletons <- vertices$from[vertices$from == vertices$to]
   #   true_singletons <- all_singletons[!all_singletons %in% vertices$to[vertices$from != vertices$to]]
   #   vertices <- vertices[!vertices$from %in% true_singletons,]
   #   vertices <- vertices[,-c(1,2)]
   #   vertices <- unique(vertices)
   # }

    if (directed) {
      if (any(data$curvature != 0)) {
        if (is.null(arrow)) arrow = arrow(length = unit(data$arrowsize*10,"points"), type="open")
      } else {
        if (is.null(arrow)) arrow = arrow(length = unit(data$arrowsize*10,"points"), type="closed")
      }
      arrowgap <- with(edges, arrowgap/sqrt((xend-x)^2+(yend-y)^2))
      edges <- transform(
        edges,
        xend = x + (1-arrowgap)*(xend-x),
        yend = y + (1-arrowgap)*(yend-y),
        x = x + arrowgap*(xend-x),
        y = y + arrowgap*(yend-y)
      )
    } else arrow=NULL
#
    if (any(data$curvature != 0)){
      edges_draw <- GeomCurve$draw_panel(edges, panel_scales,
                                         coord, arrow=arrow, curvature=data$curvature[1], angle=90)
    }
      else {edges_draw <- GeomSegment$draw_panel(edges, panel_scales, coord, arrow, lineend = "round")}

#

    selfies_draw <- NULL
    if ((nrow(selfy) > 0) & selfloops) {
      selfy$radius <- min(0.04, 1/sqrt(nrow(vertices)))
      selfy <- transform(selfy,
                           x = x + (radius + nodesize/(100*.pt) + linewidth/100)/sqrt(2),
                           y = y + (radius + nodesize/(100*.pt) + linewidth/100)/sqrt(2),
                           linewidth = linewidth*.pt,
                           fill = NA
      )
      selfies_draw <- GeomCircle$draw_panel(selfy, panel_scales, coord)
    }

    selfies_arrows <- NULL
    if ((nrow(selfy) > 0) & selfloops & directed) {
#
      selfy_arrows <- transform (
        selfy,
        xend = x - 0.5* arrowsize*.pt/100,
        yend = y-0.04 - linewidth/100,
        y = y-0.04 - linewidth/100
      )
      selfies_arrows <- GeomSegment$draw_panel(selfy_arrows, panel_scales, coord,
                                               arrow=arrow)
    }

    label_grob <- NULL
    if (labelon | !is.null(data$label)) {
 #     if (singletons){
      labels <- data.frame(
        x = data$x,
        y = data$y,
        label = data$label %||% data$from,
        colour = labelcolour %||% data$colour,
        shape = data$shape,
        size = data$fontsize,
        angle = data$angle,
        alpha = data$alpha,
        hjust = data$hjust,
        fill = data$colour,
        vjust = data$vjust,
        stringsAsFactors = FALSE
      )
      labels <- unique(labels)
      # } else {
      #   labels <- data.frame(
      #     from = data$from,
      #     to = data$to,
      #     x = data$x,
      #     y = data$y,
      #     label = data$label %||% data$from,
      #     colour = labelcolour %||% data$colour,
      #     shape = data$shape,
      #     size = data$fontsize,
      #     angle = data$angle,
      #     alpha = data$alpha,
      #     hjust = data$hjust,
      #     fill = data$colour,
      #     vjust = data$vjust,
      #     stringsAsFactors = FALSE
      #   )
      #   labels <- labels[!labels$from %in% true_singletons,]
      #   labels <- labels[,-c(1,2)]
      #   labels <- unique(labels)
      # }
#       if (labelgeom=='label'){
#       label_grob <- GeomLabel$draw_panel(labels, panel_scales, coord)
#       }
#       else {label_grob <- GeomText$draw_panel(labels, panel_scales, coord)}
#     }

      if (labelgeom=='label'){
        if(repel){
          label_grob <- ggrepel::GeomLabelRepel$draw_panel(labels, panel_scales, coord)
        } else {label_grob <- ggplot2::GeomLabel$draw_panel(labels, panel_scales, coord)}
      } else {
        if(repel){
      labels$point.size <- 1
      labels$bg.colour <- NA
      labels$bg.r <- 0.1
          label_grob <- ggrepel::GeomTextRepel$draw_panel(labels, panel_scales, coord)
        } else{label_grob <- ggplot2::GeomText$draw_panel(labels, panel_scales, coord)}
      }

  }

    ggplot2:::ggname("geom_net", grobTree(
      edges_draw,
      selfies_draw,
      selfies_arrows,
      GeomPoint$draw_panel(vertices, panel_scales, coord),
      label_grob
    ))
  }
)

I added the lines

https://github.com/iago-pssjd/geomnet/blob/c5a8609fb68c646707ce0747c8ed2a6b09f04a64/R/geom-net.r#L411-L413

to solve other issues I detected due to new variables you added to ggrepel, although if this should be done in other way, please let me know.

iago-pssjd commented 2 months ago

At some point between 1a267f4 and 8d16037 some variables in makeContent.textrepeltree, when grobs is defined through

grobs <- lapply(seq_along(valid_strings), function(i) {

change their definition, I mean, for example for segment.gp, row$segment.colour or row$segment.size are used instead of x$segment.colour and x$segment.size respectively, where row <- x$data[i, , drop = FALSE].

Variables which were saved at x are not there anymore and are supposed to be at x$data, assumption which I suspect is the cause of the last error reported:

Error in check.length(gparname) : 
  'gpar' element 'lwd' must not be length 0

What is the reason for this change and how should we deal with it? (I will try to investigate too)

Thanks!

slowkow commented 2 months ago

If you want to use the lower-level functions, then you will need to study the ggrepel code and re-create all of the same columns in your package to match the ones that are used internally in the ggrepel code. If that internal ggrepel code changes, then your code will break.

I didn't really expect other packages would be calling ggrepel::GeomTextRepel$draw_panel(), because I think of it as an internal function. My expectation was that users would be using geom_text_repel() instead (which eventually calls that internal function with all the right parameters).

Do you think there is a way to modify ggrepel code so that you can use the internal draw_panel() function and not worry about the way variables are named in ggrepel? If there is, maybe that would be a nice modification for other packages that depend on ggrepel.

iago-pssjd commented 2 months ago

I believe I fixed the issue, so I will close it for the moment. I had to add to the data passed to ggrepel::GeomTextRepel$draw_panel (labels) (many of) the new variables you added in https://github.com/slowkow/ggrepel/blob/0f34805b28abc239cc96fc497e7461910c15086a/R/geom-text-repel.R#L238-L243

Thanks!

slowkow commented 2 months ago

@iago-pssjd With your solution, the geomnet code will break again when ggrepel changes one of those variables.

I think it might be possible to find another solution, where geomnet does not need to be aware of all these variables, so it will be more robust to future changes in the ggrepel code. This would probably involve changing the ggrepel draw_panel() function in some way, but I will leave that to you to explore if you wish. For example, maybe the draw_panel() function could check for missing columns and add them if they are missing.

For now, I'm glad you found a path forward. Good luck!

iago-pssjd commented 2 months ago

@slowkow That's a good point.

I have just detected that instead of adding each variable in a separate step ggrepel::GeomTextRepel$default_aes is a list with the names and defaults of default_aes and this coul be used either

  1. by me in geomnet
  2. as you suggest, maybe in ggrepel::GeomTextRepel$draw_panel()
  3. in the function where I get the errors, ggrepel:::makeContent.textrepeltree