R’s formula machinery canonicalizes interaction labels by sorting the names inside :. So b:a and a:b are the same term, and when you pass the terms object to model.matrix() it will print the canonical label (usually a:b) regardless of the order you wrote in the formula—even with keep.order = TRUE (which only controls the order of terms, not the order of variables within an interaction).
You can verify they’re identical:
dd <- data.frame(a = 1:3, b = 1:3)
mm1 <- model.matrix(terms(~ a + b + b:a, keep.order = TRUE), dd) mm2 <- model.matrix(terms(~ a + b + a:b, keep.order = TRUE), dd)
all.equal(mm1, mm2)
If you absolutely need the printed column name to match your original b:a, just rename after creation:
mm <- model.matrix(terms(~ a + b + b:a, keep.order = TRUE), dd) colnames(mm) <- sub("^a:b$", "b:a", colnames(mm)) mm
(For wider cases you could write a small renamer that maps any x:y to your preferred order.)
So the behavior you’re seeing is expected: terms()/model.matrix() normalize interaction labels, and there’s no option to keep b:a other than renaming the columns post hoc.
More practical R tips & design-matrix gotchas: [https://rprogrammingbooks.com]