繁体   English   中英

是否有 R 函数来获取无向(非有向)网络中的唯一边?

[英]Is there an R function to get the unique edges in an undirected (not directed) network?

我想计算无向网络中唯一边的数量,例如 net

   x  y
1  A  B
2  B  A
3  A  B

这个矩阵应该只有一个唯一的边,因为边 AB 和 BA 对于无向网络是相同的。

对于有向网络,我可以通过以下方式获得唯一边的数量:

nrow(唯一的(net[,c("x","y"]))

但这不适用于无向网络。

尝试这个,

df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B"))
unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " ")))
[1] "A B"

那么这是如何工作的呢?

  1. 我们正在对数据框的每一行应用一个函数,因此我们可以一次获取每一行。 取df的第二行,

     df[2,] xy 1 BA
  2. 然后我们拆分( strsplit )这个,并unlist每个字母的向量,(我们使用as.matrix来隔离元素)

     unlist(strsplit(as.matrix(df[2,]), " ")) [1] "B" "A"
  3. 使用 sort 功能按字母顺序排列,然后将它们粘贴在一起,

     paste(sort(unlist(strsplit(as.matrix(df[2,]), " "))), collapse = " ") [1] "AB"

然后apply函数对所有行执行此操作,因为我们将索引设置为 1,然后使用unique函数来识别唯一的边。

延期

这可以扩展到 n 个变量,例如 n=3,

df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B"),  z = c("C", "D", "D"))
unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " ")))
[1] "A B C" "A B D"

如果需要更多字母,只需将两个字母组合如下,

df <- data.frame(x=c("A", "BC", "A"), y = c("B", "A", "BC"))
df
   x  y
1  A  B
2 BC  A
3  A BC
unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " ")))
[1] "A B"  "A BC"

旧版本

使用tidyverse包,创建一个名为rev的函数,它可以对我们的边进行排序,然后使用mutate创建一个结合 x 和 y 列的新列,这样它就可以很好地与rev函数配合使用,然后通过函数并找到唯一的对。

library(tidyverse)
rev <- function(x){
  unname(sapply(x, function(x) {
    paste(sort(trimws(strsplit(x[1], ',')[[1]])), collapse=',')} ))
}
df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B"))
rows <- df %>% 
  mutate(both = c(paste(x, y, sep = ", ")))

unique(rev(rows$both))

鉴于您正在使用网络, igraph解决方案:

library(igraph)

as_data_frame(simplify(graph_from_data_frame(dat, directed=FALSE)))

然后使用nrow


说明

dat %>% 
  graph_from_data_frame(., directed=FALSE) %>% # convert to undirected graph
  simplify %>%                                 # remove loops / multiple edges
  as_data_frame                                # return remaining edges

这是一个没有igraph干预的解决方案,全部在一个管道内:

df = tibble(x=c("A", "B", "A"), y = c("B", "A", "B"))

可以使用group_by()然后sort()值的组合并通过mutate()将它们paste()到新列中。 如果您有“真正的”重复项(AB,AB 将进入一组),则使用unique() )。

df %>%
  group_by(x, y) %>%
  mutate(edge_id = paste(sort(unique(c(x,y))), collapse=" ")) 

当您在新列中正确排序边缘名称时,计算唯一值或从数据框中过滤重复项非常简单。
如果你有额外的边变量,只需将它们添加到分组中。

如果您不使用{igraph}或者只是想知道如何在没有任何依赖的情况下干净地使用它...

这是你的数据...

your_edge_list <- data.frame(x = c("A", "B", "A"),
                             y = c("B", "A", "B"),
                             stringsAsFactors = FALSE)
your_edge_list
#>   x y
#> 1 A B
#> 2 B A
#> 3 A B

这是一个分步分解...

`%>%` <- magrittr::`%>%`

your_edge_list %>% 
  apply(1L, sort) %>%              # sort dyads
  t() %>%                          # transpose resulting matrix to get the original shape back
  unique() %>%                     # get the unique rows
  as.data.frame() %>%              # back to data frame
  setNames(names(your_edge_list))  # reset column names
#>   x y
#> 1 A B

如果我们放下管道,它的核心看起来像这样......

unique(t(apply(your_edge_list, 1, sort)))
#>      [,1] [,2]
#> [1,] "A"  "B"

我们可以将它包装在一个函数中:1) 处理有向和无向,2) 处理数据帧和(更常见的)矩阵,以及 3)可以丢弃循环......

simplify_edgelist <- function(el, directed = TRUE, drop_loops = TRUE) {
  stopifnot(ncol(el) == 2)

  if (drop_loops) {
    el <- el[el[, 1] != el[, 2], ]
  }

  if (directed) {
    out <- unique(el)
  } else {
    out <- unique(t(apply(el, 1, sort)))
  }

  colnames(out) <- colnames(el)

  if (is.data.frame(el)) {
    as.data.frame(out, stringsAsFactors = FALSE)
  } else {
    out
  }
}

el2 <- rbind(your_edge_list, 
             data.frame(x = c("C", "C"), y = c("C", "A"), stringsAsFactors = FALSE))
el2
#>   x y
#> 1 A B
#> 2 B A
#> 3 A B
#> 4 C C
#> 5 C A

simplify_edgelist(el2, directed = FALSE)
#>   x y
#> 1 A B
#> 5 A C

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM