R&dplyr:为选定的组成员分配组级特征

R & dplyr: Assigning group-level characteristics to selected group members

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

我希望为数据集中的每个人分配 momloc 等于其 ID 的组成员数,如果 none 为零。我正在尝试在 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)

很可能是我误解了你的问题。但我认为 table()momlocgrp 是你要找的:

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 行的因素。

这是使用 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 分组——尽管这意味着唯一的 'groups'。我们也可以只对 id 进行分组,但是对两者进行分组意味着我们可以保留两列。

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

  • id
  • NumChildren

现在,我们可以 re-combine 使用 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

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

首先,我只保留 children(假设您只想将它​​们计为您的 out[6] = 2 而不是 3)。然后,我使用 count() 创建 table 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