Связывание групп в R - PullRequest
       27

Связывание групп в R

2 голосов
/ 02 июля 2019

У меня следующая проблема. У меня в таблице два идентификатора, и мне нужно иметь возможность создать один идентификатор для всей группы, в этих двух идентификаторах есть ссылка. Пример очистит все:

library(dplyr)
df
# A tibble: 12 x 4
   id_1  id_2        val res_col
   <chr> <chr>     <dbl> <chr>  
 1 G     NA     1.01     G      
 2 G     NA    -0.255    G      
 3 G     NA     0.595    G      
 4 Z     G     -0.881    G      
 5 Z     G     -0.127    G      
 6 Z     G      0.399    G      
 7 R     NA     0.749    R      
 8 R     NA    -0.447    R      
 9 R     NA    -1.70     R      
10 D     Z      0.118    G      
11 D     Z      0.000169 G      
12 D     Z     -0.522    G 

Вот пример и столбец результата, который мне нужен. Проблема в том, что id_1 - это мой исходный идентификатор, а id_2 - это вторичный идентификатор, который сообщает, с каким исходным идентификатором связан вторичный идентификатор. Таким образом, G сам по себе, Z связан с G, R сам по себе, а D на самом деле полностью связан с G через Z. Я хотел бы получить первые id_1 для каждой группы. У меня нет способа сортировки таблицы, чтобы добиться каких-либо отношений отставания / опережения. res_col это то, что я хотел бы получить.

edit 1, в моих исходных данных могут быть десятки таких ссылок. редактировать 2, у меня более 100 тыс. записей, и я не знаю, что это за ссылки.

DATA:

df <- tibble(id_1 = c(rep("G", 3), rep("Z", 3), rep("R", 3), rep("D", 3)),
             id_2 = c(rep(NA, 3), rep("G", 3), rep(NA, 3), rep("Z", 3)),
             val = rnorm(n = 12),
             res_col = c(rep("G", 6), rep("R", 3), rep("G", 3)))

Мои мысли и то, что я сейчас пытаюсь. Я немного убрал использование igraph, возможно, еще будут и лучшие, но пока займусь этим. Спасибо.

library(dplyr)
library(igraph)

df <- tibble(id_1 = c(rep("G", 3), rep("Z", 3), rep("R", 3), rep("D", 3)),
             id_2 = c(rep(NA, 3), rep("G", 3), rep(NA, 3), rep("Z", 3)),
             val = rnorm(n = 12),
             res_col = c(rep("G", 6), rep("R", 3), rep("G", 3)))

groups <- df %>%
  select(id_1, id_2) %>% 
  mutate(id_2 = case_when(is.na(id_2) ~ id_1,
                          TRUE ~ id_2)) %>% 
  graph_from_data_frame(.) %>% 
  components(.) %>% 
  .$membership %>% 
  tibble(id_1 = names(.),
         group = .)

groups %>% 
  group_by(group) %>% 
  mutate(group_id = id_1[1]) %>% 
  ungroup() %>% 
  select(id_1, group_id) %>% 
  right_join(df, by = "id_1")

# A tibble: 12 x 5
   id_1  group_id id_2     val res_col
   <chr> <chr>    <chr>  <dbl> <chr>  
 1 G     G        NA     1.06  G      
 2 G     G        NA    -0.908 G      
 3 G     G        NA     0.320 G      
 4 Z     G        G     -0.733 G      
 5 Z     G        G      1.10  G      
 6 Z     G        G      1.50  G      
 7 R     R        NA    -2.28  R      
 8 R     R        NA     0.201 R      
 9 R     R        NA     0.641 R      
10 D     G        Z      1.54  G      
11 D     G        Z      0.160 G      
12 D     G        Z     -0.430 G  

1 Ответ

0 голосов
/ 02 июля 2019

Я рассмотрел ваш вопрос так же, как и вы: используя графический подход.Здесь я просто предоставляю альтернативный способ массирования данных.Для последней части я использую data.table - хотя это не является строго необходимым, я просто нахожу это удобным.

library(data.table)
library(igraph)

# convert data.frame to data.table
setDT(df)

# make a copy of id_2 column
df[ , id_22 := id_2]

# where id_2 is NA, set id_22 to id_1
# these vertices correspond to the 'end points' with loop edges in the graph
df[is.na(id_2), id_22 := id_1]

# convert 'edge list' of id_1 and id_22 to a graph
g <- graph_from_data_frame(df[!duplicated(id_1), .(id_1, id_22)])

# get graph components and their named membership id 
mem <- components(g)$membership

# convert to data.table
d <- data.table(id_1 = names(mem), mem = mem)

# add membership id to original data
df[ , mem := d[.SD, on = .(id_1), mem]] 

# create result column 
# for each graph component:
# where id_22 equals id_1 (i.e. the loop edges in the graph), select first id_22 value  
df[ , res := id_22[id_22 == id_1][1], by = mem]

При необходимости удалите вспомогательные столбцы:

df[ , `:=`(id_22 = NULL, mem = NULL)]

df
#     id_1 id_2         val res_col res
#  1:    G <NA>  0.27665785       G   G
#  2:    G <NA>  0.81840992       G   G
#  3:    G <NA>  0.19928880       G   G
#  4:    Z    G -0.09706282       G   G
#  5:    Z    G -0.02744784       G   G
#  6:    Z    G  0.19084119       G   G
#  7:    R <NA>  0.59491323       R   R
#  8:    R <NA> -0.04785416       R   R
#  9:    R <NA>  0.55550640       R   R
# 10:    D    Z -0.76006272       G   G
# 11:    D    Z  0.33305465       G   G
# 12:    D    Z -0.04037541       G   G

plot(g, vertex.size = 20, edge.arrow.size = 0.5)

enter image description here

...