以编程方式创建新变量,这些变量是嵌套的其他变量系列的总和
programatically create new variables which are sums of nested series of other variables
我有数据显示某些群体中具有不同教育程度的人的百分比:
df <- data_frame(group = c("A", "B"),
no.highschool = c(20, 10),
high.school = c(70,40),
college = c(10, 40),
graduate = c(0,10))
df
# A tibble: 2 x 5
group no.highschool high.school college graduate
<chr> <dbl> <dbl> <dbl> <dbl>
1 A 20. 70. 10. 0.
2 B 10. 40. 40. 10.
例如A组70%的人有高中学历
我想生成 4 个变量,这些变量给出每个组中低于 4 个教育水平(例如 lessthan_no.highschool、lessthan_high.school 等)的人数比例。
所需的 df 为:
desired.df <- data.frame(group = c("A", "B"),
no.highschool = c(20, 10),
high.school = c(70,40),
college = c(10, 40),
graduate = c(0,10),
lessthan_no.highschool = c(0,0),
lessthan_high.school = c(20, 10),
lessthan_college = c(90, 50),
lessthan_graduate = c(100, 90))
在我的实际数据中,我有很多组和更多的教育水平。当然我可以一次只做一个变量,但是我如何使用 tidyverse
工具以编程方式(优雅地)做到这一点?
我会先在 map()
中做一些类似 mutate_at()
的事情,但是我被绊倒的地方是每个新变量的求和变量列表是不同的。您可以将新变量列表及其对应的变量作为两个列表求和到 pmap()
,但如何简洁地生成第二个列表并不明显。想知道是否有某种嵌套解决方案...
how could I do this programatically (and elegantly) using tidyverse tools?
当然第一步是整理你的数据。列名中的编码信息(如教育级别)不整洁。当您将 education
转换为一个因子时,请确保级别的顺序正确 - 我使用了它们在原始数据列名称中出现的顺序。
library(tidyr)
tidy_result = df %>% gather(key = "education", value = "n", -group) %>%
mutate(education = factor(education, levels = names(df)[-1])) %>%
group_by(group) %>%
mutate(lessthan_x = lag(cumsum(n), default = 0) / sum(n) * 100) %>%
arrange(group, education)
tidy_result
# # A tibble: 8 x 4
# # Groups: group [2]
# group education n lessthan_x
# <chr> <fct> <dbl> <dbl>
# 1 A no.highschool 20 0
# 2 A high.school 70 20
# 3 A college 10 90
# 4 A graduate 0 100
# 5 B no.highschool 10 0
# 6 B high.school 40 10
# 7 B college 40 50
# 8 B graduate 10 90
这给了我们一个漂亮、整洁的结果。如果你想 spread
/cast
将这些数据转换成你不整洁的 desired.df
格式,我建议使用 data.table::dcast
,因为(据我所知)tidyverse 不提供传播多列的好方法。有关 data.table
解决方案或不雅的 tidyr
/dplyr
版本,请参阅 or 。在传播之前,您可以创建一个密钥 less_than_x_key = paste("lessthan", education, sep = "_")
.
这是一个基本的 R 解决方案。虽然问题要求 tidyverse
,但考虑到问题评论中的对话,我决定 post 它。
它使用 apply
和 cumsum
来完成艰苦的工作。然后在 cbind
进入最终结果之前还有一些外观问题。
tmp <- apply(df[-1], 1, function(x){
s <- cumsum(x)
100*c(0, s[-length(s)])/sum(x)
})
rownames(tmp) <- paste("lessthan", names(df)[-1], sep = "_")
desired.df <- cbind(df, t(tmp))
desired.df
# group no.highschool high.school college graduate lessthan_no.highschool
#1 A 20 70 10 0 0
#2 B 10 40 40 10 0
# lessthan_high.school lessthan_college lessthan_graduate
#1 20 90 100
#2 10 50 90
我有数据显示某些群体中具有不同教育程度的人的百分比:
df <- data_frame(group = c("A", "B"),
no.highschool = c(20, 10),
high.school = c(70,40),
college = c(10, 40),
graduate = c(0,10))
df
# A tibble: 2 x 5
group no.highschool high.school college graduate
<chr> <dbl> <dbl> <dbl> <dbl>
1 A 20. 70. 10. 0.
2 B 10. 40. 40. 10.
例如A组70%的人有高中学历
我想生成 4 个变量,这些变量给出每个组中低于 4 个教育水平(例如 lessthan_no.highschool、lessthan_high.school 等)的人数比例。
所需的 df 为:
desired.df <- data.frame(group = c("A", "B"),
no.highschool = c(20, 10),
high.school = c(70,40),
college = c(10, 40),
graduate = c(0,10),
lessthan_no.highschool = c(0,0),
lessthan_high.school = c(20, 10),
lessthan_college = c(90, 50),
lessthan_graduate = c(100, 90))
在我的实际数据中,我有很多组和更多的教育水平。当然我可以一次只做一个变量,但是我如何使用 tidyverse
工具以编程方式(优雅地)做到这一点?
我会先在 map()
中做一些类似 mutate_at()
的事情,但是我被绊倒的地方是每个新变量的求和变量列表是不同的。您可以将新变量列表及其对应的变量作为两个列表求和到 pmap()
,但如何简洁地生成第二个列表并不明显。想知道是否有某种嵌套解决方案...
how could I do this programatically (and elegantly) using tidyverse tools?
当然第一步是整理你的数据。列名中的编码信息(如教育级别)不整洁。当您将 education
转换为一个因子时,请确保级别的顺序正确 - 我使用了它们在原始数据列名称中出现的顺序。
library(tidyr)
tidy_result = df %>% gather(key = "education", value = "n", -group) %>%
mutate(education = factor(education, levels = names(df)[-1])) %>%
group_by(group) %>%
mutate(lessthan_x = lag(cumsum(n), default = 0) / sum(n) * 100) %>%
arrange(group, education)
tidy_result
# # A tibble: 8 x 4
# # Groups: group [2]
# group education n lessthan_x
# <chr> <fct> <dbl> <dbl>
# 1 A no.highschool 20 0
# 2 A high.school 70 20
# 3 A college 10 90
# 4 A graduate 0 100
# 5 B no.highschool 10 0
# 6 B high.school 40 10
# 7 B college 40 50
# 8 B graduate 10 90
这给了我们一个漂亮、整洁的结果。如果你想 spread
/cast
将这些数据转换成你不整洁的 desired.df
格式,我建议使用 data.table::dcast
,因为(据我所知)tidyverse 不提供传播多列的好方法。有关 data.table
解决方案或不雅的 tidyr
/dplyr
版本,请参阅 less_than_x_key = paste("lessthan", education, sep = "_")
.
这是一个基本的 R 解决方案。虽然问题要求 tidyverse
,但考虑到问题评论中的对话,我决定 post 它。
它使用 apply
和 cumsum
来完成艰苦的工作。然后在 cbind
进入最终结果之前还有一些外观问题。
tmp <- apply(df[-1], 1, function(x){
s <- cumsum(x)
100*c(0, s[-length(s)])/sum(x)
})
rownames(tmp) <- paste("lessthan", names(df)[-1], sep = "_")
desired.df <- cbind(df, t(tmp))
desired.df
# group no.highschool high.school college graduate lessthan_no.highschool
#1 A 20 70 10 0 0
#2 B 10 40 40 10 0
# lessthan_high.school lessthan_college lessthan_graduate
#1 20 90 100
#2 10 50 90