Here is a similar option with data.table , where we convert data.frame to data.table ( setDT(df) ), for each group "athlt" we order for "week" and create a column 'cp' for quick assignment ( := ) link.
library(data.table) setDT(df)[order(week), cp := power/power[1L]*100 ,by=athlt]
We can also use setorder , which is usually memory efficient as it reorders the data set by reference. But in this case (as pointed out in the comments of @Arun), the above method will also be very efficient, since order only calculates indexes instead of reordering the entire data set.
setorder(setDT(df),athlt,week)[, cp:= power/power[1L] *100, athlt][]
Or, if the "week" is numeric, you can use which.min without using order
setDT(df)[, cp := power/power[which.min(week)]*100, by=athlt]
source share