Search code examples
rggplot2word-wrapggalluvial

R: How to wrap character columns to print to 2 lines in ggplot, but they're NOT titles?


There are myriad similar questions for splitting character strings to 2 lines with \n and cat (e.g.), paste (e.g.), strwrap (e.g.), and writeLines (e.g.). But all of these either anticipate the output being explicitly printed immediately thereafter, or printing will happen automatically by plot/ggplot; in the latter case, examples typically relate to (axis) titles.

I've created an alluvial plot which creates the following plot:

Alluvial

From the following post-processed table of data:

table

I'm trying to force certain cells in "FnGpRealm" and "Effect Type" columns to wrap to two lines. An example of the various similar approached I've tried (relevant code chunk only):

dplyr::mutate(FnGpRealm = stringr::str_replace_all(string = FnGpRealm,
                                                            pattern = "Ma: Inshore/Shelf",
                                                            replacement = paste0("Ma:", "\n", "Inshore/Shelf"))

I've also tried "Ma:\nInshore/Shelf" as the replacement, used "/n" as the collapse in a paste, wrapped writeLines around the whole str_replace_all block... all to no avail. If I try to print / writeLines the column onto itself after adding the /n, it kills the column completely:

tmp$FnGpRealm

1 "Ma:\nInshore/Shelf"

tmp$FnGpRealm <- writeLines(tmp$FnGpRealm)

tmp$FnGpRealm

NULL. Warning message: Unknown or uninitialised column: FnGpRealm.

Based on the extent to which I understand how \n works in all of this, it seems character strings with regex special characters like \n get autochecked by some functions (cat, paste, writeLines), probably also within (gg)plot calls, which then wrap them to multiple lines when detected. But since ggalluvial populates its elements using character cells which evidently aren't autochecked the same way... is there a way to wrap those strings within the column natively? So when ggalluvial prints them, they're already wrapped? Or is this not a thing?

My fallback is just to rename stuff shorter like I already did with macro/mesopredator to Ma/Me. But that kinda sucks.

Thanks in advance!

edit: dput(head(df)) output:

structure(list(Effect Type = structure(c(1L, 1L, 2L, 2L, 2L, 3L), levels = c("Top-down: Direct Predation", "Top-down: Risk Effects", "Top-down: Trophic Cascade", "Competition", "BU:NVS:SAF", "BU:NVS:EAE" ), class = c("ordered", "factor")), Effect Size = structure(c(1L, 2L, 1L, 2L, 3L, 1L), levels = c("3", "2", "1"), class = c("ordered", "factor")), Count = c(16L, 8L, 32L, 11L, 2L, 17L)), class = c("grouped_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -6L), groups = structure(list( Effect Type = structure(1:3, levels = c("Top-down: Direct Predation", "Top-down: Risk Effects", "Top-down: Trophic Cascade", "Competition", "BU:NVS:SAF", "BU:NVS:EAE"), class = c("ordered", "factor" )), .rows = structure(list(1:2, 3:5, 6L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame" ), row.names = c(NA, -3L), .drop = TRUE))


Solution

  • I think stringr::str_wrap directly inside aes when specifying the label aesthetic should be fine:

    library(ggalluvial)
    
    ggplot(data = data,
           aes(axis1 = FnGpRealm, axis2 = EffectType, y = Count)) +
      geom_alluvium(aes(fill = factor(EffectSize)), alpha = 0.7) +
      geom_stratum(fill = 'white') +
      geom_text(stat = "stratum",
                aes(label = stringr::str_wrap(after_stat(stratum), 12))) +
      scale_x_discrete(expand = c(0.15, 0.05)) +
      scale_fill_manual('Effect\nSize',
                        values = c('3' = '#55b69a', '2' = '#9894c6', 
                                   '1' = '#e38642')) +
      theme(axis.line.y = element_line(),
            panel.background = element_blank(),
            legend.position = c(0.2, 0.8))
    

    enter image description here


    Data OCR'd from table in OP

    data <- structure(list(FnGpRealm = c("Ma: Inshore/Shelf", "Ma: Inshore/Shelf", 
    "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", 
    "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", 
    "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", "Ma: Inshore/Shelf", 
    "Ma: Inshore/Shelf", "Ma: Pelagic", "Ma: Pelagic", "Me: Inshore/Shelf", 
    "Me: Inshore/Shelf", "Me: Inshore/Shelf", "Me: Inshore/Shelf", 
    "Me: Inshore/Shelf", "Me: Inshore/Shelf", "Me: Inshore/Shelf", 
    "Me: Inshore/Shelf", "Me: Inshore/Shelf", "Me: Inshore/Shelf", 
    "Me: Inshore/Shelf", "Me: Inshore/Shelf", "Me: Inshore/Shelf", 
    "Me: Inshore/Shelf", "Me: Inshore/Shelf", "Me: Inshore/Shelf", 
    "Me: Pelagic", "Me: Pelagic", "Me: Pelagic", "Me: Pelagic"), 
        EffectType = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 3L, 3L, 
        4L, 5L, 5L, 6L, 1L, 3L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 
        4L, 4L, 4L, 5L, 5L, 6L, 1L, 1L, 3L, 4L, 4L), .Label = c("Top-down: Direct Predation", 
        "Top-down: Risk Effects", "Top-down: Trophic Cascade", "Competition", 
        "BU:NVS:SAF", "BU:NVS:EAE"), class = "factor"), EffectSize = c(3L, 
        2L, 3L, 1L, 3L, 2L, 1L, 1L, 3L, 2L, 1L, 3L, 3L, 3L, 3L, 2L, 
        1L, 3L, 2L, 1L, 3L, 2L, 1L, 3L, 1L, 1L, 1L, 1L, 3L, 3L, 2L, 
        3L, 3L, 2L), Count = c(16L, 8L, 32L, 11L, 2L, 17L, 4L, 4L, 
        1L, 1L, 2L, 1L, 1L, 4L, 2L, 3L, 6L, 4L, 1L, 1L, 4L, 2L, 1L, 
        1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 1L)), row.names = c(NA, 
    -34L), class = "data.frame")