This file contains all the source code to generate the figures and statistical measures reported in the paper Evolution and Spread of Politeness Systems in Indo-European

Required data files

Generated files:

To regenerate the analysis cleanly you need to delete the three .rData files, otherwise saved values will be used. The tree.sample.size variable is set at 400, which is an acceptable value for the full test, but this is slow and it can be set to a lower value for testing.

#library(logisticPCA)
library(ape)
library(vegan)
## Loading required package: permute
## Loading required package: lattice
## This is vegan 2.5-7
library(parallel)
library(phytools)
## Loading required package: maps
## 
## Attaching package: 'phytools'
## The following object is masked from 'package:vegan':
## 
##     scores
library(rnaturalearth)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✔ ggplot2 3.4.0      ✔ purrr   0.3.4 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.1.4      ✔ stringr 1.4.0 
## ✔ readr   1.4.0      ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ purrr::map()    masks maps::map()
library(ggrepel)
library(gridExtra) # for grid.arrange
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
options(ggrepel.max.overlaps = Inf)

MC.CORES <- detectCores() # Use all available processing power
cat(paste("--> Using", MC.CORES, "cores\n"))
## --> Using 12 cores

As a sanity check, transform the pronoun paradigm similarities into an inter-language distance measure. A cluster plot of the distance matrix showing structurally similar languages clustered together. Note that this is not a phylogeny!

# The similarity features are comparisions of every pair of cells in a pronoun paradigm, 
# calculated per language
pairwise.similarity <- read.delim("pronoun-similarity.csv", row.names=1, header=TRUE)
language.distance <- dist(pairwise.similarity,  method="manhattan") / ncol(pairwise.similarity)
plot(hclust(language.distance), cex=0.5)

Prepare metadata

metadata <- read_tsv("language-metadata.csv")
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   language = col_character(),
##   iso.code = col_character(),
##   latitude = col_double(),
##   longitude = col_double(),
##   ancient = col_logical()
## )
sae.features <- read.delim("SAE-features.csv", row.names=1)
sae.sum <- apply(sae.features, 1, sum)[metadata$language]
metadata$sae.score <- sae.sum
metadata$SAE <- sae.sum > 4
metadata
#View(metadata)

Summarise data with PCA

# temporary fix for missing values
sae.features[is.na(sae.features)] <- 0

pca_model <- prcomp(language.distance, scale=TRUE)$x %>% 
  as.data.frame() %>%
  mutate(language=rownames(.)) %>%
  left_join(metadata) %>%
  filter(!is.na(iso.code)) # rm non-IE languages
## Joining, by = "language"
pca_model %>%
 ggplot(aes(x=PC1, y=PC2, label=language, colour=SAE)) + 
  geom_point() + 
  scale_colour_manual(values=c("darkgray", "black")) +
  geom_text_repel(size=3, segment.size=0.25, segment.alpha=0.5, box.padding=0.1) +
  theme(legend.position = "none")

ggsave("../figures/Figure_2_PCA.png", width=5.75, height=5.75, units="in")
ggsave("../figures/Figure_2_PCA.pdf", width=5.75, height=5.75, units="in")

Supplementary figure 1 shows that PC1 can be characterised as distinguishing languages which have person-number conflation, operationalised as the answer to the question does the language ever conflate singular and plural for 2nd or 3rd person?; this is calculated from the pronoun paradigm data in pronoun-paradigms.csv using the script make_pronoun_conflation_table.py. Languages with person-number conflation tend to be loaded negatively on PC1.

pca_model %>%
  left_join(read_tsv("pronoun-number-conflation.csv")) %>% 
  ggplot(aes(x=PC1, y=PC2, label=language, colour=as.factor(conflation))) + 
  geom_point() + 
  geom_text_repel(size=3, segment.size=0.25, segment.alpha=0.5, box.padding=0.1) +
  labs(colour="Conflated\nnumber")
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   language = col_character(),
##   conflation = col_double()
## )
## Joining, by = "language"

ggsave("../figures/Supplementary_Figure_1.pdf", width=5.75, height=5.75, units="in")

Supplementary figure 2 shows that PC2 distinguishes languages which have distinct politeness forms in the singular and plural (languages with distinct politeness forms in the singular and plural are weighted positively); note that while this is a distinctive feature of IE pronominal paradigms, it does not seem to be diagnostic of the SAE linguistic area.

pca_model %>% 
  left_join(read_tsv("pronoun-distinct-politeness.csv")) %>% 
  ggplot(aes(x=PC1, y=PC2, label=language, colour=as.factor(distinct))) + 
  geom_point() + #geom_point(aes(shape=SAE)) + 
  geom_text_repel(size=3, segment.size=0.25, segment.alpha=0.5, box.padding=0.1) +
  labs(colour="Distinct\npoliteness")
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   language = col_character(),
##   distinct = col_double()
## )
## Joining, by = "language"

ggsave("../figures/Supplementary_Figure_2.pdf", width=5.75, height=5.75, units="in")

Data transformation

Prepare the tree sample if the file trees.rData doesn’t already exist (this is a slow process, so we don’t want to repeat it if not necessary). To force regeneration of trees.rData delete the current version of the file along with other .rData files.

Sanity check: Load a reference tree, fix the tree labels, and plot it

all.languages <- metadata %>% pull(language)
namehash <- c( # convert tree names to names used in analysis or to proxy languages
  # proxies are languages which are in the equivalent position in the tree
  "Arvanitika"="Albanian_Gheg",
  "Tosk"="Albanian_Tosk",
  "Eastern_Armenian"="Armenian",
  "Belarusian"="Byelorussian",
  "Irish"="Irish_A",
  "Digor_Ossetic"="Iron_Ossetic",
  "Luxembourgish"="Luxemburgish",
  "Bihari"="Maithili",
  "Old_Church_Slavic"="Old_Church_Slavonic",
  "Old_West_Norse"="Old_Norse",
  "Cagliari"="Sardinian_Campidanese",
  "Nuorese"="Sardinian_Logudorese",
  "Serbian"="Serbocroatian",
  "Singhalese"="Sinhalese",
  "Welsh"="Welsh_N")

mcc.tre <- read.nexus("ie-v1.mcc.tre")
mcc.tre$tip.label <- recode(mcc.tre$tip.label, !!!namehash)
mcc.tre <- ladderize(root(keep.tip(mcc.tre, all.languages), "Hittite"))
plot(mcc.tre, no.margin=TRUE, cex=0.6)

states.pca <- data.frame(
  PC1=round(pca_model$PC1,5), 
  PC2=round(pca_model$PC2,5)) #,
  #feat.sum=pca_model$sae.score)
row.names(states.pca) <- pca_model$language

tree.sample.size <- 400 # set this to e.g. 20 for testing, 400+ for a full analysis

if (!file.exists("trees.rData")){
  TREEFILE <- "ie-v1.nex" # this sample is 10000 trees, which is much too long
  
  trees <- read.nexus(TREEFILE)
  # drop the first half of the trees as burnin, and filter the remaining sample to a reasonable size
  trees <- trees[seq(length(trees)/2, length(trees), length.out=tree.sample.size)]

  tree.list <- mclapply(trees, 
    function(tree) {
      # fix labels
      tree$tip.label <- recode(tree$tip.label, !!!namehash)
      # scale edge lengths (there are sometimes errors when numbers are too large)
      tree$edge.length <- tree$edge.length / 1000 
      # root on Hittite
      ladderize(root(keep.tip(tree, all.languages), "Hittite"))
      }, 
    mc.cores=MC.CORES)

  class(tree.list) <- "multiPhylo"
  
  save(tree.list, states.pca, mcc.tre, file="trees.rData")
}

Simulate SAE history

Plot these states on a map.

world = ne_countries(scale = "small", returnclass = "sf")

ggplot(data=world) + 
  geom_sf(fill="white") +
  coord_sf(xlim = range(metadata$longitude), ylim = range(metadata$latitude)) +
  geom_point(data=metadata, aes(x = longitude, y = latitude, shape=ancient, fill=as.factor(sae.score)), size=3, colour="black") +
  scale_shape_manual(values=c(21,23))+
  scale_fill_grey(start=1, end=0.2) +
  theme(legend.position = "none", axis.title.x=element_blank(), axis.title.y=element_blank())

ggsave("../figures/Figure_1_Map.pdf", width=5.75, height=3.75, units="in")
ggsave("../figures/Figure_1_Map.png", width=5.75, height=3.75, units="in")

Plot the distribution of SAE feature counts

metadata %>% 
  ggplot(aes(x=sae.score)) + 
  geom_histogram(binwidth=1) + 
  scale_x_continuous(breaks=0:9) +
  xlab("Number of SAE Features") + ylab("Number of languages")

There is a natural breakpoint at sae.score >= 5, which we will thus use as a threshold for membership of the SAE area. This is the same breakpoint suggested by Haspelmath 2011:1505.

ggsave("../figures/Figure_3_SAE.png", width=3, height=2)
ggsave("../figures/Figure_3_SAE.pdf", width=3, height=2)

Demonstrate a simmap

observed.states <- metadata %>% pull(SAE) %>% as.integer()
names(observed.states) <- metadata %>% pull(language)
# make.simmap rate models: "ARD" all rates different; "SYM" symmetrical; "EQ"" equal; or use matrix
dollo.model = matrix(c(0,0,1,0), 2) # once a lineage becomes SAE it doesn't revert to non-SAE
simmap.tre <- make.simmap(mcc.tre,
  observed.states,
  model=dollo.model)
## make.simmap is sampling character histories conditioned on
## the transition matrix
## 
## Q =
##            0         1
## 0 -0.0001069 0.0001069
## 1  0.0000000 0.0000000
## (estimated using likelihood);
## and (mean) root node prior probabilities
## pi =
##   0   1 
## 0.5 0.5
## Done.
cols <- c("black", "gray")
names(cols) <- c("0", "1")
plotSimmap(simmap.tre, cols, fsize=0.5, pts=FALSE, lwd=4)

Create all the simmaps. This can be an extremely slow process, so the results are saved.

Plot three arbitrary simmaps to show variation in tree structure (from the Bayesian phylogenetic sampling process) and simulated history

opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3))
plot.simmap.i <- function(i){
  cols <- c("black", "gray")
  names(cols) <- c("0", "1")
  plot(simmap.list[[i]], cols, fsize=0.5, pts=FALSE, lwd=4)}

plot.simmap.i(1)
plot.simmap.i(2)
plot.simmap.i(3)

par(opar)
pdf("../figures/Figure_4_State_Simulation.pdf", width=7, height=4.5)
par(mfrow=c(1,3))
plot.simmap.i(1)
plot.simmap.i(2)
plot.simmap.i(3)
dev.off()
## quartz_off_screen 
##                 2

Now do the brownie.lite test (O’Meara et al. 2006).

NTREES <- length(simmap.list) 
results.trait.m <- matrix( # template results matrix
  nrow=NTREES,
  ncol=12, 
  dimnames=list(1:NTREES,
    c("BM.singlerate","logL.singlerate", "k1", "1rate.AICc", "NonSAE.rate",
      "SAE.rate","logL.multiplerate", "k2", "2rate.AICc", "pchisq",
     "converged", "delta.AICc"))
  )

if (!file.exists("results.rData")){
  process.results <- function(trait.name){
    results.trait.name <- results.trait.m
    traits <- states.pca[[trait.name]]
    names(traits) <- row.names(states.pca)
    for (i in 1:NTREES){
      cat(paste("Trait ", trait.name, " trial ", i, "/", NTREES, "\n", sep=""))
      # check that labels are aligned
      stopifnot(all(sort(simmap.list[[i]]$tip.label) == sort(names(traits))))
      fit <- NA
      tryCatch(
        fit <- brownie.lite(simmap.list[[i]], traits, test="chisq"),
          error=function(e) {}
        )
        if (length(fit) != 1){
          # calculate AICc for 1 and 2 rate models
          fit1aicc<-(-2*fit$logL1+2*fit$k1+((2*fit$k1*(fit$k1+1))/(90-fit$k1-1))) 
          fit2aicc<-(-2*fit$logL.multiple+2*fit$k2+((2*fit$k2*(fit$k2+1))/(90-fit$k2-1))) 

          results.trait.name[i,]<- as.numeric(c(
              fit$sig2.single, 
              fit$logL1, 
              fit$k1, 
              fit1aicc, 
              fit$sig2.multiple[sort(names(fit$sig2.multiple))][[1]],
              fit$sig2.multiple[sort(names(fit$sig2.multiple))][[2]],
              fit$logL.multiple,
              fit$k2,
              fit2aicc,
              fit$P.chisq,
              fit$convergence == "Optimization has converged.",
              fit1aicc-fit2aicc
              )) 
        }
    }
    results.trait.name
  }
  results <- mclapply(names(states.pca), process.results,
    mc.cores=min(MC.CORES, length(names(states.pca))))
  names(results) <- names(states.pca)
  save(results, file="results.rData")
} else load("results.rData")

Get rate estimates (report in article text)

trait_labels <- c("PC1", "PC2")
# trait_labels <- c("PC1", "PC2", "feat.sum")
lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait") %>% 
  pivot_longer(c("logL.singlerate", "logL.multiplerate"), names_to="model", values_to="rate") %>%
  group_by(trait, model) %>%
  summarise(median_rate=median(rate))
## `summarise()` has grouped output by 'trait'. You can override using the
## `.groups` argument.

delta AICc

Compare AIC for one rate and two rate models

trait_labels <- c("PC1", "PC2")
# trait_labels <- c("PC1", "PC2", "feat.sum")


lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait") %>% 
  pivot_longer(c("1rate.AICc", "2rate.AICc"), names_to="model", values_to="AICc") %>%
  ggplot(aes(x=trait, y=AICc, colour=model)) + geom_boxplot()

Change in AIC gives a measure of how much one model should be preferred over the other

fig5c <- lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait") %>% 
  ggplot(aes(x=trait, y=delta.AICc)) + 
  geom_boxplot() +
  labs(title="(c)") +
  theme(axis.title.x=element_blank())
fig5c

Report median change in AICc in text

lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait") %>%
  group_by(trait) %>%
  summarise(median.delta.AIC.c=median(delta.AICc), evidence.ratio=exp(0.5*median(delta.AICc)))

For the two rate model, how the estimated rates of evolution of each component in the SAE and non-SAE regions of the trees.

fig5a <- lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait" ) %>%
  pivot_longer(c("SAE.rate", "NonSAE.rate"), names_to="area", values_to="rate") %>%
  mutate(area=if_else(area=="SAE.rate", "SAE", "non-SAE")) %>%
  ggplot(aes(x=trait, y=rate, fill=area)) + 
  geom_boxplot() + 
  theme(legend.position = "bottom") + 
  scale_fill_grey(start=0.5, end=0.8)+
  labs(title="(a)") +
  theme(axis.title.x=element_blank())
fig5a

Summarise the relative rate of evolution of each component for SAE versus non-SAE parts of the tree

fig5b <- lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait" ) %>%
  mutate(rate.multiplier=SAE.rate/NonSAE.rate) %>%
  ggplot(aes(x=trait, y=rate.multiplier)) + geom_boxplot() +
  labs(title="(b)") +
  theme(axis.title.x=element_blank())
fig5b

lapply(trait_labels, function(d) as_tibble(results[[d]])) %>%
  set_names(trait_labels) %>%
  bind_rows(.id="trait" ) %>%
  mutate(rate.multiplier=SAE.rate/NonSAE.rate) %>%
  group_by(trait) %>%
  summarise(median.rate.multiplier=median(rate.multiplier))
fig5 <- grid.arrange(fig5a, fig5b, fig5c, nrow=1)

ggsave("../figures/Figure_5_results.png", plot=fig5, width=7, height=3)
ggsave("../figures/Figure_5_results.pdf", plot=fig5, width=7, height=3)

References

Dunn, Michael and Tresoldi, Tiago. 2021. Indo-European Lexical Data (IELex) and ‘Good Enough’ Tree. Uppsala: Uppsala universitet. Available at: https://github.com/evotext/ielex-data-and-tree ; DOI: 10.5281/zenodo.5556801

Haspelmath, Martin. 2001. “The European Linguistic Area: Standard Average European.” Language Typology and Language Universals: An International Handbook 2: 1492–1510.

O’Meara, Brian .C., Cécile Ané, Michael J. Sanderson, and Peter C. Wainwright. 2006. Testing for different rates of continuous trait evolution using likelihood. Evolution 60(5): 922-933.

Session info

sessioninfo::session_info()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 4.0.4 (2021-02-15)
##  os       macOS Big Sur 10.16         
##  system   x86_64, darwin17.0          
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  ctype    en_US.UTF-8                 
##  tz       Europe/Stockholm            
##  date     2023-02-08                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package           * version    date       lib source        
##  ape               * 5.5        2021-04-25 [1] CRAN (R 4.0.2)
##  assertthat          0.2.1      2019-03-21 [1] CRAN (R 4.0.2)
##  backports           1.2.1      2020-12-09 [1] CRAN (R 4.0.2)
##  broom               0.7.9      2021-07-27 [1] CRAN (R 4.0.2)
##  bslib               0.2.5.1    2021-05-18 [1] CRAN (R 4.0.2)
##  cellranger          1.1.0      2016-07-27 [1] CRAN (R 4.0.2)
##  class               7.3-18     2021-01-24 [1] CRAN (R 4.0.4)
##  classInt            0.4-3      2020-04-07 [1] CRAN (R 4.0.2)
##  cli                 3.4.1      2022-09-23 [1] CRAN (R 4.0.4)
##  cluster             2.1.0      2019-06-19 [1] CRAN (R 4.0.4)
##  clusterGeneration   1.3.7      2020-12-15 [1] CRAN (R 4.0.2)
##  coda                0.19-4     2020-09-30 [1] CRAN (R 4.0.2)
##  codetools           0.2-18     2020-11-04 [1] CRAN (R 4.0.4)
##  colorspace          2.0-3      2022-02-21 [1] CRAN (R 4.0.5)
##  combinat            0.0-8      2012-10-29 [1] CRAN (R 4.0.2)
##  crayon              1.4.1      2021-02-08 [1] CRAN (R 4.0.2)
##  DBI                 1.1.1      2021-01-15 [1] CRAN (R 4.0.2)
##  dbplyr              2.1.1      2021-04-06 [1] CRAN (R 4.0.2)
##  digest              0.6.28     2021-09-23 [1] CRAN (R 4.0.2)
##  dplyr             * 1.0.10     2022-09-01 [1] CRAN (R 4.0.4)
##  e1071               1.7-7      2021-05-23 [1] CRAN (R 4.0.2)
##  ellipsis            0.3.2      2021-04-29 [1] CRAN (R 4.0.2)
##  evaluate            0.14       2019-05-28 [1] CRAN (R 4.0.1)
##  expm                0.999-6    2021-01-13 [1] CRAN (R 4.0.2)
##  fansi               1.0.3      2022-03-24 [1] CRAN (R 4.0.5)
##  farver              2.1.1      2022-07-06 [1] CRAN (R 4.0.4)
##  fastmatch           1.1-3      2021-07-23 [1] CRAN (R 4.0.2)
##  forcats           * 0.5.1      2021-01-27 [1] CRAN (R 4.0.2)
##  fs                  1.5.0      2020-07-31 [1] CRAN (R 4.0.2)
##  generics            0.1.3      2022-07-05 [1] CRAN (R 4.0.4)
##  ggplot2           * 3.4.0      2022-11-04 [1] CRAN (R 4.0.4)
##  ggrepel           * 0.9.1      2021-01-15 [1] CRAN (R 4.0.2)
##  glue                1.6.2      2022-02-24 [1] CRAN (R 4.0.5)
##  gridExtra         * 2.3        2017-09-09 [1] CRAN (R 4.0.2)
##  gtable              0.3.1      2022-09-01 [1] CRAN (R 4.0.4)
##  haven               2.3.1      2020-06-01 [1] CRAN (R 4.0.2)
##  highr               0.8        2019-03-20 [1] CRAN (R 4.0.2)
##  hms                 1.0.0      2021-01-13 [1] CRAN (R 4.0.2)
##  htmltools           0.5.1.1    2021-01-22 [1] CRAN (R 4.0.2)
##  httr                1.4.2      2020-07-20 [1] CRAN (R 4.0.2)
##  igraph              1.2.6      2020-10-06 [1] CRAN (R 4.0.2)
##  jquerylib           0.1.4      2021-04-26 [1] CRAN (R 4.0.2)
##  jsonlite            1.7.2      2020-12-09 [1] CRAN (R 4.0.2)
##  KernSmooth          2.23-18    2020-10-29 [1] CRAN (R 4.0.4)
##  knitr               1.31       2021-01-27 [1] CRAN (R 4.0.2)
##  labeling            0.4.2      2020-10-20 [1] CRAN (R 4.0.2)
##  lattice           * 0.20-41    2020-04-02 [1] CRAN (R 4.0.4)
##  lifecycle           1.0.3      2022-10-07 [1] CRAN (R 4.0.4)
##  lubridate           1.8.0      2021-10-07 [1] CRAN (R 4.0.2)
##  magrittr            2.0.3      2022-03-30 [1] CRAN (R 4.0.5)
##  maps              * 3.4.0      2021-09-25 [1] CRAN (R 4.0.2)
##  MASS                7.3-53     2020-09-09 [1] CRAN (R 4.0.4)
##  Matrix              1.3-2      2021-01-06 [1] CRAN (R 4.0.4)
##  mgcv                1.8-33     2020-08-27 [1] CRAN (R 4.0.4)
##  mnormt              2.0.2      2020-09-01 [1] CRAN (R 4.0.2)
##  modelr              0.1.8      2020-05-19 [1] CRAN (R 4.0.2)
##  munsell             0.5.0      2018-06-12 [1] CRAN (R 4.0.2)
##  nlme                3.1-152    2021-02-04 [1] CRAN (R 4.0.4)
##  numDeriv            2016.8-1.1 2019-06-06 [1] CRAN (R 4.0.2)
##  permute           * 0.9-5      2019-03-12 [1] CRAN (R 4.0.2)
##  phangorn            2.7.1      2021-07-13 [1] CRAN (R 4.0.2)
##  phytools          * 0.7-90     2021-09-25 [1] CRAN (R 4.0.2)
##  pillar              1.8.1      2022-08-19 [1] CRAN (R 4.0.4)
##  pkgconfig           2.0.3      2019-09-22 [1] CRAN (R 4.0.2)
##  plotrix             3.8-2      2021-09-08 [1] CRAN (R 4.0.2)
##  proxy               0.4-26     2021-06-07 [1] CRAN (R 4.0.2)
##  purrr             * 0.3.4      2020-04-17 [1] CRAN (R 4.0.2)
##  quadprog            1.5-8      2019-11-20 [1] CRAN (R 4.0.2)
##  R6                  2.5.1      2021-08-19 [1] CRAN (R 4.0.2)
##  Rcpp                1.0.9      2022-07-08 [1] CRAN (R 4.0.4)
##  readr             * 1.4.0      2020-10-05 [1] CRAN (R 4.0.2)
##  readxl              1.3.1      2019-03-13 [1] CRAN (R 4.0.2)
##  reprex              2.0.1      2021-08-05 [1] CRAN (R 4.0.2)
##  rgeos               0.5-5      2020-09-07 [1] CRAN (R 4.0.2)
##  rlang               1.0.6      2022-09-24 [1] CRAN (R 4.0.4)
##  rmarkdown           2.17       2022-10-07 [1] CRAN (R 4.0.4)
##  rnaturalearth     * 0.1.0      2017-03-21 [1] CRAN (R 4.0.2)
##  rstudioapi          0.13       2020-11-12 [1] CRAN (R 4.0.2)
##  rvest               1.0.0      2021-03-09 [1] CRAN (R 4.0.2)
##  s2                  1.0.7      2021-09-28 [1] CRAN (R 4.0.2)
##  sass                0.4.0      2021-05-12 [1] CRAN (R 4.0.2)
##  scales              1.2.1      2022-08-20 [1] CRAN (R 4.0.4)
##  scatterplot3d       0.3-41     2018-03-14 [1] CRAN (R 4.0.2)
##  sessioninfo         1.1.1      2018-11-05 [1] CRAN (R 4.0.2)
##  sf                  1.0-3      2021-10-07 [1] CRAN (R 4.0.2)
##  sp                  1.4-5      2021-01-10 [1] CRAN (R 4.0.2)
##  stringi             1.7.5      2021-10-04 [1] CRAN (R 4.0.2)
##  stringr           * 1.4.0      2019-02-10 [1] CRAN (R 4.0.2)
##  tibble            * 3.1.8      2022-07-22 [1] CRAN (R 4.0.4)
##  tidyr             * 1.1.4      2021-09-27 [1] CRAN (R 4.0.2)
##  tidyselect          1.2.0      2022-10-10 [1] CRAN (R 4.0.4)
##  tidyverse         * 1.3.1      2021-04-15 [1] CRAN (R 4.0.2)
##  tmvnsim             1.0-2      2016-12-15 [1] CRAN (R 4.0.2)
##  units               0.7-1      2021-03-16 [1] CRAN (R 4.0.2)
##  utf8                1.2.2      2021-07-24 [1] CRAN (R 4.0.2)
##  vctrs               0.5.1      2022-11-16 [1] CRAN (R 4.0.4)
##  vegan             * 2.5-7      2020-11-28 [1] CRAN (R 4.0.2)
##  withr               2.5.0      2022-03-03 [1] CRAN (R 4.0.5)
##  wk                  0.5.0      2021-07-13 [1] CRAN (R 4.0.2)
##  xfun                0.34       2022-10-18 [1] CRAN (R 4.0.4)
##  xml2                1.3.2      2020-04-23 [1] CRAN (R 4.0.2)
##  yaml                2.2.1      2020-02-01 [1] CRAN (R 4.0.2)
## 
## [1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library