thomasp85 / ggforce

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

geom_mark_hull() label returns error in lines[[1]] - lines[[2]]: non-conformable arrays #291

Closed mark-druffel closed 9 months ago

mark-druffel commented 1 year ago

Hi there, thanks for all your incredible work on the ggplot2 ecosystem!

I am having an issue when using geom_mark_hull() with the label aesthetic. I am not at all experienced with this function so I apologize if I'm using it improperly or in an unexpected way :grimacing: : I created a reproducible example at the bottom.

Referring to the reprex, the first plot works as you can see. The second graph fails and gives an error that appears to come from elbow(). The third graph works because I remove the label aesthetic. The issue seems to occur when groups overlap, I'm guessing something is happening when the elbow of multiple labels intersect :shrug:

library(tidygraph)
#> 
#> Attaching package: 'tidygraph'
#> The following object is masked from 'package:stats':
#> 
#>     filter
library(ggraph)
#> Loading required package: ggplot2
library(ggforce)

graph <- highschool |> 
  filter(year == 1958) |> 
  as_tbl_graph() |>  
  mutate(popularity = centrality_degree(mode = "in"),
         groups_2 = as.factor(group_edge_betweenness(n_groups = 2)),
         groups_5 = as.factor(group_edge_betweenness(n_groups = 5)))

graph |> 
  ggraph(layout = "stress") + 
  geom_edge_link0(width = 0.15) +
  geom_node_point(aes(size = popularity), show.legend = F) +
  geom_mark_hull(
    mapping = aes(x = x, y = y, group = groups_2, fill = groups_2, label = groups_2),
    concavity = 2,
    expand = unit(1, "mm"),
    alpha = 0.2
  ) +
  scale_fill_brewer(palette = "Set1") +
  labs(x = NULL, y = NULL, fill = "Group") +
  theme_void()
#> Warning: Using the `size` aesthetic in this geom was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` in the `default_aes` field and elsewhere instead.


graph |> 
  ggraph(layout = "stress") + 
  geom_edge_link0(width = 0.15) +
  geom_node_point(aes(size = popularity), show.legend = F) +
  geom_mark_hull(
    mapping = aes(x = x, y = y, group = groups_5, fill = groups_5, label = groups_5),
    concavity = 2,
    expand = unit(1, "mm"),
    alpha = 0.2
  ) +
  scale_fill_brewer(palette = "Set1") +
  labs(x = NULL, y = NULL, fill = "Group") +
  theme_void()
#> Warning in xmin - x: longer object length is not a multiple of shorter object
#> length
#> Warning in xmax - x: longer object length is not a multiple of shorter object
#> length
#> Warning in ymin - y: longer object length is not a multiple of shorter object
#> length
#> Warning in ymax - y: longer object length is not a multiple of shorter object
#> length
#> Error in lines[[1]] - lines[[2]]: non-conformable arrays

graph |> 
  ggraph(layout = "stress") + 
  geom_edge_link0(width = 0.15) +
  geom_node_point(aes(size = popularity), show.legend = F) +
  geom_mark_hull(
    mapping = aes(x = x, y = y, group = groups_5, fill = groups_5),
    concavity = 2,
    expand = unit(1, "mm"),
    alpha = 0.2
  ) +
  scale_fill_brewer(palette = "Set1") +
  labs(x = NULL, y = NULL, fill = "Group") +
  theme_void()

Created on 2022-12-23 with reprex v2.0.2

arcresu commented 1 year ago

I encountered this same issue when drawing hulls on a ggraph.

Using the same graph from the reprex above, and a slightly more minimal example:

graph |> 
  ggraph(layout = "stress") + 
  geom_mark_hull(
    mapping = aes(x = x, y = y, group = groups_5, fill = groups_5, label = groups_5),
    # con.type = "none",
    expand = unit(1, "mm"),
  )

Uncommenting con.type = "none" fixes the issue, which is more evidence that the issue is the label connectors. con.type = "straight" gives a slightly different error message but the same bug.

If you increase expand to >= 3 mm or add a radius >= 8 mm then a different bug is triggered regardless of the connector type:

Error in anchors[[i]] : subscript out of bounds

traced to: https://github.com/thomasp85/ggforce/blob/9be635c582559f016254b111770a61e4b4aa0958/R/mark_label.R#L35

yoda-vid commented 1 year ago

I've seen both of these errors (Error in lines[[1]] - lines[[2]]: non-conformable arrays and Error in anchors[[i]] : subscript out of bounds), even with the con.type = "none" workaround. My only workaround has been to remove labels completely and manually add labels in ggplot. Has anyone found other workarounds?

thomasp85 commented 9 months ago

I'm afraid I can't provoke these errors on my own system. geom_mark_*() are quite difficult to reproduce since they are dependent on the output size. Can one of you provide a reprex where you get the error during rendering on a fixed dimension graphic device (e.g. calling ragg::agg_png(width = ..., height = ...)) before plotting

arcresu commented 9 months ago

Sure, I reproduced it on two different machines (one Linux, one Mac) using cairo_pdf(width = 7, height = 7).

library(ggforce)
library(ggraph)
library(tidygraph)

sessionInfo()

cairo_pdf(width = 7, height = 7)

graph <- highschool |> 
  filter(year == 1958) |> 
  as_tbl_graph() |>  
  mutate(popularity = centrality_degree(mode = "in"),
         groups_2 = as.factor(group_edge_betweenness(n_groups = 2)),
         groups_5 = as.factor(group_edge_betweenness(n_groups = 5)))

graph |> 
  ggraph(layout = "stress") + 
  geom_mark_hull(
    mapping = aes(x = x, y = y, group = groups_5, fill = groups_5, label = groups_5),
    # con.type = "none",
    expand = unit(1, "mm"),
  )

Run with R --vanilla CMD BATCH test.R:

R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

[Previously saved workspace restored]

> library(ggforce)
Loading required package: ggplot2
> library(ggraph)
> library(tidygraph)

Attaching package: ‘tidygraph’

The following object is masked from ‘package:stats’:

    filter

> 
> sessionInfo()
R version 4.3.2 (2023-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux trixie/sid

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.25.so;  LAPACK version 3.11.0

locale:
 [1] LC_CTYPE=en_AU.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_AU.UTF-8        LC_COLLATE=en_AU.UTF-8    
 [5] LC_MONETARY=en_AU.UTF-8    LC_MESSAGES=en_AU.UTF-8   
 [7] LC_PAPER=en_AU.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_AU.UTF-8 LC_IDENTIFICATION=C       

time zone: [...]
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] tidygraph_1.3.0 ggraph_2.1.0    ggforce_0.4.1   ggplot2_3.4.4  

loaded via a namespace (and not attached):
 [1] vctrs_0.6.5        cli_3.6.2          rlang_1.1.2        ggrepel_0.9.4     
 [5] purrr_1.0.2        generics_0.1.3     glue_1.6.2         colorspace_2.1-0  
 [9] gridExtra_2.3      viridis_0.6.4      scales_1.3.0       fansi_1.0.6       
[13] grid_4.3.2         tweenr_2.0.2       munsell_0.5.0      tibble_3.2.1      
[17] MASS_7.3-60        lifecycle_1.0.4    compiler_4.3.2     graphlayouts_1.0.2
[21] igraph_1.6.0       dplyr_1.1.4        polyclip_1.10-6    Rcpp_1.0.11       
[25] pkgconfig_2.0.3    tidyr_1.3.0        digest_0.6.33      farver_2.1.1      
[29] viridisLite_0.4.2  R6_2.5.1           tidyselect_1.2.0   utf8_1.2.4        
[33] pillar_1.9.0       magrittr_2.0.3     tools_4.3.2        withr_2.5.2       
[37] gtable_0.3.4      
> 
> cairo_pdf(width = 7, height = 7)
> 
> graph <- highschool |> 
+   filter(year == 1958) |> 
+   as_tbl_graph() |>  
+   mutate(popularity = centrality_degree(mode = "in"),
+          groups_2 = as.factor(group_edge_betweenness(n_groups = 2)),
+          groups_5 = as.factor(group_edge_betweenness(n_groups = 5)))
> 
> graph |> 
+   ggraph(layout = "stress") + 
+   geom_mark_hull(
+     mapping = aes(x = x, y = y, group = groups_5, fill = groups_5, label = groups_5),
+     # con.type = "none",
+     expand = unit(1, "mm"),
+   )
Error in lines[[1]] - lines[[2]] : non-conformable arrays
Calls: <Anonymous> ... makeContent -> makeContent.hull_enc -> make_label -> con_fun
In addition: Warning messages:
1: In xmin - x :
  longer object length is not a multiple of shorter object length
2: In xmax - x :
  longer object length is not a multiple of shorter object length
3: In ymin - y :
  longer object length is not a multiple of shorter object length
4: In ymax - y :
  longer object length is not a multiple of shorter object length
Execution halted

Uncommenting the con.type line allows it to run without error.

thomasp85 commented 9 months ago

Ah - this was fixed by an unrelated PR. Should work on the dev version