是否有 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
这个矩阵应该只有一条唯一的边,因为无向网络的边 A-B 和 B-A 是相同的。
对于定向网络,我可以通过以下方式获得唯一边的数量:
nrow(unique(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"
那么这是如何工作的?
我们正在对数据框的每一行应用一个函数,因此我们可以一次获取每一行。
取df的第二行,
df[2,]
x y
1 B A
然后我们将 (strsplit
) 和 unlist
拆分为每个字母的向量,(我们使用 as.matrix
来隔离元素)
unlist(strsplit(as.matrix(df[2,]), " "))
[1] "B" "A"
使用排序功能按字母顺序排列,然后粘贴回去,
paste(sort(unlist(strsplit(as.matrix(df[2,]), " "))), collapse = " ")
[1] "A B"
然后 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"))
可以通过 mutate()
在新列中使用 group_by()
然后 sort()
值组合和 paste()
它们。 unique()
如果你有 "true" 重复(A-B,A-B 将进入一组)。
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
我想计算无向网络中唯一边的数量,例如 net
x y
1 A B
2 B A
3 A B
这个矩阵应该只有一条唯一的边,因为无向网络的边 A-B 和 B-A 是相同的。
对于定向网络,我可以通过以下方式获得唯一边的数量:
nrow(unique(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"
那么这是如何工作的?
我们正在对数据框的每一行应用一个函数,因此我们可以一次获取每一行。 取df的第二行,
df[2,] x y 1 B A
然后我们将 (
strsplit
) 和unlist
拆分为每个字母的向量,(我们使用as.matrix
来隔离元素)unlist(strsplit(as.matrix(df[2,]), " ")) [1] "B" "A"
使用排序功能按字母顺序排列,然后粘贴回去,
paste(sort(unlist(strsplit(as.matrix(df[2,]), " "))), collapse = " ") [1] "A B"
然后 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"))
可以通过 mutate()
在新列中使用 group_by()
然后 sort()
值组合和 paste()
它们。 unique()
如果你有 "true" 重复(A-B,A-B 将进入一组)。
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