在dplyr mutate里面的forloop

时间:2016-06-14 21:32:13

标签: r for-loop dplyr mutated

我想以更优雅的方式使用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))

2 个答案:

答案 0 :(得分:3)

尽管有切线,但使用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

从这里开始,我们使用c1c2c3a1变量翻译成variableTransform(重复其他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),我们需要将一些变量带回到列中。 (我们gather编辑并且现在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即可得到正确答案。

答案 1 :(得分:0)

类似于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
# ...