У меня есть таблица с людьми и группами, к которым они принадлежат. Форматируется так:
person_id <- c("A1", "A1", "A1", "A1", "A2", "A2", "A3", "A3", "B1", "B1", "C1", "C1", "C2", "C2")
year <- c(2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016)
group_id <- c("abc", "abc", "cdz", "cdz", "abc", "abc", "ghe", "ghe", "abc", "fjx", "ghe", "ghe", "cdz", "cdz")
example <- data.frame(person_id, group_id, year)
Я хочу создать столбец, в котором для каждого человека будет показано, с какими людьми он делит группу в определенный год. Это то, что у меня есть на данный момент:
example <- within(example, {
connections <- ave(person_id, group_id, year,
FUN=function(x) paste(x, collapse=', ')
)})
res <- example %>%
group_by(person_id, year) %>%
summarise(joint=paste(connections, collapse=', '))
Это близко к тому, что мне нужно, но я не хочу включать person_id в каждую строку. Например, мой код создает новый столбец с первым значением «A1,A2,B1,A1,C2». Я хотел бы, чтобы это значение было «A2,B1,C2». В моем примере человек B1 ни с кем не делит группу в 2016 году. Мой код создает значение строки «B1», но я бы хотел, чтобы эта ячейка была пустой строкой. Как я могу этого добиться?
Кроме того, данные, с которыми я работаю, довольно большие, примерно 1 миллиард строк. Группировать по два раза кажется довольно неэффективным, но я не уверен, что то, что я хочу сделать, возможно без этого. Есть ли лучший способ подойти к этому?
Примечание. Я не могу использовать tidyr. Кроме того, моя версия R — 3.4.3.
@GregorThomas Извините, просто опечатка.





код без tidyr:
library(dplyr)
library(tibble)
example %>%
mutate(p_y = paste(person_id, year, sep = '_'), person_id =NULL, year = NULL) %>%
igraph::graph_from_data_frame() %>%
igraph::components() %>%
igraph::membership() %>%
enframe() %>%
filter(grepl('_', name)) %>%
mutate(person_id = sub("_.*", "", name), year = sub(".*_", "", name))%>%
group_by(year, value) %>%
mutate(value = list(person_id)) %>%
group_by(person_id, year) %>%
summarise(value = toString(setdiff(value[[1]], person_id)), .groups = 'drop')
# A tibble: 12 × 3
person_id year value
<chr> <chr> <chr>
1 A1 2015 "A2, B1, C2"
2 A1 2016 "A2, C2"
3 A2 2015 "A1, B1, C2"
4 A2 2016 "A1, C2"
5 A3 2015 "C1"
6 A3 2016 "C1"
7 B1 2015 "A1, A2, C2"
8 B1 2016 ""
9 C1 2015 "A3"
10 C1 2016 "A3"
11 C2 2015 "A1, A2, B1"
12 C2 2016 "A1, A2"
Редактировать:
В случае отсутствия транзитивности:
example %>%
group_by(year, group_id) %>%
mutate(v = list(person_id)) %>%
group_by(person_id, year) %>%
summarise(v = toString(setdiff(unlist(v), person_id)))
# A tibble: 12 × 3
# Groups: person_id [6]
person_id year v
<chr> <dbl> <chr>
1 A1 2015 "A2, B1, C2"
2 A1 2016 "A2, C2"
3 A2 2015 "A1, B1"
4 A2 2016 "A1"
5 A3 2015 "C1"
6 A3 2016 "C1"
7 B1 2015 "A1, A2"
8 B1 2016 ""
9 C1 2015 "A3"
10 C1 2016 "A3"
11 C2 2015 "A1"
12 C2 2016 "A1"
Я получаю сообщение «Ошибка: столбец «год» должен иметь длину 1 (общее значение), а не 14». Я думаю, это потому, что моя версия R — 3.4.3. Возможен ли этот подход в моей версии R (я не могу обновить R на своем компьютере)? Кроме того, в ваших результатах для каждой строки указан 2015 год. Как я могу указать правильный год для каждого человека?
К сожалению, у меня на машине нет тидыра. У меня также нет разрешения на установку новых пакетов. Поэтому я не могу использовать unite. А откуда п_й?
@sla813 p_y — новая переменная. Вы можете просто использовать mutate(p_y = paste(person_id, year, sep = '_'), person_id =NULL, year = NULL) вместо unite.
@sla813 у тебя есть igraph? Это основа кода. Остальное можно сделать, используя базу R. Дайте мне знать, если у вас есть igraph.
У меня есть igraph.
Можно ли это сделать без раздельного? Это тоже из Тидыра.
@ sla813 да. измените separate на mutate(person_id = sub("_.*", "", name), year = sub(".*_", "", name))
Теперь я получаю сообщение «Ошибка: значение столбца не может быть изменено, поскольку это группирующая переменная».
@ sla813, кажется, старый R не позволял изменять переменную группировки. Замените mutate(value = ...) на mutate(val= list(person_id)), затем в summarize используйте val[[1]] вместо value[[1]].
Одна проблема остается. Строки 2, 4, 12 для значения столбца не должны содержать «B1», поскольку B1 находится в отдельной группе в 2016 году.
@ sla813 это упущение. должно быть group_by(year, value) в первой группе, а не group_by(value) отдельно
Извините, еще кое-что. Поскольку C2 входит только в группу с A1, строки 3,4,7 не должны содержать «C2», а строки 11, 12 должны быть только «A1».
@ sla813 C2 находится в группе с A1, а A1 находится в группе с A2. поэтому по транзитивности A2 и C2 находятся в группе. Если вы не рассматриваете только одно ребро, а не все ребра, в этом случае мы не будем использовать igraph. Это не было хорошо представлено в вашем исходном вопросе.
aggregate по group_id с toString, объединить с оригиналом, aggregate снова по person_id, затем strsplit на ", ", установить diff, сделать data.frame и merge с оригиналом.
> example |>
+ merge(aggregate(cbind(pid=person_id) ~ group_id + year, example, toString)) |>
+ aggregate(pid ~ person_id, toString) |>
+ {\(.) Map(\(x, y) setdiff(unique(x), y), strsplit(.$pid, ', '), .$person_id) |>
+ list() |>
+ list2DF() |>
+ data.frame(person_id=.$person_id, pid=_)}() |>
+ merge(example)
person_id Var.2 group_id year
1 A1 A2, B1, C2 abc 2015
2 A1 A2, B1, C2 abc 2016
3 A1 A2, B1, C2 cdz 2015
4 A1 A2, B1, C2 cdz 2016
5 A2 A1, B1 abc 2015
6 A2 A1, B1 abc 2016
7 A3 C1 ghe 2015
8 A3 C1 ghe 2016
9 B1 A1, A2 abc 2015
10 B1 A1, A2 fjx 2016
11 C1 A3 ghe 2015
12 C1 A3 ghe 2016
13 C2 A1 cdz 2015
14 C2 A1 cdz 2016
Данные:
> dput(example)
structure(list(person_id = c("A1", "A1", "A1", "A1", "A2", "A2",
"A3", "A3", "B1", "B1", "C1", "C1", "C2", "C2"), group_id = c("abc",
"abc", "cdz", "cdz", "abc", "abc", "ghe", "ghe", "abc", "fjx",
"ghe", "ghe", "cdz", "cdz"), year = c(2015, 2016, 2015, 2016,
2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016, 2015, 2016)), class = "data.frame", row.names = c(NA,
-14L))
Я не понимаю, почему
C1появляется в первом ряду.C1не делит группу сA1в 2015 или 2016 году, так почему же он отображается в первой строке результата?