Adaption of circlize::chordDiagramFromDataFrame() with defaults set to allow for more effective visualisation of directional origin-destination data

mig_chord(
  x,
  lab = NULL,
  lab_bend1 = NULL,
  lab_bend2 = NULL,
  label_size = 1,
  label_nudge = 0,
  label_squeeze = 0,
  axis_size = 0.8,
  axis_breaks = NULL,
  ...,
  no_labels = FALSE,
  no_axis = FALSE,
  clear_circos_par = TRUE,
  zero_margin = TRUE,
  start.degree = 90,
  gap.degree = 4,
  track.margin = c(-0.1, 0.1),
  points.overflow.warning = FALSE
)

Arguments

x

Data frame with origin in first column, destination in second column and bilateral measure in third column

lab

Named vector of labels for plot. If NULL will use names from d

lab_bend1

Named vector of bending labels for plot. Note line breaks do not work with facing = "bending" in circlize.

lab_bend2

Named vector of second row of bending labels for plot.

label_size

Font size of label text.

label_nudge

Numeric value to nudge labels towards (negative number) or away (positive number) the sector axis.

label_squeeze

Numeric value to nudge lab_bend1 and lab_bend2 labels apart (negative number) or together (positive number).

axis_size

Font size on axis labels.

axis_breaks

Numeric value for how often to add axis label breaks. Default not activated, uses default from circlize::circos.axis()

...

Arguments for circlize::chordDiagramFromDataFrame().

no_labels

Logical to indicate if to include plot labels. Set to FALSE by default.

no_axis

Logical to indicate if to include plot axis. Set to FALSE by default.

clear_circos_par

Logical to run circlize::circos.clear(). Set to TRUE by default. Set to FALSE if you wish to add further to the plot.

zero_margin

Set margins of the plotting graphics device to zero. Set to TRUE by default.

start.degree

Argument for circlize::circos.par().

gap.degree

Argument for circlize::chordDiagramFromDataFrame().

track.margin

Argument for circlize::chordDiagramFromDataFrame().

points.overflow.warning

Argument for circlize::chordDiagramFromDataFrame().

Value

Chord diagram based on first three columns of x. The function tweaks the defaults of circlize::chordDiagramFromDataFrame() for easier plotting of directional origin-destination data. Users can override these defaults and pass additional tweaks using any of the circlize::chordDiagramFromDataFrame() arguments.

The layout of the plots are designed to specifically work on plotting images into PDF devices with widths and heights of 7 inches (the default dimension when using the pdf function). See the end of the examples for converting PDF to PNG images in R.

Fitting the sector labels on the page is usually the most time consuming task. Use the different label options, including line breaks, label_nudge, track height in preAllocateTracks and font sizes in label_size and axis_size to find the best fit. If none of the label options produce desirable results, plot your own using circlize::circos.text having set no_labels = TRUE and clear_circos_par = FALSE.

Examples

# \dontrun{
library(tidyverse)
library(countrycode)
# download Abel and Cohen (2019) estimates
f <- read_csv("https://ndownloader.figshare.com/files/38016762", show_col_types = FALSE)
f
#> # A tibble: 307,833 × 9
#>    year0 orig  dest  sd_drop_neg sd_rev_neg mig_rate da_min_open da_mi…¹ da_pb…²
#>    <dbl> <chr> <chr>       <dbl>      <dbl>    <dbl>       <dbl>   <dbl>   <dbl>
#>  1  1990 BDI   BDI             0          0      0          0         0     0   
#>  2  1990 COM   BDI             0          0      0          0         0     0   
#>  3  1990 DJI   BDI             0          0      0          0         0     0   
#>  4  1990 ERI   BDI             0          0      0          0         0    38.3 
#>  5  1990 ETH   BDI             0          0      0          0         0     0.82
#>  6  1990 KEN   BDI            30         30     75.7       51.3     207.  248.  
#>  7  1990 MDG   BDI             0          0      0          0.03      0     0   
#>  8  1990 MWI   BDI             0          0      0          0         0    98.8 
#>  9  1990 MUS   BDI             0          0      0          0.06      0     0   
#> 10  1990 MYT   BDI             0          0      0          0         0     0   
#> # … with 307,823 more rows, and abbreviated variable names ¹​da_min_closed,
#> #   ²​da_pb_closed

# use dictionary to get region to region flows
d <- f %>%
  mutate(
    orig = countrycode(sourcevar = orig, custom_dict = dict_ims,
                       origin = "iso3c", destination = "region"),
    dest = countrycode(sourcevar = dest, custom_dict = dict_ims,
                       origin = "iso3c", destination = "region")
  ) %>%
  group_by(year0, orig, dest) %>%
  summarise_all(sum) %>%
  ungroup()
d
#> # A tibble: 216 × 9
#>    year0 orig   dest             sd_dr…¹ sd_re…² mig_r…³ da_mi…⁴ da_mi…⁵ da_pb…⁶
#>    <dbl> <chr>  <chr>              <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#>  1  1990 Africa Africa           4297155 7845806  5.47e6  6.87e6  7.73e6  1.00e7
#>  2  1990 Africa Asia              240464  258816  7.24e5  2.84e5  5.54e5  8.15e5
#>  3  1990 Africa Europe            555826  664496  1.91e6  8.30e5  2.19e6  2.74e6
#>  4  1990 Africa Latin America a…    1505    2709  7.81e3  9.04e3  5.67e4  5.46e4
#>  5  1990 Africa Northern America  289058  301706  2.23e5  3.22e5  7.83e5  8.28e5
#>  6  1990 Africa Oceania            21550   23570  6.59e4  3.02e4  1.66e5  1.77e5
#>  7  1990 Asia   Africa             94088  158903  2.00e5  1.02e5  9.36e4  3.53e5
#>  8  1990 Asia   Asia             3616112 8617460  1.44e7  6.97e6  1.03e7  1.71e7
#>  9  1990 Asia   Europe           1496141 2322839  5.48e6  2.85e6  4.21e6  6.42e6
#> 10  1990 Asia   Latin America a…   14316   14343  1.07e5  2.02e4  1.36e5  1.75e5
#> # … with 206 more rows, and abbreviated variable names ¹​sd_drop_neg,
#> #   ²​sd_rev_neg, ³​mig_rate, ⁴​da_min_open, ⁵​da_min_closed, ⁶​da_pb_closed

# 2015-2020 pseudo-Bayesian estimates for plotting
pb <- d %>%
    filter(year0 == 2015) %>%
    mutate(flow = da_pb_closed/1e6) %>%
    select(orig, dest, flow)
pb
#> # A tibble: 36 × 3
#>    orig   dest                               flow
#>    <chr>  <chr>                             <dbl>
#>  1 Africa Africa                           8.69  
#>  2 Africa Asia                             0.896 
#>  3 Africa Europe                           3.31  
#>  4 Africa Latin America and the Caribbean  0.0361
#>  5 Africa Northern America                 1.59  
#>  6 Africa Oceania                          0.264 
#>  7 Asia   Africa                           0.907 
#>  8 Asia   Asia                            23.8   
#>  9 Asia   Europe                           9.14  
#> 10 Asia   Latin America and the Caribbean  0.233 
#> # … with 26 more rows
    
# pdf(file = "chord.pdf")
mig_chord(x = pb)

# dev.off()
# file.show("chord.pdf")

# pass arguments to circlize::chordDiagramFromDataFrame
# pdf(file = "chord.pdf")
mig_chord(x = pb, 
          # order of regions
          order = rev(unique(pb$orig)),
          # spacing for labels
          preAllocateTracks = list(track.height = 0.3),
          # colours
          grid.col = c("blue", "royalblue", "navyblue", "skyblue", "cadetblue", "darkblue")
          ) 
#> Warning: Since you have set `order`, you should better set `grid.col` as a named
#> vector where sector names are the vector names, or else the color will
#> be wrongly assigned.

# dev.off()
# file.show("chord.pdf")

# multiple line labels to fit on longer labels
r <- pb %>%
  sum_region() %>%
  mutate(lab = str_wrap_n(string = region, n = 2)) %>%
  separate(col = lab, into = c("lab1", "lab2"), sep = "\n", remove = FALSE, fill = "right")
#> Asking for more lines than words
#> Asking for more lines than words
#> Asking for more lines than words
#> Asking for more lines than words
r
#> # A tibble: 6 × 8
#>   region                          out_mig in_mig  turn    net lab    lab1  lab2 
#>   <chr>                             <dbl>  <dbl> <dbl>  <dbl> <chr>  <chr> <chr>
#> 1 Africa                             6.10   2.52  8.62  -3.58 "Afri… Afri… NA   
#> 2 Asia                              18.7    6.93 25.6  -11.7  "Asia" Asia  NA   
#> 3 Europe                             8.11  15.7  23.8    7.59 "Euro… Euro… NA   
#> 4 Latin America and the Caribbean    5.90   4.39 10.3   -1.51 "Lati… Lati… and …
#> 5 Northern America                   6.96  14.8  21.7    7.83 "Nort… Nort… Amer…
#> 6 Oceania                            1.26   2.67  3.92   1.41 "Ocea… Ocea… NA   

# pdf(file = "chord.pdf")
mig_chord(x = pb,
          lab = r %>%
            select(region, lab) %>%
            deframe(),
          preAllocateTracks = list(track.height = 0.25),
          label_size = 0.8,
          axis_size = 0.7
          )

# dev.off()
# file.show("chord.pdf")

# bending labels
# pdf(file = "chord.pdf")
mig_chord(x = pb,
          lab_bend1 = r %>%
            select(region, lab1) %>%
            deframe(),
          lab_bend2 = r %>%
            select(region, lab2) %>%
            deframe()
          )

# dev.off()
# file.show("chord.pdf")


# convert pdf to image file
# library(magick)
# p <- image_read_pdf("chord.pdf")
# image_write(image = p, path = "chord.png")
# file.show("chord.png")
# }