我想从数据集中前 3 列的每个值中减去每个“空白”列的平均值,如下所示:
df <- data.frame(da=1:5, d2=6:10, dd=2:6,
blank...1=c(0.1, 0.1, 0.4, 0.2, 0.1), d5=2:6, dg=7:11,
di=3:7, blank...2=c(0.2, 0.2, 0.4, 0.1, 0.1), dm=21:25,
h4=5:9, d7=26:30, blank...3=c(0.1, 0.3, 0.4, 0.4, 0.1))
df
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 1 6 2 0.1 2 7 3 0.2 21 5 26 0.1
# 2 2 7 3 0.1 3 8 4 0.2 22 6 27 0.3
# 3 3 8 4 0.4 4 9 5 0.4 23 7 28 0.4
# 4 4 9 5 0.2 5 10 6 0.1 24 8 29 0.4
# 5 5 10 6 0.1 6 11 7 0.1 25 9 30 0.1
换句话说,我想将 blank...1 的平均值减去 da、d2 和 dd 列的每个值;然后将 blank...2 的平均值减去 d5、dg 和 di 等列的每个值。实际数据集实际上有 15 个空白,需要从前面的 11 列中减去。
我已经使用
colMeans
生成了向量中所有“空白”列的平均值,但我不知道如何编写函数来指示哪个值用于哪些列。
如何通过函数来实现这一点?
这是一种更详细的 tidyverse 方法。首先,我将数据弄长,跟踪原始行以及每列应使用哪组“空白”。
library(tidyverse)
df_long <- df |>
mutate(row = row_number()) |>
pivot_longer(-row) |>
mutate(group = cumsum(lag(name |> str_detect("blank"), 1, 0)), .by = row)
然后我可以更改每个值以减去相关的“空白”平均值(通过减去名称包含“空白”的组中的值的平均值)并再次重新调整宽度。
df_long |>
mutate(value = if_else(name |> str_detect("blank"), value,
value - mean(value[name |> str_detect("blank")])),
.by = group) |>
select(-group) |>
pivot_wider(names_from = name, values_from = value)
结果
# A tibble: 5 × 13
row da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
<int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.7 4.74 25.7 0.1
2 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.7 5.74 26.7 0.3
3 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.7 6.74 27.7 0.4
4 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.7 7.74 28.7 0.4
5 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.7 8.74 29.7 0.1
这是受 jpsmith 启发的另一种基本 R 方法:
blanks = grep("blank", names(df)) # find the blank columns
blank_means = colMeans(df[blanks]) # get their means
## subtract blanks means from the non-blank columns
## this relies on having exactly 3 non-blank columns per blank column
df[-blanks] = Map("-", df[-blanks], rep(blank_means, each = 3))
df
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
# 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
# 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
# 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
# 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1
我确信有一个更优雅的解决方案,但在基本 R 中,您可以首先识别名称中带有“空白”的那些(
blnknms
),然后使用 lapply
迭代这些列并减去该列的平均值来自前三列:
# get columns with "blank" in the name
blnknms <- grep("blank", names(df))
df[,-blnknms] <- unlist(lapply(blnknms, \(x){
df[,(x-3):(x-1)] - mean(df[,x])
}), recursive = FALSE)
输出:
# da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
# 1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
# 2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
# 3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
# 4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
# 5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1
如果您
rep
吃每个 colMeans
的次数与相应非空白切片的列数相同,则只需从整个切片中减去结果向量即可。请注意,要从列中减去向量,我们需要t
转置并重新t
转置。
> bl <- grepl('^blank', names(df))
> df[!bl] <- t(t(df[!bl]) - rep(colMeans(df[bl]), each=which.max(bl) - 1L))
> df
da d2 dd blank...1 d5 dg di blank...2 dm h4 d7 blank...3
1 0.82 5.82 1.82 0.1 1.8 6.8 2.8 0.2 20.74 4.74 25.74 0.1
2 1.82 6.82 2.82 0.1 2.8 7.8 3.8 0.2 21.74 5.74 26.74 0.3
3 2.82 7.82 3.82 0.4 3.8 8.8 4.8 0.4 22.74 6.74 27.74 0.4
4 3.82 8.82 4.82 0.2 4.8 9.8 5.8 0.1 23.74 7.74 28.74 0.4
5 4.82 9.82 5.82 0.1 5.8 10.8 6.8 0.1 24.74 8.74 29.74 0.1