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()
的 momloc
和 grp
是你要找的:
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
请注意,我将 grp
和 id
更改为第 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 应用一个操作。
对于这些数据帧中的每一个,我们按 id
和 grp
分组——尽管这意味着唯一的 '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
我有一个大数据集,通过分组变量分成许多小组= 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()
的 momloc
和 grp
是你要找的:
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
请注意,我将 grp
和 id
更改为第 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 应用一个操作。
对于这些数据帧中的每一个,我们按 id
和 grp
分组——尽管这意味着唯一的 '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