在函数接受多个不同列的参数时使用dplyr mutate_

在函数接受多个不同列的参数时使用dplyr mutate_,r,dplyr,R,Dplyr,我有一个data.frame,其中有大量列的名称遵循一种模式。例如: df <- data.frame( x_1 = c(1, NA, 3), x_2 = c(1, 2, 4), y_1 = c(NA, 2, 1), y_2 = c(5, 6, 7) ) 有没有一种方法可以让mutate\u在/mutate\u每个 这: 我尝试过的各种变体都失败了 问题与类似,但不同之处在于函数调用的第二个参数不是单个列,而是vars中每列的不同列 提前谢谢 我不知道你是否可以这样

我有一个
data.frame
,其中有大量列的名称遵循一种模式。例如:

df <- data.frame(
  x_1 = c(1, NA, 3), 
  x_2 = c(1, 2, 4), 
  y_1 = c(NA, 2, 1), 
  y_2 = c(5, 6, 7)
)
有没有一种方法可以让
mutate\u在
/
mutate\u每个

这:

我尝试过的各种变体都失败了

问题与类似,但不同之处在于函数调用的第二个参数不是单个列,而是vars中每列的不同列


提前谢谢

我不知道你是否可以这样理解,但对于这个问题,这里有一个不同的观点。如果您发现自己拥有非常广泛的数据(例如,大量具有类似名称的列),并且希望对其进行处理,那么使用
tidyr::gather
(请参见此处的文档)整理数据(长期的
stata
术语)可能会有所帮助


将数据转换为这种格式后,使用
groupby
组合和重新排列值会更容易,而不是尝试在
事物上进行
变异。例如,您可以使用
df%%>%gather()%%>%mutate(var=substr(key,1,1))
对第一个值进行ge,并使用
groupby(var)

x和
y进行不同的操作。当我问这个问题时,答案是“你不能!”这不再是答案,由于
tidyr
现在支持
pivot\u更宽
pivot\u更长
这是一个老问题,但我同意Jesse的观点,您需要稍微整理一下数据
collect
将是一种方法,但它缺少了某种可能性,即
stats::restrape
可以指定要收集的列组。下面是一个使用
重塑
的解决方案:

df %>% 
   reshape(varying   = list(c("x_1", "y_1"), c("x_2", "y_2")), 
           times     = c("x", "y"),
           direction = "long") %>% 
   mutate(x = ifelse(is.na(x_1), x_2, x_1)) %>% 
   reshape(idvar     = "id", 
           timevar   = "time",
           direction = "wide") %>% 
   rename_all(funs(gsub("[a-zA-Z]+(_*)([0-9]*)\\.([a-zA-Z]+)", "\\3\\1\\2", .)))
#   id x_1 x_2 x y_1 y_2 y
# 1  1   1   1 1  NA   5 5
# 2  2  NA   2 2   2   6 2
# 3  3   3   4 3   1   7 1
为了对任意数量的列对执行此操作,您可以执行以下操作:

df2 <- setNames(cbind(df, df), c(t(outer(letters[23:26], 1:2, paste, sep = "_"))))
v <- split(names(df2), purrr::map_chr(names(df2), ~ gsub(".*_(.*)", "\\1", .)))
n <- unique(purrr::map_chr(names(df2), ~ gsub("_[0-9]+", "", .) ))
df2 %>% 
    reshape(varying   = v, 
            times     = n,
            direction = "long") %>% 
     mutate(x = ifelse(is.na(!!sym(v[[1]][1])), !!sym(v[[2]][1]), !!sym(v[[1]][1]))) %>% 
     reshape(idvar     = "id", 
             timevar   = "time",
             direction = "wide") %>% 
     rename_all(funs(gsub("[a-zA-Z]+(_*)([0-9]*)\\.([a-zA-Z]+)", "\\3\\1\\2", .)))
#   id w_1 w_2 w x_1 x_2 x y_1 y_2 y z_1 z_2 z
# 1  1   1   1 1  NA   5 5   1   1 1  NA   5 5
# 2  2  NA   2 2   2   6 2  NA   2 2   2   6 2
# 3  3   3   4 3   1   7 1   3   4 3   1   7 1
df2%
重塑(idvar=“id”,
timevar=“time”,
direction=“wide”)%%>%
重命名所有(funs(gsub(“[a-zA-Z]+([0-9]*)\ \。([a-zA-Z]+)”,“\\3\\1\\2”,))
#id w_1 w_2 w x_1 x_2 x y_1 y_2 y z_1 z_2 z
#111NA55111NA555
#2 2钠2 2 6 2钠2 2 6 2
# 3  3   3   4 3   1   7 1   3   4 3   1   7 1

这假设应该进行比较的列彼此相邻,并且可能具有NA值的所有列都位于以
\u 1
为后缀的列中,替换值列以
\u 2

为后缀,我认为这会产生相反的效果。在
收集
”之后,我会遇到根据密钥前缀和原始行的唯一标识符进行分组的问题。为了详细说明这个问题,我的
data.frame
包含了大约117个不同项目的数据,这些项目是由不同实验室分批测试样本的实验室测试结果。因此,每个项目有7列——为样本测量的值、采样值的范围以及有关批次的各种数据,包括批次差异和校准信息。因此,需要进行一些相当实质性的处理才能使值规范化和一致。总共是7列还是117列?您拥有的列越多(列操作也越复杂),就越难让
mutate
系列执行您喜欢的操作。您可能希望使其更整洁(例如,请参见),并使用
groupby
,该工具旨在处理此类问题。或者您可以切换到基本R操作(这可能更容易处理复杂的列操作)。117个测量变量各7列。使用每个测量变量的其他6列,将原始测量值转换为可用测量值的过程对于117个变量中的每一个都是相同的。这就是为什么我在寻找一种基于
mutate\uu
函数族的方法。到目前为止,我能想到的是创建7个矩阵,每个矩阵有117列,但这是一个非常不幸的方法,它确实使代码复杂化。我现在正在处理类似的事情。这与我上一个问题中的问题相同:,但在这种情况下,数据集太大,导致RStudio崩溃。
data.table
set
循环可能是更快的方法之一
dplyr::coalesce
在可读性方面可能会更好一些,我认为这是正确的,谢谢。我同意数据需要整理——这个操作是一系列步骤中的早期操作,这些步骤的目的是整理数据。
> df %>% gather()
   key value
1  x_1     1
2  x_1    NA
3  x_1     3
4  x_2     1
5  x_2     2
6  x_2     4
7  y_1    NA
8  y_1     2
9  y_1     1
10 y_2     5
11 y_2     6
12 y_2     7
df %>% 
   reshape(varying   = list(c("x_1", "y_1"), c("x_2", "y_2")), 
           times     = c("x", "y"),
           direction = "long") %>% 
   mutate(x = ifelse(is.na(x_1), x_2, x_1)) %>% 
   reshape(idvar     = "id", 
           timevar   = "time",
           direction = "wide") %>% 
   rename_all(funs(gsub("[a-zA-Z]+(_*)([0-9]*)\\.([a-zA-Z]+)", "\\3\\1\\2", .)))
#   id x_1 x_2 x y_1 y_2 y
# 1  1   1   1 1  NA   5 5
# 2  2  NA   2 2   2   6 2
# 3  3   3   4 3   1   7 1
df2 <- setNames(cbind(df, df), c(t(outer(letters[23:26], 1:2, paste, sep = "_"))))
v <- split(names(df2), purrr::map_chr(names(df2), ~ gsub(".*_(.*)", "\\1", .)))
n <- unique(purrr::map_chr(names(df2), ~ gsub("_[0-9]+", "", .) ))
df2 %>% 
    reshape(varying   = v, 
            times     = n,
            direction = "long") %>% 
     mutate(x = ifelse(is.na(!!sym(v[[1]][1])), !!sym(v[[2]][1]), !!sym(v[[1]][1]))) %>% 
     reshape(idvar     = "id", 
             timevar   = "time",
             direction = "wide") %>% 
     rename_all(funs(gsub("[a-zA-Z]+(_*)([0-9]*)\\.([a-zA-Z]+)", "\\3\\1\\2", .)))
#   id w_1 w_2 w x_1 x_2 x y_1 y_2 y z_1 z_2 z
# 1  1   1   1 1  NA   5 5   1   1 1  NA   5 5
# 2  2  NA   2 2   2   6 2  NA   2 2   2   6 2
# 3  3   3   4 3   1   7 1   3   4 3   1   7 1