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
这就是管道。我将在一秒钟内解释这些步骤。您要查找的内容可能是 treatment
、xyz
和 ans
列的组合。
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
拆分为c1
和x
将有助于翻译第一个并保留后者:
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
从这里开始,让我们使用variableTransform
将c1
、c2
和c3
变量转换为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
),我们需要将一些变量带回列中。 (我们@987654341@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 在数据表中的等价物是啥? [复制]