Как группировать и объединять строки, но пропустить одну группу?

У меня есть таблица с людьми и группами, к которым они принадлежат. Форматируется так:

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.

Я не понимаю, почему C1 появляется в первом ряду. C1 не делит группу с A1 в 2015 или 2016 году, так почему же он отображается в первой строке результата?

Gregor Thomas 23.05.2024 19:54

@GregorThomas Извините, просто опечатка.

sla813 23.05.2024 20:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
84
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

код без 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 год. Как я могу указать правильный год для каждого человека?

sla813 23.05.2024 20:36

К сожалению, у меня на машине нет тидыра. У меня также нет разрешения на установку новых пакетов. Поэтому я не могу использовать unite. А откуда п_й?

sla813 23.05.2024 21:21

@sla813 p_y — новая переменная. Вы можете просто использовать mutate(p_y = paste(person_id, year, sep = '_'), person_id =NULL, year = NULL) вместо unite.

Onyambu 23.05.2024 21:30

@sla813 у тебя есть igraph? Это основа кода. Остальное можно сделать, используя базу R. Дайте мне знать, если у вас есть igraph.

Onyambu 23.05.2024 21:31

У меня есть igraph.

sla813 23.05.2024 21:58

Можно ли это сделать без раздельного? Это тоже из Тидыра.

sla813 23.05.2024 22:28

@ sla813 да. измените separate на mutate(person_id = sub("_.*", "", name), year = sub(".*_", "", name))

Onyambu 23.05.2024 22:31

Теперь я получаю сообщение «Ошибка: значение столбца не может быть изменено, поскольку это группирующая переменная».

sla813 23.05.2024 22:42

@ sla813, кажется, старый R не позволял изменять переменную группировки. Замените mutate(value = ...) на mutate(val= list(person_id)), затем в summarize используйте val[[1]] вместо value[[1]].

Onyambu 23.05.2024 22:44

Одна проблема остается. Строки 2, 4, 12 для значения столбца не должны содержать «B1», поскольку B1 находится в отдельной группе в 2016 году.

sla813 24.05.2024 00:03

@ sla813 это упущение. должно быть group_by(year, value) в первой группе, а не group_by(value) отдельно

Onyambu 24.05.2024 00:18

Извините, еще кое-что. Поскольку C2 входит только в группу с A1, строки 3,4,7 не должны содержать «C2», а строки 11, 12 должны быть только «A1».

sla813 24.05.2024 00:41

@ sla813 C2 находится в группе с A1, а A1 находится в группе с A2. поэтому по транзитивности A2 и C2 находятся в группе. Если вы не рассматриваете только одно ребро, а не все ребра, в этом случае мы не будем использовать igraph. Это не было хорошо представлено в вашем исходном вопросе.

Onyambu 24.05.2024 00:44

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))

Другие вопросы по теме