dplyr mutate 中的 for 循环

Posted

技术标签:

【中文标题】dplyr mutate 中的 for 循环【英文标题】:forloop inside dplyr mutate 【发布时间】:2016-10-15 19:18:22 【问题描述】:

我想以更优雅的方式使用 mutate 执行一些列操作,因为我的表中有 200 多个列,我想使用 mutate 进行转换。

这是一个例子

样本数据:

df <- data.frame(treatment=rep(letters[1:2],10),
c1_x=rnorm(20),c2_y=rnorm(20),c3_z=rnorm(20),
c4_x=rnorm(20),c5_y=rnorm(20),c6_z=rnorm(20),
c7_x=rnorm(20),c8_y=rnorm(20),c9_z=rnorm(20),
c10_x=rnorm(20),c11_y=rnorm(20),c12_z=rnorm(20),
c_n=rnorm(20))

示例代码:

dfm<-df %>%
mutate(cx=(c1_x*c4_x/c_n+c7_x*c10_x/c_n),
cy=(c2_y*c5_y/c_n+c8_y*c11_y/c_n),
cz=(c3_z*c6_z/c_n+c9_z*c12_z/c_n))

【问题讨论】:

“执行以下操作”非常模糊,需要读者费力地阅读您的代码,你知道的。你可以用词来形容它。 同意@Gregor;你也可以 tidyr::gather() (hadleyverse 2) 而不是 reshape2:melt()ing (hadleyverse 1) 理论上,他们的无限 hadleyverses?我们只发现了其中两个。我们只是一个mere simulation。 这是否意味着有一个 hadleyverse 允许您使用 ggplot2 制作饼图和第二个 y 轴? 如果不明显,hadleyverse 1 = (plyr, reshape2, ggplot2); hadleyverse 2 = (dplyr, tidyr, rvest, readr, ..., ggvis?)。我不知道reshape和ggplot的原始版本(混沌?) 【参考方案1】:

尽管有切线,但使用tidyr 函数的初步建议是您需要去的地方。这个函数管道似乎可以根据您提供的内容完成工作。

您的数据:

df <- data.frame(treatment=rep(letters[1:2],10),
                 c1_x=rnorm(20), c2_y=rnorm(20), c3_z=rnorm(20),
                 c4_x=rnorm(20), c5_y=rnorm(20), c6_z=rnorm(20),
                 c7_x=rnorm(20), c8_y=rnorm(20), c9_z=rnorm(20),
                 c10_x=rnorm(20), c11_y=rnorm(20), c12_z=rnorm(20),
                 c_n=rnorm(20))
library(dplyr)
library(tidyr)

第一个辅助 data.frame 用于将您的 c#_[xyz] 变量转换为统一的变量。我敢肯定还有其他方法可以解决这个问题,但它可以工作,并且根据您的 200 多列相对容易复制和扩展。

variableTransform <- data_frame(
  cnum = paste0("c", 1:12),
  cvar = rep(paste0("a", 1:4), each = 3)
)
head(variableTransform)
# Source: local data frame [6 x 2]
#    cnum  cvar
#   <chr> <chr>
# 1    c1    a1
# 2    c2    a1
# 3    c3    a1
# 4    c4    a2
# 5    c5    a2
# 6    c6    a2

这就是管道。我将在一秒钟内解释这些步骤。您要查找的内容可能是 treatmentxyzans 列的组合。

df %>%
  tidyr::gather(cnum, value, -treatment, -c_n) %>%
  tidyr::separate(cnum, c("cnum", "xyz"), sep = "_") %>%
  left_join(variableTransform, by = "cnum") %>%
  select(-cnum) %>%
  tidyr::spread(cvar, value) %>%
  mutate(
    ans = a1 * (a2/c_n) + a3 * (a4/c_n)
  ) %>%
  head
#   treatment       c_n xyz         a1          a2         a3          a4         ans
# 1         a -1.535934   x -0.3276474  1.45959746 -1.2650369  1.02795419  1.15801448
# 2         a -1.535934   y -1.3662388 -0.05668467  0.4867865 -0.10138979 -0.01828831
# 3         a -1.535934   z -2.5026018 -0.99797169  0.5181513  1.20321878 -2.03197283
# 4         a -1.363584   x -0.9742016 -0.12650863  1.3612361 -0.24840493  0.15759418
# 5         a -1.363584   y -0.9795871  1.52027017  0.5510857  1.08733839  0.65270681
# 6         a -1.363584   z  0.2985557 -0.22883439  0.1536078 -0.09993095  0.06136036

首先,我们取原始数据,将所有(除了两列)列变成两列“列名”和“列值”对:

df %>%
  tidyr::gather(cnum, value, -treatment, -c_n) %>%
#   treatment         c_n cnum      value
# 1         a  0.20745647 c1_x -0.1250222
# 2         b  0.01015871 c1_x -0.4585088
# 3         a  1.65671028 c1_x -0.2455927
# 4         b -0.24037137 c1_x  0.6219516
# 5         a -1.16092349 c1_x -0.3716138
# 6         b  1.61191700 c1_x  1.7605452

c1_x 拆分为c1x 将有助于翻译第一个并保留后者:

  tidyr::separate(cnum, c("cnum", "xyz"), sep = "_") %>%
#   treatment         c_n cnum xyz      value
# 1         a  0.20745647   c1   x -0.1250222
# 2         b  0.01015871   c1   x -0.4585088
# 3         a  1.65671028   c1   x -0.2455927
# 4         b -0.24037137   c1   x  0.6219516
# 5         a -1.16092349   c1   x -0.3716138
# 6         b  1.61191700   c1   x  1.7605452

从这里开始,让我们使用variableTransformc1c2c3 变量转换为a1(对其他9 个变量重复):

  left_join(variableTransform, by = "cnum") %>%
  select(-cnum) %>%
#   treatment         c_n xyz      value cvar
# 1         a  0.20745647   x -0.1250222   a1
# 2         b  0.01015871   x -0.4585088   a1
# 3         a  1.65671028   x -0.2455927   a1
# 4         b -0.24037137   x  0.6219516   a1
# 5         a -1.16092349   x -0.3716138   a1
# 6         b  1.61191700   x  1.7605452   a1

由于我们想同时处理多个变量(使用简单的mutate),我们需要将一些变量带回列中。 (我们@9​​87654341@ed 和现在spread 的原因有助于我保持事物的组织和命名。我相信有人可以想出另一种方法来做到这一点。)

  tidyr::spread(cvar, value) %>% head
#   treatment       c_n xyz         a1          a2         a3          a4
# 1         a -1.535934   x -0.3276474  1.45959746 -1.2650369  1.02795419
# 2         a -1.535934   y -1.3662388 -0.05668467  0.4867865 -0.10138979
# 3         a -1.535934   z -2.5026018 -0.99797169  0.5181513  1.20321878
# 4         a -1.363584   x -0.9742016 -0.12650863  1.3612361 -0.24840493
# 5         a -1.363584   y -0.9795871  1.52027017  0.5510857  1.08733839
# 6         a -1.363584   z  0.2985557 -0.22883439  0.1536078 -0.09993095

从这里,我们只需要mutate就可以得到正确的答案。

【讨论】:

【参考方案2】:

与 r2evans 的回答类似,但操作更多而不是连接(解释更少)。

library(tidyr)
library(stringr)
library(dplyr)

# get it into fully long form
gather(df, key = cc_xyz, value = value, c1_x:c12_z) %>%
    # separate off the xyz and the c123
    separate(col = cc_xyz, into = c("cc", "xyz")) %>%
    # extract the number
    mutate(num = as.numeric(str_replace(cc, pattern = "c", replacement = "")),
           # mod it by 4 for groupings and add a letter so its a good col name
           num_mod = paste0("v", (num %% 4) + 1)) %>%
    # remove unwanted columns
    select(-cc, -num) %>%
    # go into a reasonable data width for calculation
    spread(key = num_mod, value = value) %>%
    # calculate
    mutate(result = v1 + v2/c_n + v3 + v4 / c_n)

#    treatment          c_n xyz           v1           v2            v3          v4        result
# 1          a -1.433858289   x  1.242153708 -0.985482158 -0.0240414692  1.98710285    0.51956295
# 2          a -1.433858289   y -0.019255516  0.074453615 -1.6081599298  1.18228939   -2.50389188
# 3          a -1.433858289   z -0.362785313  2.296744655 -0.0610463292  0.89797526   -2.65188998
# 4          a -0.911463819   x -1.088308527 -0.703388193  0.6308253909  0.22685013    0.06534405
# 5          a -0.911463819   y  1.284513516  1.410276163  0.5066869590 -2.07263912    2.51790289
# 6          a -0.911463819   z  0.957778345 -1.136532104  1.3959561507 -0.50021647    4.14947069
# ...

【讨论】:

以上是关于dplyr mutate 中的 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

dplyr 中的 mutate_each / summarise_each:如何选择某些列并为变异列赋予新名称?

R使用dplyr group_by / sum for循环,作为连接列表输出

dplyr mutate 和 summarise 在数据表中的等价物是啥? [复制]

R中的dplyr mutate - 添加列作为列的连接

当我不知道 data.frame 中的列名时,当我使用 dplyr mutate 函数时

在 dplyr mutate_at 调用中使用多列的函数