为了形象化这个问题,我们假设我有一个 R 数据集 data
,其中包含以下列:
- 因素
- 参数
- T1_g1
- T2_g1
- T1_g2
- T2_g2
我想对列的子集执行操作:
data_final <- data %>%
mutate_at(vars(T1, T2), funs(if(param > 100) {
. * T(n)_g1
} else {
. * T(n)_g2
}
如何在表达式 T(n)_g1
中引用正确的列名称,以便它分别从 T1_g1
和 T2_g1
获取数据,同时变异?
(在实际案例中,我有更多的列和条件,因此手动输入所有可能的情况不是一个选项)
最佳答案
if
需要一次比较,但由于这将是一个向量,因此您需要 if_else
(或 ifelse
)。我不知道您是否可以在快速 mutate*
界面中根据要更改的名称(轻松地)动态确定其他列名称。一个快速破解可能是:
data %>%
mutate(
T1 = if_else(param > 100, T1_g1, T1_g2) * T1,
T2 = if_else(param > 100, T2_g1, T2_g2) * T2
)
但这仅在您有一个小/静态的 T*
变量列表需要修改时才有效。
如果这些T*
变量的数量是动态的(或者只是“高”),一种方法包括将帧重新整形为更长的格式。 (有人可能会说,无论如何,长格式可能更适合这种情况,所以我将引导您了解 Wide-long-mutate 以及 Wide-long-mutate-wide。)
一些数据:
x <- data_frame(
param = c(1L,50L,101L,150L),
T1 = 1:4,
T2 = 5:8,
T1_g1 = (1:4)/10,
T1_g2 = (1:4)*10,
T2_g1 = (5:8)/10,
T2_g2 = (5:8)*10
)
x
# # A tibble: 4 x 7
# param T1 T2 T1_g1 T1_g2 T2_g1 T2_g2
# <int> <int> <int> <dbl> <dbl> <dbl> <dbl>
# 1 1 1 5 0.1 10 0.5 50
# 2 50 2 6 0.2 20 0.6 60
# 3 101 3 7 0.3 30 0.7 70
# 4 150 4 8 0.4 40 0.8 80
首先,第一次 reshape :
x %>%
gather(k, v, -param) %>%
mutate(
num = sub("^T([0-9]+).*", "\\1", k),
k = sub("^T[0-9]+(.*)", "T\\1", k)
) %>%
spread(k, v)
# # A tibble: 8 x 5
# param num T T_g1 T_g2
# <int> <chr> <dbl> <dbl> <dbl>
# 1 1 1 1 0.1 10
# 2 1 2 5 0.5 50
# 3 50 1 2 0.2 20
# 4 50 2 6 0.6 60
# 5 101 1 3 0.3 30
# 6 101 2 7 0.7 70
# 7 150 1 4 0.4 40
# 8 150 2 8 0.8 80
我们所做的是将四行与 3*n
列进行转换,其中包含 T#
、T#_g1
和 T#_g2
模式,仅包含 3 列,但行数是 n
倍。我们将这个n
保留为另一列(暂时)。一般来说,这可以说是一种很好的使用格式:tidyverse
,尤其是 ggplot2
确实喜欢这种格式的数据,但可能还有更多我不知道的数据。
现在是完整的 shebang(重复前几行代码):
x %>%
gather(k, v, -param) %>%
mutate(
num = sub("^T([0-9]+).*", "\\1", k),
k = sub("^T[0-9]+(.*)", "T\\1", k)
) %>%
spread(k, v) %>%
mutate(T = T * if_else(param > 100, T_g1, T_g2)) %>%
gather(k, v, -param, -num) %>%
mutate(k = if_else(grepl("^T", k), paste0("T", num, substr(k, 2, nchar(k))), k)) %>%
select(-num) %>%
spread(k, v)
# # A tibble: 4 x 7
# param T1 T1_g1 T1_g2 T2 T2_g1 T2_g2
# <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1 10 0.1 10 250 0.5 50
# 2 50 40 0.2 20 360 0.6 60
# 3 101 0.900 0.3 30 4.90 0.7 70
# 4 150 1.6 0.4 40 6.4 0.8 80
reshape 后,您最初的 mutate_at
概念被简化为单个 mutate(T = ...)
调用。其余的涉及重新水化宽度。
如果您的数据很大,这可能会有点麻烦。其他解决方案可能涉及手动确定 T#
列并手动执行 ifelse
(在 mutate
之外)。
关于通过 mutate_at 中的名称片段引用其他列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53284289/