Will you allow base R answers? Then, it's an ugly solution:
df <- data.frame(a = runif(10, 2.0, 7.5),
b = runif(10, 5.0, 9.5),
c = runif(10, 1.0, 9.5),
d = runif(10, 2.0, 9.1),
e = runif(10, 3.0, 9.2),
f = runif(10, 4.0, 9.3),
g = runif(10, 5.0, 9.4),
h = runif(10, 6.0, 9.5),
i = runif(10, 7.0, 9.6),
j = runif(10, 8.0, 9.7),
k = runif(10, 9.0, 9.8),
l = runif(10, 1.0, 9.9))
# main modification
df_mod <- sapply(X = 0:((ncol(x = df) / 3) - 1),
FUN = function(t)
{
temp <- df[, (3 * t) + 1:3]
(temp / rowSums(x = temp)) * 100
})
# Given in question
df[, 1:3] = sweep(df[, 1:3], 1, rowSums(df[, 1:3]), `/`) * 100
df[, 4:6] = sweep(df[, 4:6], 1, rowSums(df[, 4:6]), `/`) * 100
df[, 7:9] = sweep(df[, 7:9], 1, rowSums(df[, 7:9]), `/`) * 100
df[, 10:12] = sweep(df[, 10:12], 1, rowSums(df[, 10:12]), `/`) * 100
# method 1: using idea from https://stackoverflow.com/a/4227483/11117265
df_1 <- do.call(what = cbind.data.frame,
args = df_mod)
names(x = df_1) <- names(x = df)
all.equal(target = df_1,
current = df)
#> [1] TRUE
# method 2
df_2 <- cbind.data.frame(lapply(X = df_mod,
FUN = cbind))
names(x = df_2) <- names(x = df)
all.equal(target = df_2,
current = df)
#> [1] TRUE
Created on 2019-03-07 by the reprex package (v0.2.1)