【问题标题】:Using dplyr mutate_at when a function takes multiple arguments which are different columns当函数接受多个不同列的参数时使用 dplyr mutate_at
【发布时间】:2018-07-06 08:17:50
【问题描述】:

我有一个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_at 对每对列执行相同的操作。如:

df %>%
  mutate(
    x = ifelse(is.na(x_1), x_2, x_1), 
    y = ifelse(is.na(y_1), y_2, y_1)
  )

有没有办法用mutate_at/mutate_each 做到这一点?

这个:

df %>%
  mutate_each(vars(x_1, y_1), funs(ifelse(is.na(.), vars(x_2, y_2), .)))

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

问题与Using functions of multiple columns in a dplyr mutate_at call 类似,但不同之处在于函数调用的第二个参数不是单列,而是vars 中每一列的不同列。

提前致谢。

【问题讨论】:

  • 我现在正在做类似的事情。这与我之前的问题:stackoverflow.com/questions/47005763/… 中的问题相同,但在这种情况下,数据集太大以至于 RStudio 崩溃。
  • a data.table set 循环可能是更快的方法之一。 dplyr::coalesce 的可读性可能会更好一些

标签: r dplyr


【解决方案1】:

我不知道你是否可以这样理解,但这里有一个不同的角度来看待这个问题。如果您发现自己拥有非常广泛的数据(例如,大量具有相似名称的列)并且您想对它们做一些事情,那么使用tidyr::gather tidy 数据(在stata 术语中很长)可能会有所帮助(请参阅文档在这里http://tidyr.tidyverse.org/)。

> 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

将数据转换为这种格式后,使用group_by 组合和重新排列值比尝试mutate_at 更容易。例如,您可以使用df %&gt;% gather() %&gt;% mutate(var = substr(key,1,1)) 获取第一个值,并使用group_by(var) 以不同方式操作xs 和ys。

【讨论】:

  • 我认为这会产生相反的效果。在gather'ing 之后,我会遇到按键的前缀和原始行的唯一标识符进行分组的问题。
  • 为了详细说明问题,我的data.frame有大约117个不同项目的数据,这些数据是不同实验室分批测试样品的实验室测试结果。因此,每个项目有 7 列——样品的测量值、采样值的比例以及有关批次的各种数据,包括批次差异和校准信息。因此,需要进行一些相当大的处理才能使值标准化和一致。
  • 是总共 7 列还是 117 列?您拥有的列越多(列操作越复杂),让mutate 家族做您喜欢的事情就越困难。您可能想让它更整洁(例如,参见cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html)并使用旨在处理此类问题的group_by。或者您可以切换到基本 R 操作(这可能更容易处理复杂的列操作)。
  • 它的 819 列。 7 列,每列 117 个测量变量。将原始测量值转换为可用测量值的过程(使用每个测量变量的其他 6 列)对于 117 个中的每一个都是相同的。这就是为什么我正在寻找一种基于 mutate_ 系列函数的方法。到目前为止,我能够想出的是创建 7 个矩阵,每个矩阵 117 列,但这是一种相当不幸的方法,它确实使代码复杂化。
【解决方案2】:

老问题,但我同意 Jesse 的观点,即您需要稍微整理一下数据。 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

为了对任意数量的列对执行此操作,您可以执行以下操作:

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

这假定应比较的列彼此相邻,并且所有可能具有 NA 值的列都在以_1 为后缀的列中,并且替换值列以_2 为后缀。

【讨论】:

  • 我认为这是正确的,谢谢。我同意需要整理数据——此操作处于一系列步骤的早期,其目的是整理数据。
【解决方案3】:

当我问这个问题时,答案是“你不能!”这不再是答案,因为tidyr 现在支持pivot_widerpivot_longer

【讨论】:

    猜你喜欢
    • 2022-10-12
    • 2017-01-05
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多