【问题标题】:R & dplyr: Assigning group-level characteristics to selected group membersR & dplyr:将组级特征分配给选定的组成员
【发布时间】:2018-03-21 05:23:33
【问题描述】:

我有一个大型数据集,通过一个分组变量 = grp 分为许多小组;一个组的所有成员按照较大数据集的顺序是连续的。每个组的成员都有一个 id 代码 (= id) 并从 1 开始按顺序编号。在一个组中,一些成员满足逻辑标准 = is_child。每个成员都有一个变量 (momloc),其中包含零或另一个组成员的 ID 号(如果存在,则为母亲)。

我希望将 momloc 与其 ID 相同的组成员的数量分配给数据集中的每个人,如果没有,则为零。我正在尝试在 dplyr 中执行此操作,因为我在那里设置了组,并且我有可以工作的代码,但它是嵌套 ifelse 函数的 Rube Goldberg 装置,它为中间值添加了两个额外的列,其中一个包含一个向量,遍历数据集 3 次,速度非常慢。必须有比这更好的方法。我纠结于 mutate、处理行和摘要、处理组的不同语法。

以下是简化的数据集和预期结果

grp      <- c(1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2)
id       <- c(1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7)
is_child <- c(0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0)
momloc   <- c(0, 0, 2, 2, 0, 0, 0, 3, 2, 2, 2)
data <- tibble(grp, id, is_child, momloc)

想要的输出:

out = c(0, 2, 0, 0, 0, 2, 1, 0, 0, 0, 0)

【问题讨论】:

  • 我很难理解out 背后的逻辑。你能解释一下为什么out[2] = 2out[7] = 1吗?我也看不到is_child 的来源。
  • 没关系。 @akrun 的回答很清楚;-)
  • @MauritsEvers 我想我错过了那里的1。 @andrewH 最好显示创建所需输出的代码

标签: r dplyr iteration summary


【解决方案1】:

很可能是我误解了您的问题。但我认为momlocgrp 中的table() 是您正在寻找的:

library(tidyverse)
grp      <- c(1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2) %>% factor
id       <- c(1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7) %>% factor
is_child <- c(0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0)
momloc   <- c(0, 0, 2, 2, 0, 0, 0, 3, 2, 2, 2)
data <- tibble(grp, id, is_child, momloc)

out = c(0, 2, 0, 0, 0, 2, 1, 0, 0, 0, 0)

data2 <- filter(data, is_child == 1)

data3 <- table(id = factor(data2$momloc, levels = levels(id)), grp = data2$grp) %>% 
    as.data.frame(responseName = "out")

left_join(data, data3, by = c("grp", "id"))
#> # A tibble: 11 x 5
#>    grp   id    is_child momloc   out
#>    <fct> <fct>    <dbl>  <dbl> <int>
#>  1 1     1           0.     0.     0
#>  2 1     2           0.     0.     2
#>  3 1     3           1.     2.     0
#>  4 1     4           1.     2.     0
#>  5 2     1           0.     0.     0
#>  6 2     2           0.     0.     2
#>  7 2     3           0.     0.     1
#>  8 2     4           1.     3.     0
#>  9 2     5           1.     2.     0
#> 10 2     6           1.     2.     0
#> 11 2     7           0.     2.     0

all(cbind(data, out) == left_join(data, data3, by = c("grp", "id")))
#> [1] TRUE

请注意,我将 grpid 更改为考虑第 2 行和第 3 行。

【讨论】:

    【解决方案2】:

    这是使用dplyr 的解决方案。

    data.moms <- data %>% 
      split(grp) %>%
      lapply(., function(data.grp) {
        data.grp %>% group_by(id, grp) %>% summarise(NumChildren = sum(.$momloc == id))
      }) %>% do.call(rbind, .)
    

    我们首先使用split(grp)将数据帧拆分为多个数据帧,每个组一个。

    然后,我们使用lapply() 对列表中的每个data.frame 应用一个操作。 对于这些数据帧中的每一个,我们按idgrp 进行分组——即使这意味着唯一的“组”。我们也可以只在id 上进行分组,但同时分组意味着我们可以保留两列。

    现在列表中的每个 data.frame 包含 3 列

    • 身份证
    • grp
    • NumChildren

    现在,我们可以使用do.call(rbind, .) 重新组合汇总的数据帧。

    > data.moms
    # A tibble: 11 x 3
    # Groups:   id [7]
          id   grp NumChildren
       <dbl> <dbl>       <int>
     1  1.00  1.00           0
     2  2.00  1.00           2
     3  3.00  1.00           0
     4  4.00  1.00           0
     5  1.00  2.00           0
     6  2.00  2.00           3
     7  3.00  2.00           1
     8  4.00  2.00           0
     9  5.00  2.00           0
    10  6.00  2.00           0
    11  7.00  2.00           0
    

    【讨论】:

      【解决方案3】:

      我提出了一个仅使用dplyr 的解决方案。

      首先,我只保留孩子(假设您只想将它​​们算作您的out[6] = 2 而不是 3)。然后,我使用count()创建了momloc的频率表,并将其合并到原始数据中。

      data %>%
          filter(is_child == 1) %>% # only count for children
          group_by(grp) %>%
          count(momloc) %>%
          right_join(data, by = c("grp" = "grp", "momloc" = "id")) %>%
          rename(
              id = momloc,
              momloc = momloc.y,
              out = n
          ) %>%
          mutate(out = ifelse(is.na(out), 0, out))
      
      #> # A tibble: 11 x 5
      #> # Groups:   grp [2]
      #>      grp    id   out is_child momloc
      #>    <dbl> <dbl> <dbl>    <dbl>  <dbl>
      #>  1     1     1     0        0      0
      #>  2     1     2     2        0      0
      #>  3     1     3     0        1      2
      #>  4     1     4     0        1      2
      #>  5     2     1     0        0      0
      #>  6     2     2     2        0      0
      #>  7     2     3     1        0      0
      #>  8     2     4     0        1      3
      #>  9     2     5     0        1      2
      #> 10     2     6     0        1      2
      #> 11     2     7     0        0      2
      

      【讨论】:

      • 男人!当我得到多个这样的很棒的答案时,无法接受所有这些真的很痛苦。我从这些中学到了一些新东西。我会接受大卫的回答,因为我认为它最接近我真正要求的,而且因为我担心分裂和绑定约 900 万个家庭。但我想我实际上会尝试所有三个,看看哪个最快,可能下周的某个时间。我会在这里发布结果。
      猜你喜欢
      • 1970-01-01
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-18
      • 2013-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多