Determining language areas in linguistic typology

2026-07-28

In a previous post I showed a simple way to control for language family and area with categorical variables in a linear mixed model using Python. In a more recent post I showed how we can plot languages on a map based on various features such as word order and language family. The use of “macroareas” as a control for potential language contact is not ideal, as it treats all languages within a given area as having the same relationship. However, “macroareas” are a better option than no areal control at all, and while I will consider other options in a future post, this post focuses on how such areas are derived.

The Glottolog database has identified macroareas for languages of the world. These six areas include Eurasia, Africa, Papunesia, North America, South America, and Australia, and are justified by Hammarström & Donohue (2014) as meeting necessary criteria for geographical independence. Another way of deriving areas is by clustering - using an algorithm such as K-means to determine the number of groups that best accounts for variance in a set of GPS coordinates. The remainder of this post illustrates and compares the two approaches using maps, with some discussion about the benefits of each.

Getting the data

As with previous tutorials, I’m assuming that you’ve set up a Python 3.10 environment and have some familiarity with writing and running Python scripts (i.e. via a terminal). For a basic setup tutorial, you can follow these instructions.

For our purposes, we first want to observe how the six macroareas look when we map them based on GPS coordinates. Glottolog has information for languages grouped into macroareas along with latitude and longitude. The following python code downloads the data from Glottolog and stores it in a file in your local directory, then loads it using pandas so that we can visualize the data structure in the terminal.

import os, requests # for file handling
import pandas as pd # for data processing

glottofile = "Glottolog_Languages.csv" # comma-separated file with glottocodes and other info
# check whether the file exists already
if not os.path.isfile(glottofile):
    # use the raw bitstream URL for this file
    url = "https://cdstar.eva.mpg.de//bitstreams/EAEA0-608B-9919-A962-0/languages_and_dialects_geo.csv"
    # fetch the file content
    response = requests.get(url)
    # write content to the local file
    if response.status_code == 200:
        with open(glottofile, "wb") as file:
            file.write(response.content)
        print("Download complete!")
    else:
        print(f"Failed to download. Status code: {response.status_code}")
else:
    print("File exists!")

gldf = pd.read_csv(glottofile) # read in the dataset
print(gldf.head()) # check its structure
print(f'There are {len(gldf)} languoids in the dataset')
print(gldf.macroarea.value_counts()) # print the macroareas and how many languoids in each area

# File exists!
#
#   glottocode        name isocodes     level  macroarea  latitude  longitude
# 0   3adt1234  3Ad-Tekles      NaN   dialect     Africa       NaN        NaN
# 1   aala1237      Aalawa      NaN   dialect  Papunesia       NaN        NaN
# 2   aant1238  Aantantara      NaN   dialect  Papunesia       NaN        NaN
# 3   aari1239        Aari      aiw  language     Africa   5.95034    36.5721
# 4   aari1240      Aariya      aay  language    Eurasia       NaN        NaN
#
# There are 22324 languoids in the dataset
#
# macroarea
# Eurasia          7116
# Africa           6581
# Papunesia        5149
# North America    1453
# South America    1229
# Australia         704
#


Plotting languages by Glottolog macroarea

Here we can see that there are over 22k data points in the dataset, but not all of them correspond to individual languages (some are classed as dialects, for example), and not all of them have GPS coordinates (latitude and longitude). We can also see that some macroareas are much better represented than others. In the following code we can reduce the dataset to only those language varieties with GPS coordinates.

gldf = gldf.dropna(subset=['latitude']) # drop data that doesn't contain latitude info
print(f'There are {len(gldf)} languoids in the dataset with latitude info')
print(gldf.macroarea.value_counts()) # print the macroareas and how many languoids in each area

# There are 8889 languoids in the dataset with latitude info
#
# macroarea
# Africa           2418
# Papunesia        2281
# Eurasia          2251
# North America     795
# South America     703
# Australia         441
#

This leaves us with nearly 9k data points, and the following code plots them on an interactive map. In this blog post I reproduce only the image, but running this code locally will display an html page that allows you to click and zoom.

import plotly.express as px # the plotting package
import colorcet as cc # the color package

# create the scatter map
fig = px.scatter_geo(
    gldf,                             # the dataframe containing our data
    lat="latitude",                   # name of the column with latitude values
    lon="longitude",                  # name of the column with longitude values
    color="macroarea",                # macroarea column dictates color
    hover_name="name",                # name of the language for visualization
    projection="natural earth",       # map projection used by plotly
    title="Languages by Macroarea",   # title of the map
    color_discrete_sequence=cc.glasbey_dark, # set of distinctive colors (from 256)
)

# display the map in a browser
fig.show()
Languages by Glottolog Macroarea

From the map, we can see that these macroareas line up very well with the major landmasses, with only a few possible exceptions (like Chinese Pidgin English which is classed as belonging to the “Eurasian” macroarea despite sharing a location with Nauru in the Pacific).

As noted in the 2014 paper, this set of macroareas is intended to provide a foundation, based on our knowledge of possible interactions between humans. The essential observation is that areas which share landmass are more likely to see contact between groups and therefore between speakers of different languages. The exception to such a rule is when technology (such as sailing vessels for the Austronesians) enables contact across vast distances (i.e. oceans) or difficult-to-cross barriers (i.e. mountains).

Deriving and plotting macroareas by geographic coordinates

Another way to derive areas is by clustering data according to distance between coordinates. There are two general ways to do this, both of which require transformation of the coordinates to feed to a clustering algorithm. The first method involves conversion to Hilbert distances, and the second to Cartesian space. While both can be done relatively easily using Python, Hilbert distances (due to their 1D nature) can suffer from “boundary discontinuities” and lead to problems with clustering algorithms, whereas Cartesian coordinates lend themselves better to clustering methods.

The following code first converts latitude and longitude of all the languoids in the dataset to 3D Cartesian space, and then fits a series of K-means clusters, using the silhouette score to identify the optimal number of clusters.

import numpy as np
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler

def get_clusters(df):
    """A function to determine optimal number of clusters from lat, long"""
    # conversion to 3D Cartesian space
    lat_rad = np.radians(df['latitude'])
    lon_rad = np.radians(df['longitude'])

    df['X'] = np.cos(lat_rad) * np.cos(lon_rad)
    df['Y'] = np.cos(lat_rad) * np.sin(lon_rad)
    df['Z'] = np.sin(lat_rad)

    features = df[['X', 'Y', 'Z']].values

    # z-score standardization
    scaler = StandardScaler()
    scaled_features = scaler.fit_transform(features)

    # iterative silhouette evaluation
    silhouette_scores = {}
    k_range = range(2, 20)  # Checking 2 to 20 potential geographic language macro-areas

    for k in k_range:
        kmeans = KMeans(n_clusters=k, random_state=42, n_init=20)
        labels = kmeans.fit_predict(scaled_features)

        score = silhouette_score(scaled_features, labels)
        silhouette_scores[k] = score
        print(f"Macro-Clusters (K): {k} | silhouette coefficient: {score:.4f}")

    # extract optimal number of clusters
    optimal_k = max(silhouette_scores, key=silhouette_scores.get)
    print(f"Optimal geographic language clusters: K = {optimal_k}")

    print(f"Trying with K = {optimal_k}")

    # apply final model and put each language in its cluster
    final_model = KMeans(n_clusters=optimal_k, random_state=42, n_init=20)
    df['K_macroarea'] = final_model.fit_predict(scaled_features)

    return df

# run the function on the `gldf` dataframe
gldf = get_clusters(gldf)

You should see that the optimal number of clusters identified through this method is six, which is the same number of macroareas used in the Glottolog dataset. We can now proceed to plotting the languages based on the newly-identified clusters, with the following code.

clusters = "K_macroarea" # column for grouping
gldf[clusters] = gldf[clusters].astype(str) # convert to categorical (number to string)

# create the scatter map
fig = px.scatter_geo(
    gldf,                             # the dataframe containing our data
    lat="latitude",                   # name of the column with latitude values
    lon="longitude",                  # name of the column with longitude values
    color=clusters,                   # cluster column dictates color
    hover_name="name",                # name of the language for visualization
    projection="natural earth",       # map projection used by plotly
    title=f"Languages by {clusters}", # title of the map
    color_discrete_sequence=cc.glasbey_dark, # set of distinctive colors (from 256)
)

fig.show() # display the map
Languages by Cartesian clusters

This gives us an interactive map, corresponding to the image above. Comparing this map with the Glottolog macroareas map, there are several observations. The first is that both identify 6 clusters, but in slightly different ways. Glottolog’s macroareas separate Australia from Papunesia/Oceania, while the K-means clustering combines these but separates Asia from Eurasia. A second observation is that some island oceanic languages are grouped with the Americas by the K-means clsuters, in opposition to the Glottolog macroareas. A third thing to notice is that some languages in the arctic are classified differently by the K-means meathod than by Glottolog. A final point is that a few individual languages seem to be oddly colored by the Glottolog grouping, such as Chinese Pidgin English identified above.

Discussion

The fact that the two ways of identifying geographical clusters for languages are so similar is quite striking. Intuitions and expert determinations of “language area” seem in this sense to align quite well with a more naive mathematical approach. You could make an argument that the K-means geographical groupings are actually better in some ways - aligning with real barriers to population movement, such as the Himalayan mountain range and the Wallace Line. Even the K-means grouping of some Oceanic languages with the Americas is plausible, given that contact is attested from at least the 1200s (see also DNA studies).

However, these groupings could also be somewhat spurious. History indicates that humans often find reasons to overcome apparent obstacles, such as when motivated by trade and resources. At the same time, there is much to be said for ease of movement, which geographical proximity facilitates. In the case of the Glottolog macroareas, there are reasonable theoretical reasons for creating these particular groupings, as highlighted in Hammarström & Donohue (2014).

One additional question is how well these slightly different geographical groupings facilitate downstream investigations. The Glottolog macroareas are regularly used in typological studies as categorical grouping variables to control for contact. The K-means Cartesian clusters have yet to be used for this, though I am working on a paper to compare the different groupings. Another common way of controlling for area is by using a Gaussian process term, though this is generally used along with a categorical grouping variable (i.e. macroareas), as I will discuss in a future post. One benefit of the unsupervised clustering approach is that it could potentially be applied to any geographical area, given enough data points, as I discuss in a current working paper.

In the end, it may be best to combine insights derived both from theoretical positions (and our knowledge of human history and migration) as well as from mathematical approaches such as K-means clustering. Each perspective can inform the other, and as we develop better ways of organizing and analyzing our data, this will help us get closer to understanding how languages change.