I think your model is sound. The important question is how you should define how difficult a race is. If you’re happy with defining it as the difference between the average speed of the race and the average speed of an average race, it’s easy. Here’s an R example:
Let’s generate 15 individuals, each with a relative (on an additive scale) speed advantage:
id = 1:15
indspeed = (id-mean(id))/4
This means that, e.g., the fastest individual has an average speed of 1.75 (km/h or whatever) higher than the mean speed for a given race. Similarly, let’s define the mean speeds for five races as 60, 65, 70, 75 and 80:
race = seq(60,80,5)
We can now generate the results for the various races. For each race, we select 7 individuals at random, and simulate their speed, based on the race difficulty (average race speed), individual speed advantage/disadvantage and some randomness.
gen_data = function(raceid) {
raceind = sample(id, 7)
speed = race[raceid] + indspeed[raceind] + rnorm(length(raceind), sd=5)
race_df = data.frame(ind = raceind, race=raceid, speed=speed)
race_df
}
set.seed(1) # Reproducible random data
dl = lapply(seq_along(race), gen_data)
d = do.call(rbind, dl)
d = transform(d, race = factor(race), ind=factor(ind))
We can now fit a mixed-effects model with race as fixed effects and individuals as random effects:
library(lme4)
fit = lmer(speed ~ race + 0 + (1|ind), data = d)
The fixed effects coefficient estimates are estimates of the mean speed of each race:
> fixef(fit)
race1 race2 race3 race4 race5
60.4 67.9 69.2 74.9 82.2
To get a measure of their relative difficulties, you can subtract the average speed:
> fixef(fit) - mean(fixef(fit))
race1 race2 race3 race4 race5
-10.52 -3.07 -1.72 4.03 11.28
You can of course calculate other measures of difficulty, e.g., percentage of the average speed (if that makes sense), or perhaps the square root of the absolute differences.
Note that each time you add a new race, the relative difficulties of the old races will change. The changes may be large if the new race is much more difficult (or easy) than all the previous races.
If you have many races, you might consider fitting race as a random effect too. Then the conditional means of these random effects are estimates of the relative difficulty. This has the advantage of shrinking the estimates towards the global mean, generally giving better estimates. Here’s the R syntax:
fit = lmer(speed ~ (1|race) + (1|ind), data = d)
ranef(fit)$race
Note that you need to include the overall intercept here (implicit in the formula). Also note that you don’t need to subtract the mean from ranef(fit)$race
, as random effects are already assumed to have a mean of zero.