Tìm hàng xóm ngay lập tức theo nhóm bằng bảng dữ liệu hoặc igraph


14

Tôi có một dữ liệu .

groups <- data.table(group = c("A", "B", "C", "D", "E", "F", "G"), 
                     code_1 = c(2,2,2,7,8,NA,5),
                     code_2 = c(NA,3,NA,3,NA,NA,2),
                     code_3 = c(4,1,1,4,4,1,8))

group code_1 code_2 code_3
  A      2     NA      4
  B      2      3      1
  C      2     NA      1
  D      7      3      4
  E      8     NA      4
  F     NA     NA      1
  G      5      2      8

Những gì tôi muốn đạt được, là cho mỗi nhóm để tìm những người hàng xóm ngay lập tức dựa trên các mã có sẵn. Ví dụ: Nhóm A có các nhóm lân cận B, C ngay lập tức do mã_1 (mã_1 bằng 2 trong tất cả các nhóm) và có các nhóm lân cận ngay lập tức D, E do code_3 (code_3 bằng 4 trong tất cả các nhóm đó).

Những gì tôi đã thử là cho mỗi mã, đặt lại cột (nhóm) đầu tiên dựa trên các kết quả khớp như sau:

groups$code_1_match = list()
for (row in 1:nrow(groups)){

  set(groups, i=row, j="code_1_match", list(groups$group[groups$code_1[row] == groups$code_1]))
}

  group code_1 code_2 code_3          code_1_match
    A      2     NA      4              A,B,C,NA
    B      2      3      1              A,B,C,NA
    C      2     NA      1              A,B,C,NA
    D      7      3      4                  D,NA
    E      8     NA      4                  E,NA
    F     NA     NA      1 NA,NA,NA,NA,NA,NA,...
    G      5      2      8                  NA,G

"Loại" này hoạt động nhưng tôi sẽ cho rằng có một loại bảng dữ liệu hơn để làm điều này. Tôi đã thử

groups[, code_1_match_2 := list(group[code_1 == groups$code_1])]

Nhưng điều này không làm việc.

Tôi có thiếu một số thủ thuật bảng dữ liệu rõ ràng để đối phó với nó?

Kết quả trường hợp lý tưởng của tôi sẽ giống như thế này (hiện đang yêu cầu sử dụng phương pháp của tôi cho cả 3 cột và sau đó nối các kết quả):

group code_1 code_2 code_3    Immediate neighbors
  A      2     NA      4         B,C,D,E
  B      2      3      1         A,C,D,F
  C      2     NA      1         A,B,F
  D      7      3      4           B,A
  E      8     NA      4           A,D
  F     NA     NA      1           B,C
  G      5      2      8           

Có thể được thực hiện bằng cách sử dụng igraph.
zx8754

1
Mục đích của tôi là cung cấp kết quả cho igraph để tạo ma trận kề. Nếu tôi thiếu một số chức năng sẽ làm điều đó xin vui lòng chỉ cho tôi, nó sẽ thực sự hữu ích!
Người dùng 2321

1
@ zx8754 vui lòng xem xét đăng một giải pháp liên quan igraph, nó có thể thực sự thú vị.
tmfmnk

@tmfmnk được đăng, mặc dù nghĩ rằng có thể có một cách tốt hơn để làm điều đó.
zx8754

Câu trả lời:


10

Sử dụng igraph , nhận hàng xóm độ 2, thả các nút số, dán các nút còn lại.

library(data.table)
library(igraph)

# reshape wide-to-long
x <- melt(groups, id.vars = "group")[!is.na(value)]

# convert to graph
g <- graph_from_data_frame(x[, .(from = group, to = paste0(variable, "_", value))])

# get 2nd degree neighbours
x1 <- ego(g, 2, nodes = groups$group)

# prettify the result
groups$res <- sapply(seq_along(x1), function(i) toString(intersect(names(x1[[ i ]]),
                                                                   groups$group[ -i ])))

#    group code_1 code_2 code_3        res
# 1:     A      2     NA      4 B, C, D, E
# 2:     B      2      3      1 A, C, D, F
# 3:     C      2     NA      1    A, B, F
# 4:     D      7      3      4    B, A, E
# 5:     E      8     NA      4       A, D
# 6:     F     NA     NA      1       B, C
# 7:     G      5      2      8           

Thêm thông tin

Đây là cách dữ liệu của chúng tôi trông như thế nào trước khi chuyển đổi sang đối tượng igraph. Chúng tôi muốn đảm bảo code1 với giá trị 2 khác với code2 với giá trị 2, v.v.

x[, .(from = group, to = paste0(variable, "_", value))]
#     from       to
#  1:    A code_1_2
#  2:    B code_1_2
#  3:    C code_1_2
#  4:    D code_1_7
#  5:    E code_1_8
#  6:    G code_1_5
#  7:    B code_2_3
#  8:    D code_2_3
#  9:    G code_2_2
# 10:    A code_3_4
# 11:    B code_3_1
# 12:    C code_3_1
# 13:    D code_3_4
# 14:    E code_3_4
# 15:    F code_3_1
# 16:    G code_3_8

Đây là cách mạng của chúng tôi trông như thế nào: nhập mô tả hình ảnh ở đây

Lưu ý rằng A..Gcác nút luôn được kết nối thông qua code_x_y. Vì vậy, chúng ta cần phải có được mức độ 2,ego(..., order = 2) cung cấp cho chúng tôi hàng xóm lên đến bao gồm hàng xóm mức độ 2 và trả về một đối tượng danh sách.

Để có được tên:

lapply(x1, names)
# [[1]]
# [1] "A"        "code_1_2" "code_3_4" "B"        "C"        "D"        "E"       
# 
# [[2]]
# [1] "B"        "code_1_2" "code_2_3" "code_3_1" "A"        "C"        "D"        "F"       
# 
# [[3]]
# [1] "C"        "code_1_2" "code_3_1" "A"        "B"        "F"       
# 
# [[4]]
# [1] "D"        "code_1_7" "code_2_3" "code_3_4" "B"        "A"        "E"       
# 
# [[5]]
# [1] "E"        "code_1_8" "code_3_4" "A"        "D"       
# 
# [[6]]
# [1] "F"        "code_3_1" "B"        "C"       
# 
# [[7]]
# [1] "G"        "code_1_5" "code_2_2" "code_3_8"

Để làm đẹp kết quả, chúng ta cần loại bỏ code_x_ycác nút và nút gốc (nút thứ nhất)

sapply(seq_along(x1), function(i) toString(intersect(names(x1[[ i ]]), groups$group[ -i ])))
#[1] "B, C, D, E" "A, C, D, F" "A, B, F"    "B, A, E"    "A, D"       "B, C"       ""   

Không phải là một chuyên gia trong igraph, điều này trông thực sự kỳ lạ. Nó dường như hoạt động :) Nếu tôi hiểu chính xác, đầu tiên nó tạo ra một biểu đồ trong đó các mã là hàng xóm trực tiếp và sau đó nó tìm thấy hàng xóm ngay lập tức thực sự là hàng xóm thứ hai từ biểu đồ đó?
Người dùng 2321

@ User2321 thêm thông tin, hy vọng nó rõ ràng hơn.
zx8754

1
@ User2321 btw không phải là chuyên gia, chỉ muốn giải quyết vấn đề igraph đôi khi. Vẫn đang chờ đợi một số chuyên gia để đề xuất một cách tốt hơn.
zx8754

1
Vâng, tôi đang xem xét cung cấp một tiền thưởng chỉ trong trường hợp. Nhưng chúng ta hãy xem trong 2 ngày :)
Người dùng 2321

7

Có lẽ có một số cách thực tế hơn để đạt được điều này nhưng bạn có thể làm một cái gì đó như thế này, bằng cách sử dụng tan chảy và tham gia:

mgrp <- melt(groups, id.vars = "group")[!is.na(value)]
setkey(mgrp, variable, value)
for (i in seq_along(groups$group)) {
  let = groups$group[i]
  set(
    groups, 
    i = i, 
    j = "inei", 
    value = list(mgrp[mgrp[group == let], setdiff(unique(group), let)])
  )
}

groups
#    group code_1 code_2 code_3    inei
# 1:     A      2     NA      4 B,C,D,E
# 2:     B      2      3      1 A,C,D,F
# 3:     C      2     NA      1   A,B,F
# 4:     D      7      3      4   B,A,E
# 5:     E      8     NA      4     A,D
# 6:     F     NA     NA      1     B,C
# 7:     G      5      2      8       

5

Điều này được lấy cảm hứng từ sự tan chảy của @ sindri_baldur. Giải pháp này:

  1. Làm tan chảy các nhóm
  2. Thực hiện một cartesian tự tham gia.
  3. Dán tất cả các nhóm phù hợp.
  4. Tham gia trở lại DT ban đầu
library(data.table)
#> Warning: package 'data.table' was built under R version 3.6.2
groups <- data.table(group = c("A", "B", "C", "D", "E", "F", "G"), code_1 = c(2,2,2,7,8,NA,5), code_2 = c(NA,3,NA,3,NA,NA,2), code_3=c(4,1,1,4,4,1,8))

molten_grps = melt(groups, measure.vars = patterns("code"), na.rm = TRUE)

inei_dt = molten_grps[molten_grps,
            on = .(variable, value),
            allow.cartesian = TRUE
            ][,
              .(inei = paste0(setdiff(i.group, .BY[[1L]]), collapse = ", ")),
              by = group]

groups[inei_dt, on = .(group), inei := inei]

groups
#>     group code_1 code_2 code_3       inei
#>    <char>  <num>  <num>  <num>     <char>
#> 1:      A      2     NA      4 B, C, D, E
#> 2:      B      2      3      1 A, C, D, F
#> 3:      C      2     NA      1    A, B, F
#> 4:      D      7      3      4    B, A, E
#> 5:      E      8     NA      4       A, D
#> 6:      F     NA     NA      1       B, C
#> 7:      G      5      2      8

5

Như được đề cập bởi zx8754, sử dụng data.table::meltvới combnvà sau đóigraph::as_adjacency_matrix

library(data.table)
df <- melt(groups, id.vars="group", na.rm=TRUE)[,
    if (.N > 1L) transpose(combn(group, 2L, simplify=FALSE)), value][, (1) := NULL]

library(igraph)
as_adjacency_matrix(graph_from_data_frame(df, FALSE))

đầu ra:

7 x 7 sparse Matrix of class "dgCMatrix"
  A B C E D G F
A . 1 1 1 1 1 .
B 1 . 2 . 1 1 1
C 1 2 . . . 1 1
E 1 . . . 1 1 .
D 1 1 . 1 . . .
G 1 1 1 1 . . .
F . 1 1 . . . .

hoặc không sử dụng igraph

x <- df[, unique(c(V1, V2))]
df <- rbindlist(list(df, data.table(x, x)))
tab <- table(df)   #or xtabs(~ V1 + V2, data=df)
ans <- t(tab) + tab
diag(ans) <- 0L
ans

đầu ra:

   V1
V2  A B C D E F G
  A 0 1 1 1 1 0 1
  B 1 0 2 1 0 1 1
  C 1 2 0 0 0 1 1
  D 1 1 0 0 1 0 0
  E 1 0 0 1 0 0 1
  F 0 1 1 0 0 0 0
  G 1 1 1 0 1 0 0

1
Có thể xtabstạo ra một đầu ra tương tự như igraphbước?
Cole

Đây là một câu trả lời thực sự hữu ích và (trong mắt tôi), cảm ơn bạn!
Người dùng 2321

@Cole, yeah có thể sử dụng tablehoặcxtabs
chin gió12
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.