使用 dplyr 在 R 中自动创建变量的最佳方法
Best way to automate variable creation in R using dplyr
df <- as.data.frame(cbind(c(1:10), c(15, 70, 29, 64, 57, 29, 10, 80,81, 71)))
V1 V2
1 1 15
2 2 70
3 3 29
4 4 64
5 5 57
6 6 29
7 7 10
8 8 80
9 9 81
10 10 71
cuts <- c(5, 10, 90, 95)
我想为所有(在本例中为四个)切割值 x
(例如 P5
、P10
、P90
和 P95
) 表示是否v2 <= x
。添加变量的直接方法 "by hand" 不会超出少数范围:
df %<>%
mutate( P5 = V2 <= 5) %>%
mutate(P10 = V2 <= 10) %>%
mutate(P90 = V2 <= 90) %>%
mutate(P95 = V2 <= 95)
V1 V2 P5 P10 P90 P95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
显然,要使数据保持 "tidy" 格式,应应用最终的 gather(year, islegal, c(3;6))
。
我尝试过的另一种方法是
do.call(rbind, lapply(cuts, function(x) {
df %>% mutate(year = x, islegal = V2 <= x)
})) %>% spread(year, islegal)
V1 V2 5 10 90 95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
显然,我会删除最后的 spread()
以使数据保持 "tidy" 格式。
问题:是否有比第二种方法更好或更通用的使用 {dplyr}
的方法来自动创建变量(像这里的类似分位数的截止值,或虚拟变量或类似),不需要像第一种方法那样显式输入 cuts
的内容?
如果您想 "programatically" 使用 dplyr
,您应该查看 "standard evaluation" 替代函数的常用版本。请参阅非标准评估小插图 (vignette("nse", "dplyr")
)。
基本上除了 mutate
函数之外,还有一个 mutate_
函数允许您指定转换列表。在你的情况下,你可以用这样的东西建立你的列表
cuts <- c(5,10,90,95)
mymutate <- setNames(lapply(cuts , function(x)
lazyeval::interp(~V2<=x, x=x)), paste0("P", cuts ))
然后你可以用
进行转换
df %>% mutate_(.dots=mymutate )
# V1 V2 P5 P10 P90 P95
# 1 1 15 FALSE FALSE TRUE TRUE
# 2 2 70 FALSE FALSE TRUE TRUE
# 3 3 29 FALSE FALSE TRUE TRUE
# 4 4 64 FALSE FALSE TRUE TRUE
# 5 5 57 FALSE FALSE TRUE TRUE
# 6 6 29 FALSE FALSE TRUE TRUE
# 7 7 10 FALSE TRUE TRUE TRUE
# 8 8 80 FALSE FALSE TRUE TRUE
# 9 9 81 FALSE FALSE TRUE TRUE
# 10 10 71 FALSE FALSE TRUE TRUE
当然你不需要 dplyr 来做这么简单的事情。
names(cuts) <- paste0("p", cuts)
data.frame(df, lapply(cuts, function(x) df$V2 <= x))
V1 V2 p5 p10 p90 p95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
如果您打算最终将数据转换为整齐的数据,您可以简单地从一个开始:
library(dplyr)
df <- as.data.frame(cbind(c(1:10), c(15, 70, 29, 64, 57, 29, 10, 80,81, 71)))
cuts <- data_frame(P=c(5, 10, 90, 95))
p_df <- df %>% tidyr::crossing(cuts) %>%
mutate(flag=V2<=P)
p_df
# V1 V2 P flag
#1 1 15 5 FALSE
#2 1 15 10 FALSE
#3 1 15 90 TRUE
#4 1 15 95 TRUE
#5 2 70 5 FALSE
#...
如果原来的格式真的是你想要的,tidyr::spread
结果
p_df %>%
tidyr::spread(P, flag, sep="")
# V1 V2 P5 P10 P90 P95
#1 1 15 FALSE FALSE TRUE TRUE
#2 2 70 FALSE FALSE TRUE TRUE
#3 3 29 FALSE FALSE TRUE TRUE
#4 4 64 FALSE FALSE TRUE TRUE
#5 5 57 FALSE FALSE TRUE TRUE
#6 6 29 FALSE FALSE TRUE TRUE
#7 7 10 FALSE TRUE TRUE TRUE
#8 8 80 FALSE FALSE TRUE TRUE
#9 9 81 FALSE FALSE TRUE TRUE
#10 10 71 FALSE FALSE TRUE TRUE
df <- as.data.frame(cbind(c(1:10), c(15, 70, 29, 64, 57, 29, 10, 80,81, 71)))
V1 V2
1 1 15
2 2 70
3 3 29
4 4 64
5 5 57
6 6 29
7 7 10
8 8 80
9 9 81
10 10 71
cuts <- c(5, 10, 90, 95)
我想为所有(在本例中为四个)切割值 x
(例如 P5
、P10
、P90
和 P95
) 表示是否v2 <= x
。添加变量的直接方法 "by hand" 不会超出少数范围:
df %<>%
mutate( P5 = V2 <= 5) %>%
mutate(P10 = V2 <= 10) %>%
mutate(P90 = V2 <= 90) %>%
mutate(P95 = V2 <= 95)
V1 V2 P5 P10 P90 P95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
显然,要使数据保持 "tidy" 格式,应应用最终的 gather(year, islegal, c(3;6))
。
我尝试过的另一种方法是
do.call(rbind, lapply(cuts, function(x) {
df %>% mutate(year = x, islegal = V2 <= x)
})) %>% spread(year, islegal)
V1 V2 5 10 90 95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
显然,我会删除最后的 spread()
以使数据保持 "tidy" 格式。
问题:是否有比第二种方法更好或更通用的使用 {dplyr}
的方法来自动创建变量(像这里的类似分位数的截止值,或虚拟变量或类似),不需要像第一种方法那样显式输入 cuts
的内容?
如果您想 "programatically" 使用 dplyr
,您应该查看 "standard evaluation" 替代函数的常用版本。请参阅非标准评估小插图 (vignette("nse", "dplyr")
)。
基本上除了 mutate
函数之外,还有一个 mutate_
函数允许您指定转换列表。在你的情况下,你可以用这样的东西建立你的列表
cuts <- c(5,10,90,95)
mymutate <- setNames(lapply(cuts , function(x)
lazyeval::interp(~V2<=x, x=x)), paste0("P", cuts ))
然后你可以用
进行转换df %>% mutate_(.dots=mymutate )
# V1 V2 P5 P10 P90 P95
# 1 1 15 FALSE FALSE TRUE TRUE
# 2 2 70 FALSE FALSE TRUE TRUE
# 3 3 29 FALSE FALSE TRUE TRUE
# 4 4 64 FALSE FALSE TRUE TRUE
# 5 5 57 FALSE FALSE TRUE TRUE
# 6 6 29 FALSE FALSE TRUE TRUE
# 7 7 10 FALSE TRUE TRUE TRUE
# 8 8 80 FALSE FALSE TRUE TRUE
# 9 9 81 FALSE FALSE TRUE TRUE
# 10 10 71 FALSE FALSE TRUE TRUE
当然你不需要 dplyr 来做这么简单的事情。
names(cuts) <- paste0("p", cuts)
data.frame(df, lapply(cuts, function(x) df$V2 <= x))
V1 V2 p5 p10 p90 p95
1 1 15 FALSE FALSE TRUE TRUE
2 2 70 FALSE FALSE TRUE TRUE
3 3 29 FALSE FALSE TRUE TRUE
4 4 64 FALSE FALSE TRUE TRUE
5 5 57 FALSE FALSE TRUE TRUE
6 6 29 FALSE FALSE TRUE TRUE
7 7 10 FALSE TRUE TRUE TRUE
8 8 80 FALSE FALSE TRUE TRUE
9 9 81 FALSE FALSE TRUE TRUE
10 10 71 FALSE FALSE TRUE TRUE
如果您打算最终将数据转换为整齐的数据,您可以简单地从一个开始:
library(dplyr)
df <- as.data.frame(cbind(c(1:10), c(15, 70, 29, 64, 57, 29, 10, 80,81, 71)))
cuts <- data_frame(P=c(5, 10, 90, 95))
p_df <- df %>% tidyr::crossing(cuts) %>%
mutate(flag=V2<=P)
p_df
# V1 V2 P flag
#1 1 15 5 FALSE
#2 1 15 10 FALSE
#3 1 15 90 TRUE
#4 1 15 95 TRUE
#5 2 70 5 FALSE
#...
如果原来的格式真的是你想要的,tidyr::spread
结果
p_df %>%
tidyr::spread(P, flag, sep="")
# V1 V2 P5 P10 P90 P95
#1 1 15 FALSE FALSE TRUE TRUE
#2 2 70 FALSE FALSE TRUE TRUE
#3 3 29 FALSE FALSE TRUE TRUE
#4 4 64 FALSE FALSE TRUE TRUE
#5 5 57 FALSE FALSE TRUE TRUE
#6 6 29 FALSE FALSE TRUE TRUE
#7 7 10 FALSE TRUE TRUE TRUE
#8 8 80 FALSE FALSE TRUE TRUE
#9 9 81 FALSE FALSE TRUE TRUE
#10 10 71 FALSE FALSE TRUE TRUE