Plotting typological data with R

I made a tutorial on how to plot typological data (here from grambank) with R.

Edit: I redid the tutorial to reflect a more sustainable workflow, using the data as published on zenodo rather than the download link on grambank. I also added part 2 on how to combine two data sets.

Thanks go to Robert Forkel and Hedvig Skirgård, who were very helpful in pointing me to the right places and resources. Shout-out to Simon Greenhill for creating the rcldf package.

Here is the code.

# For any package on cran that you have not installed yet, 
# RStudio will probably ask you whether you want to install.
# Otherwise, install the package with the command install.packages("PACKAGE"), 
# do not forget the quotation marks. Example:
# install.packages("sf")
library(ggplot2)
library(dplyr)
library(sf)
library(rnaturalearth)
# the rnaturalearthhires package is not on cran,
# because it is too big. Uncomment to download package
 install.packages(
  "rnaturalearthhires",
  repos = "https://ropensci.r-universe.dev",
  type = "source"
)
library(rnaturalearthhires)

# The rcldf package is also not on cran, so you have to install it with devtools.
# Uncomment the following lines to install package.
# library(devtools)
# devtools::install_github("SimonGreenhill/rcldf", dependencies = TRUE)

library(rcldf)

# Check out the documentation here for exploring the rcldf package:
# https://github.com/grambank/grambank/wiki/Fetching-and-analysing-Grambank-data-with-R
# The following section is mostly taken from there.

grambank <-  rcldf::cldf(mdpath = "https://zenodo.org/records/7740140/files/grambank/grambank-v1.0.zip")
# what tables are there?
summary(grambank)

# get languages:
head(grambank$tables$LanguageTable)
# get values:
head(grambank$tables$ValueTable)

# the package also has a command to join various tables into one big table, 
# so we can look up parameter values and coordinates in one data frame.
gb.wide <- as.cldf.wide(grambank, 'ValueTable')
head(gb.wide)

# Now we proceed with making our table for numeral classifiers in Cameroon
# (or the parameter and region of your choice)

# First, we create or map data for Cameroon:
cameroon <- ne_states(country = "Cameroon", returnclass = "sf")

# Now, we want to filter the grambank data. 
# We only want language coordinates within Cameroon.

# We first need to figure out the map meta data for our country map.
st_crs(cameroon)
# The crucial part of the output here is this:
# Coordinate Reference System:
# User input: WGS 84 
# See here for further reference:
# https://earthdatascience.org/courses/earth-analytics/spatial-data-r/understand-epsg-wkt-and-other-crs-definition-file-types/

# We can save this parameter like so:
projcrs <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
gbsf <- st_as_sf(x = gb.wide,                         
                      coords = c("Longitude", "Latitude"),
                      crs = projcrs, na.fail = FALSE)
# now we can filter our grambank table for those
# languages that are spoken in Cameroon
camergb <-  st_filter(gbsf, cameroon)
head(camergb)

# Next, we filter for the parameter we want to look at.
# In our example, that's numeral classifiers.
# We can look up its code at  https://grambank.clld.org/parameters
# The parameter for numeral classifiers is GB057.
camergb.clf <- filter(camergb, Parameter_ID == "GB057")

camergb.clf
# Here, we plot the Grambank data to a Map
ggplot() +
  geom_sf(data=cameroon, fill="#eeeeee")+
  geom_sf(data=camergb.clf, aes(fill=as.factor(Value)), shape=21)+
  scale_fill_manual(values=c("#ffaa99"), labels=c("absent", "unkown")) +
  labs(fill="Classifiers")+
  theme_void()


############################################
#     Part 2: Combine with second dataset  #
############################################

#Classifiers from Her et al.
heretal <- read.csv("https://raw.githubusercontent.com/cldf-datasets/wacl/refs/heads/main/raw/WACL_v1.csv")
head(heretal)
# Replace values to make them compatible with grambank values
heretal$CLF[heretal$CLF == FALSE] <- 0
heretal$CLF[heretal$CLF == TRUE] <- 1
heretal

# Transform data frame to shape file object
heretalSF <- st_as_sf(x = heretal,                         
                      coords = c("longitude", "latitude"),
                      crs = projcrs, na.fail = FALSE)

# Filter for Cameroonian languages
camerher <- st_filter(heretalSF, cameroon)
head(camerher)

# Combine dataframes camerher and camergb.clf into camerclf
# First, replace column name "Value" by "CLF" in the grambank data
# to conform with the other study
camergb.clf <- camergb.clf %>% rename_at('Value', ~'CLF')
# replace value "?" by "3" in grambank data.
# this will create a nicer order of categories later
camergb.clf <- camergb.clf |> mutate(CLF = ifelse(is.na(CLF), 3, CLF))
# currently, the grambank value is stored as a character string
# <chr>, because it originally contained "?"
# We want to store it as a number instead, for nicer automatic ordering
camergb.clf$CLF <- sapply(camergb.clf$CLF, as.double)
# Check if everything is fine.
View(camergb.clf)
# Add the camerher rows under the grambank rows. 
# create an extra column "id" to index the data set
# id= 1 --> grambank, id = 2 --> Her et al.
camerclf <- bind_rows(list(camergb.clf, camerher), .id = "id")
camerclf

# Combine everything into one plot
ggplot() +
  geom_sf(data=cameroon, fill="#eeeeee")+
  geom_sf(data=camerclf, aes(shape=as.factor(id), fill=as.factor(CLF),  size=as.factor(id)))+
  scale_shape_manual(values=c(23,21), labels=c("Grambank", "Her et al. (2022)"))+
  scale_fill_manual(values=c("#ffaa99", "black", "white"), labels=c("absent", "present", "unkown")) +
  guides(fill=guide_legend(override.aes = list(shape=21)))+
  scale_size_manual(values=c(3,1.5), labels=c("Grambank", "Her et al. (2022)"))+
  labs(fill="Classifiers", shape="Dataset", size="Dataset")+
  theme_void()

Afaka font

I regularly go a little overboard when designing puzzles for the German Olympiad of Linguistics, but for one of this year’s puzzles, I really outnerded myself. I designed a True Type Font for the writing system Afaka, which was developed for the creole language Ndyuka. It was conceived in 1910 by Afáka Atumisi and is named after its inventor. It’s a syllabary, partially based on a rebus system.

Afaka letter "fo"

For example, the symbol representing the syllable /fo/ shows four vertical lines. And there is an Afaka word pronounced “fo”, which means “four” (yes, it’s cognate with the English word).

There is a preliminary Unicode sheet with codes, but the writing system hasn’t been fully developed and codified so far. Accordingly, my font is also only a preliminary solution to writing Ndyuka in Afaka script. But it’s great for playing around, and designing puzzles! You can download the font here.

Custom typological maps with R

I used to plot my typological language data to geo-spatial maps with Generic Mapping Tools, which is awesome, and where a simple two-liner will do the job. But I found this hard to use in teaching, since it doesn’t run smoothly on everyone’s operating system. So it’s time for me to move on and learn to do maps with R. There are a few awesome resources out there, including lingtypology, which reads data directly off glottolog.

But I wanted to plot data that is not included in a database, and since it’s actually easy, but not entirely trivial to find on the internet so far, here’s a short tutorial. First of all, here is our little data set, with geo-spatial coordinates for each language, language family and basic word order info. Save this to a text file in your working directory with the name “typology.txt”.

Language,Latitude,Longitude,Family,Family2,Word order
Movima,-13.81,-65.63,Isolate,9,0
Arapaho,43.39,-108.81,Algic,10,OV
Alta,15.69,121.45,Austronesian,12,OV
Savosavo,-9.13,159.81,Isolate,9,OV
Teop,-5.67,154.97,Austronesian,11,VO
Sumi,26,94.42,Sino-Tibetan,12,VO
Yali,-4.08,139.46,Nuclear-Trans-New-Guinea,13,OV
Beja,17.24,36.67,Afro-Asiatic,14,VO
Vera'a,-13.89,167.43,Austronesian,11,OV
Cabecar,9.67,-83.41,Chibchan,15,VO
Urum,42.04,43.99,Turkic,16,OV
Dolgan,71.11,94.29,Turkic,16,OV
Gorwaa,-4.24,35.8,Afro-Asiatic,14,OV
Pnar,24.82,92.26,Austroasiatic,17,0
Goemai,8.74,9.72,Afro-Asiatic,14,VO
English,53,-1,Indo-European,7,VO
Nung,-29.71,19.08,Tuu,18,VO
Bora,-2,-72.26,Boran,19,VSO
Nafsan,-17.7,168.38,Austronesian,11,OV
Komnzo,-8.65,141.52,Yam,20,OV
Kamas,55.07,94.83,Uralic,21,VO
Kakabe,10.6,-11.44,Mande,22,OV
Daakaka,-16.27,168.01,Austronesian,11,VO

Next in your R script, load the following packages. You might have to install them first:

library(ggplot2)
library(maps)
library(sf) #for advanced mapping options
library(viridisLite) #for pretty, color-blind friendly color palettes

Read in your map data and your language coordinates and save them to short variables like “map” and “df” (for data frame).

df <- read.csv("typology.txt")
head(df) #shows you the beginning of the data, good for trouble shooting
map <- map_data("world")

In order to plot your language coordinates with additional information about language families, do this:

#family
ggplot() +
  geom_polygon(data = map, aes(x = long, y = lat, group=group), fill="#dddddd") + #plots the world map in the background in light grey
  geom_point(data = df, aes(x = Longitude, y = Latitude, fill = factor(Family)), shape=21, size=3) + #plots the language coordinates
  theme_minimal()+ #fewer embellishments
  coord_sf()+ #nice proportions
  scale_fill_viridis_d(option = "plasma")+ #color scheme
  labs(fill = "Family", y="",x="") #labels
Plot of languages as they are geographically distributed across the world, with color indicating language family

To get information about word order patterns, instead use this:

#word order
ggplot() +
  geom_polygon(data = map, aes(x = long, y = lat, group = group), fill="#dddddd") +
  geom_point(data = df, aes(x = Longitude, y = Latitude, fill = factor(Word.order)), shape=21,  size=3) +
  theme_minimal()+
  coord_sf()+
  scale_fill_viridis_d(option = "plasma")+
  labs(fill = "Word Order", y="",x="")

And since newbies sometimes use my tutorials: If you don’t understand something here, don’t give up, google it! Everyone does it, and most questions you might have, have already been asked and answered by someone somewhere.

How to revise your article after reviewing

Opening the mail…

Once you have submitted your article to a research journal, it can take a few weeks to a few months before you hear back. During that time, your editors are busy finding competent and willing reviewers, and hopefully, those reviewers are busy reading your work with discretion and charity and thinking about the best ways to help you improve your manuscript. If you do not hear back from the editors after 3 months, feel free to send them a friendly reminder that you are still waiting for reviews.

Continue reading “How to revise your article after reviewing”

Getting your manuscript ready for its first submission

You have interesting and original research to publish, but you have not done this before, how do you proceed? Here is how I do it.

Continue reading “Getting your manuscript ready for its first submission”

GMT: Changing the color of a symbol within a xy file

This is a very specific problem with Generic Mapping Tools which I didn’t find well documented: if you map certain symbols to a list of coordinates specified in COORDINATES.xy, you can specify that subsets of those coordinates are mapped to symbols in different colors in two ways:

Continue reading “GMT: Changing the color of a symbol within a xy file”

Bibtex bibliographies selected by keywords, with customised keyword separators

This is a very specialised problem, but since I just found the solution, I wanted to briefly document this for myself. I usually use natbib, but for the preparation of reading lists, sorted by topic, I wanted to try biblatex. Creating a list of references selected by a keyword is not a problem at all.

Continue reading “Bibtex bibliographies selected by keywords, with customised keyword separators”

Compiling a list of glosses from your glossed examples in a LaTeX document (under UNIX)

If you have many interlinearized examples in your LaTeX documents, you have probably wondered about the best way to handle them. Here are some ideas. There are two potential problems with the glosses: 1) different publishers may have different requirements for how to print them, so transferring glossed examples from one manuscript to another may be difficult. 2) You’ll want to have a list of all the glosses in your document, and it should be complete and consistent. To solve all that, the main strategy is to label all your glosses explicitly as such by using a new command we may call “Gloss”:

Continue reading “Compiling a list of glosses from your glossed examples in a LaTeX document (under UNIX)”