【问题标题】:programatically create new variables which are sums of nested series of other variables以编程方式创建新变量,这些变量是其他变量的嵌套系列之和
【发布时间】:2018-08-26 03:24:23
【问题描述】:

我的数据显示了某些群体中具有不同教育程度的人的百分比:

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(),但如何简洁地生成第二个列表并不明显。想知道是否有某种嵌套解决方案...

【问题讨论】:

  • 没有低于 no.highschool 的级别,因此 lessthan_no.highschool 将始终为 0。
  • desired.df 中有变量less.than.hs。不应该是no.highschool吗?
  • 不确定您的意思?
  • @lost Gregor 击败了我,在您想要的结果中,您重复输入的变量,因此它们的名称应该相同。其中之一不是。我认为这是一个错字。哦,我错过了关于tidyverse 的部分,所以我正忙于编写基本的 R 方式。会感兴趣吗?
  • 这是一个错字,抱歉。固定。

标签: r dplyr tidyverse purrr


【解决方案1】:

这是一个基本的 R 解决方案。虽然问题要求tidyverse 一个,但考虑到 cmets 中对问题的对话,我决定发布它。
它使用applycumsum 来完成艰苦的工作。然后在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

【讨论】:

    【解决方案2】:

    我怎样才能使用 tidyverse 工具以编程方式(优雅地)做到这一点?

    毫无疑问,第一步是整理您的数据。列名中的编码信息(如 edu 级别)不整齐。当您将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 并没有提供一个很好的方式来传播多列。请参阅Spreading multiple columns with tidyrHow can I spread repeated measures of multiple variables into wide format? 了解data.table 解决方案或不优雅的tidyr/dplyr 版本。在传播之前,您可以创建一个密钥less_than_x_key = paste("lessthan", education, sep = "_")

    【讨论】:

    • 故意采用不整洁的格式。之所以采用这种格式,是因为它将连接到格式整齐的个人级数据,并将用于建模等。
    • 好的,太好了。但是如果你想“优雅地”使用tidyverse工具,你需要先整理一下。我链接到的问题应该可以帮助你恢复到你需要的不整洁的格式。我认为我不需要在这里重复这些答案。如果有更改或更新,那些关注该问题的问题应该得到更新。
    • 这可行,尽管变量的顺序与 OP 中的不同:desired.df % select(-n) %>% mutate(education = paste0("lessthan_", education )) %>% spread(education, lessthan_x) %>% right_join(df)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    相关资源
    最近更新 更多