T. rex size distributions

dinosaurs
r
Published

February 15, 2025

I ought to be spending this weekend working on a risk intelligence thing for work, but I’ve stumbled across something that brings my inner child immense joy and such things cannot be ignored. It is this 2019 paper “An Older and Exceptionally Large Adult Specimen of Tyrannosaurus rex” by Persons et al. First it’s cool to see paleontology in action, instead of filtered through talking head documentaries. Second, the diligent authors have personally measured 12 T. rex specimens, which is a decent chunk of the 29 or-so specimens discovered to-date, and so we have a cool dataset to play with.

Code
library(tidyverse)
library(knitr)

t.rex <- read_tsv("t.rex-persons.tsv", col_types = "ccnnnnnnnnnnnnnnn") |>
  rename(Specimen = Specimens) |>
  mutate(Morph = factor(Morph, levels = c("Robust", "Gracile")))

kable(t.rex, format.args = list(decimal.mark = ".", big.mark = ","))
Table 1: T. rex bone measurements in millimetres (Table 1 from Persons et al 2019).
Specimen Morph Dentary tooth row length Scapula blade width Manual p. I-1 length Ilium length Femur length Femur circumference Proximal femur width Tibia length Tibia shaft width Fibula length Fibula shaft width Astragalus height Astragalus width Pedal p. IV-1 length Body mass (kg)
RSM P2523.8 Robust 595 74.8 98.5 1,545 1,333 590 426 1,140 184 995 62 328 310 184 8,870
CM 9380 (AMNH 973) Robust 508 73.0 NA 1,540 1,269 534 399 1,166 150 1,025 NA NA NA 145 6,740
FMNH PR2081 Robust 585 68.0 78.0 1,525 1,321 580 380 1,140 160 1,030 59 NA NA 154 8,462
MOR 1125 Robust 492 65.7 NA NA 1,150 515 370 1,060 150 915 53 280 295 170 6,100
RTMP 81.12.1, NMC 9950 Robust NA NA NA NA NA 495 NA 1,095 155 985 65 NA NA 140 5,469
BHI 3033 Gracile 575 NA NA 1,540 1,350 505 350 1,065 158 945 55 325 280 NA 5,779
MOR 555 Gracile 635 65.0 89.0 1,470 1,280 520 370 1,150 170 1,035 56 NA 235 176 6,264
MOR 980 Gracile 546 NA NA 1,397 1,232 483 NA NA NA NA NA NA NA NA 5,112
RTMP 81.6.1 Gracile 530 NA NA NA 1,210 460 270 1,030 160 NA 52 NA NA 107 4,469
BM R8040 (AMNH 5881) NA NA NA NA NA NA 480 330 NA NA NA NA NA NA 124 5,025
MOR 009 NA NA NA 90.0 1,180 1,100 469 NA 1,105 140 930 NA NA NA NA 4,714
USNM 6183 NA NA NA NA NA 1,040 426 NA 910 NA NA NA NA NA NA 3,617

We can also add “Goliath”, the exciting 2024 specimen. Bear in mind that Persons didn’t personally measure this fossil, so that’s a source of potential error. I took the measurements from the few social media posts about the fossil. The lack of information on this specimen seems strange to me, I even wonder if it’s a hoax. It wouldn’t be the first time.

Code
campione.body.mass.kg <- function(min.femoral.circ.mm) 10**((2.754 * log10(min.femoral.circ.mm) - 0.683) - 3)

goliath <- read_tsv("t.rex-goliath.tsv", col_types = "ccnnnnnnnnnnnnnnn") |>
  mutate(
    Morph = factor(Morph, levels = c("Robust", "Gracile")),
    `Body mass (kg)` = campione.body.mass.kg(`Femur circumference`)
  )

t.rex <- bind_rows(t.rex, goliath)
kable(select(goliath, Specimen, `Femur length`, `Femur circumference`, `Body mass (kg)`))
Table 2: Reported measurements for the Goliath specimen.
Specimen Femur length Femur circumference Body mass (kg)
Goliath 1371 648 11483.59

The most interesting bone is the femur, being as it’s more likely to be present and is used to estimate body mass. The equation used is from Campione et al 2014. Bear in mind they suggest a 25% prediction error.

An interesting thing about T. rex is that there are two morphs, gracile and robust, which I understand to be layman’s terms for “elven” and “chonky”. Let’s see if that’s visible in the femur measurements.

Code
ggplot(t.rex) +
  aes(x = `Femur length`, y = `Femur circumference`, colour = Morph, label = Specimen) +
  geom_label(nudge_y = 10, na.rm = TRUE) +
  geom_point(na.rm = TRUE) +
  scale_x_continuous(limits = c(1000, 1400)) +
  scale_y_continuous(limits = c(400, 700)) +
  theme_minimal()
Figure 1: Femur circumference and length for T. rex specimens.

Looking at this scatterplot, there’s a linear relationship between length and circumference, and Goliath sits neatly on it. (Too neatly? Hmm. Should it even be linear? Wouldn’t the square-cube law suggest femurs should get thicker faster than they get longer?) However it’s not obvious if Goliath should be gracile or robust. In fact, it’s not particularly clear from the femurs alone why some are gracile and some robust; CM 9380 (Holotype) and MOR 555 (Wankel) are very close, for example. We need more features. Unfortunately, the T. rex data are very sparse, so we need to impute missing values.

Tibia length is relatively complete, can we use that?

Code
ggplot(t.rex) +
  aes(x = `Femur length`, y = `Tibia length`, colour = Morph) +
  geom_point() +
  scale_x_continuous(limits = c(1000, 1400)) +
  scale_y_continuous(limits = c(900, 1200)) +
  theme_minimal()
Figure 2: Femur length and tibia length for T. rex specimens.

The relationship between fibia length and tibia length is surprisingly not very informative.

Code
ggplot(t.rex) +
  aes(x = `Femur circumference`, y = `Tibia length`, colour = Morph) +
  geom_point() +
  scale_x_continuous(limits = c(400, 700)) +
  scale_y_continuous(limits = c(900, 1200)) +
  theme_minimal()
Figure 3: Femur circumference and tibia length for T. rex specimens.

Femur circumference relates a little better. Let’s try a dead simple linear model to predict the missing femur lengths. We can include body mass, which is a function of femur circumference, to try and capture some of the knowledge from that model.

reg <- lm(`Femur length` ~ `Femur circumference` * `Body mass (kg)` + `Tibia length`, data = t.rex, subset = !is.na(t.rex$`Tibia length`) & !is.na(`Femur length`))
summary(reg)

Call:
lm(formula = `Femur length` ~ `Femur circumference` * `Body mass (kg)` + 
    `Tibia length`, data = t.rex, subset = !is.na(t.rex$`Tibia length`) & 
    !is.na(`Femur length`))

Residuals:
       1        2        3        4        6        7        9       11 
  -2.758   10.381    3.116 -116.353   97.051   30.241   49.109  -69.132 
      12 
  -1.655 

Coefficients:
                                         Estimate Std. Error t value Pr(>|t|)
(Intercept)                            -1.136e+04  2.430e+04  -0.467    0.665
`Femur circumference`                   5.020e+01  1.037e+02   0.484    0.654
`Body mass (kg)`                       -3.832e+00  8.868e+00  -0.432    0.688
`Tibia length`                         -2.585e-01  8.777e-01  -0.294    0.783
`Femur circumference`:`Body mass (kg)`  3.317e-03  8.054e-03   0.412    0.702

Residual standard error: 88.3 on 4 degrees of freedom
Multiple R-squared:  0.6787,    Adjusted R-squared:  0.3574 
F-statistic: 2.112 on 4 and 4 DF,  p-value: 0.2433

It’s not great, though slightly better than the other combinations I tried and probably the best we could do with such a small dataset. The additional imputed femur length (RTMP 81.12.1) is feasible enough.

Code
preds <- predict(reg, newdata = t.rex)

t.rex |>
  mutate(`Femur length` = coalesce(`Femur length`, preds)) |>
  select(`Specimen`, `Morph`, `Femur length`, `Femur circumference`, `Tibia length`, `Body mass (kg)`) |>
  kable()
Table 3: Imputed femur length for RTMP 81.12.1.
Specimen Morph Femur length Femur circumference Tibia length Body mass (kg)
RSM P2523.8 Robust 1333.000 590 1140 8870.00
CM 9380 (AMNH 973) Robust 1269.000 534 1166 6740.00
FMNH PR2081 Robust 1321.000 580 1140 8462.00
MOR 1125 Robust 1150.000 515 1060 6100.00
RTMP 81.12.1, NMC 9950 Robust 1230.292 495 1095 5469.00
BHI 3033 Gracile 1350.000 505 1065 5779.00
MOR 555 Gracile 1280.000 520 1150 6264.00
MOR 980 Gracile 1232.000 483 NA 5112.00
RTMP 81.6.1 Gracile 1210.000 460 1030 4469.00
BM R8040 (AMNH 5881) NA NA 480 NA 5025.00
MOR 009 NA 1100.000 469 1105 4714.00
USNM 6183 NA 1040.000 426 910 3617.00
Goliath NA 1371.000 648 NA 11483.59

I’m not sure what else to do with this. Must be tough being a paleontologist, working with so little direct data!