我有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)
)
Run Code Online (Sandbox Code Playgroud)
我想申请mutate_at对每对列执行相同的操作。如:
df %>%
mutate(
x = ifelse(is.na(x_1), x_2, x_1),
y = ifelse(is.na(y_1), y_2, y_1)
)
Run Code Online (Sandbox Code Playgroud)
有没有办法用mutate_at/做到这一点mutate_each?
这个:
df %>%
mutate_each(vars(x_1, y_1), funs(ifelse(is.na(.), vars(x_2, y_2), .)))
Run Code Online (Sandbox Code Playgroud)
我尝试过的各种变体都失败了。
该问题类似于在 dplyr mutate_at 调用中使用多列的函数,但不同之处在于函数调用的第二个参数不是单个列,而是 vars 中每列的不同列。
提前致谢。
老问题,但我同意杰西的观点,你需要整理一下你的数据。gather将是一种可行的方法,但它缺乏某种stats::reshape可以指定要收集的列组的可能性。所以这里有一个解决方案reshape:
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
Run Code Online (Sandbox Code Playgroud)
为了对任意数量的列对执行此操作,您可以执行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
这假设应比较的列彼此相邻,并且具有可能的 NA 值的所有列都位于以 为后缀的列中,_1并且替换值列以 为后缀_2。