将presence/absence的矩阵变换为顶点连接的Data.frame。 (删除具有 eeuqal 无序值的重复行)

Transforming matrix of presence/absence to Data.frame of vertice connection. (Removing duplicated rows with eeuqal unordered values)

我有一个矩阵 table,其中行表示一个地点,列表示特定岩石的 presence/absence。

>Mat
       A   B   C   D   E   F   G
Aiz    1   0   0   0   0   0   0
Aren   0   1   1   0   1   0   0
Atx    0   0   1   0   1   0   0
Berr   1   1   0   0   0   1   0
Bra    0   0   0   0   0   1   0
Bur    0   1   0   0   1   0   0
Cab    1   0   1   1   1   0   0

如您所见,有些行在某些列中具有相同的元素,例如。行 Aiz 和行 Berr 在列 A 中共享一个存在,这表明在我的示例中它们共享同一块岩石。

我想从这个矩阵构造无向图,其中节点是站点(行名),链接是列元素的份额。

所以,基本上,我需要将此矩阵转换为 data.frame 样式:

>DF
   siteA    siteB weight
1    Aiz    Berr     1
2    Aiz     Cab     1
3   Aren   Atxos     2
4   Aren    Berr     1
5   Aren     Bur     2       
6   Aren     Cab     1
7    Atx     Bur     1
...

其中每一行标识共享相同岩石的两个站点(存在于原始 Mat 的同一列中)并且列 weight,表示两个站点共有的岩石数量.

因此,通过一系列嵌套的 for loopsif 条件,我设法接近 DF 矩阵,尽管我的 DF 有行重复的结果,例如:

> df_links
   siteA    siteB weight
1    Aiz    Berr     1
2    Aiz     Cab     1
3   Aren   Atxos     2
4   Berr     Aiz     1
5  Atxos    Aren     2

您看到的地方,例如:row 1row 4row 3row 5 相同)的网站栏共享相同的元素。由于这是针对无向图的,因此 Aiz-Berr 或 Ber-Aiz 的含义相同,因此我只需要其中一行。

Q 1: 所以,我尝试用 tidyverse 解决重复问题,但似乎没有任何效果。充其量我只会摆脱重复行中的一个而不是全部。所以,我的问题是,有没有办法可以做到这一点?只保留具有相同元素的行之一 [i,j],而不考虑顺序?

Q 2:这个可能比较繁琐,所以排在第二位。即使我的代码有效(直到上面 Q1 中指出的问题),它也不是最漂亮的。是 data.frames 到 data.frames 和 for loops 的矩阵序列,并且包含条件。 是否有更整洁的版本可以从原始 Mat 到所需的 DF?我不太熟悉 sapply 和整个家庭,所以我使用了循环。有没有更快更好看的解决方案?

要删除 2 个可互换列的重复条目,请先重新排序,然后删除数据框的重复行。

df_links <- transform(df_links, siteA = pmin(siteA, siteB), 
                  siteB = pmax(siteA, siteB))
unique(df_links)
#  siteA siteB weight
#1   Aiz  Berr      1
#2   Aiz   Cab      1
#3  Aren Atxos      2

为了避免在无向图中删除重复的顶点对,您可以使用 dist 函数,这在处理向量对之间的相似性时非常有用。当您将相似性(或权重)定义为站点(或向量)之间常见岩石的数量时,您需要传递一个自定义函数,这可以使用包 proxy 中的 dist 来完成。

#The similarity is the number of matching '1'
similarityMatrix <- as.matrix(proxy::dist(Mat, method = function(x,y){
  length(which(x+y==2))
}))
similarityMatrix
#     Aiz Aren Atx Berr Bra Bur Cab
#Aiz    0    0   0    1   0   0   1
#Aren   0    0   2    1   0   2   2
#Atx    0    2   0    0   0   1   2
#Berr   1    1   0    0   1   1   1
#Bra    0    0   0    1   0   0   0
#Bur    0    2   1    1   0   0   1
#Cab    1    2   2    1   0   1   0

在那里,所有成对的网站之间都有一个相似度矩阵。由于您要构建的图形是无向的,因此您需要从该矩阵中每对 select 一次。

#Unique pairwise combinations of different vectors
combinations <- t(combn(colnames(similarityMatrix), 2))
pairwiseSites <- data.frame(combinations, similarityMatrix[combinations])

colnames(pairwiseSites) <- c("siteA", "siteB", "weight")

pairwiseSites
#   siteA siteB weight
#1    Aiz  Aren      0
#2    Aiz   Atx      0
#3    Aiz  Berr      1
#4    Aiz   Bra      0
#5    Aiz   Bur      0
#6    Aiz   Cab      1
#7   Aren   Atx      2
#8   Aren  Berr      1
#9   Aren   Bra      0
#10  Aren   Bur      2
#11  Aren   Cab      2
#12   Atx  Berr      0
#13   Atx   Bra      0
#14   Atx   Bur      1
#15   Atx   Cab      2
#16  Berr   Bra      1
#17  Berr   Bur      1
#18  Berr   Cab      1
#19   Bra   Bur      0
#20   Bra   Cab      0
#21   Bur   Cab      1

原始数据

Mat <- read.table(header=TRUE, text="
A   B   C   D   E   F   G
Aiz    1   0   0   0   0   0   0
Aren   0   1   1   0   1   0   0
Atx    0   0   1   0   1   0   0
Berr   1   1   0   0   0   1   0
Bra    0   0   0   0   0   1   0
Bur    0   1   0   0   1   0   0
Cab    1   0   1   1   1   0   0")

df_links <- read.table(header=TRUE, text="
siteA    siteB weight
1    Aiz    Berr     1
2    Aiz     Cab     1
3   Aren   Atxos     2
4   Berr     Aiz     1
5  Atxos    Aren     2")

使用 purrr 包可以解决问题。

# reproduce input
mat <- matrix(
  data = c(1,0,0,0,0,0,0,
           0,1,1,0,1,0,0,
           0,0,1,0,1,0,0,
           1,1,0,0,0,1,0,
           0,0,0,0,0,1,0,
           0,1,0,0,1,0,0,
           1,0,1,1,1,0,0), nrow = 7, ncol = 7)
colnames(mat) <- LETTERS[1:7]
rownames(mat) <- c("Aiz", "Aren", "Atx", "Berr", "Bra", "Bur", "Cab")

# convert to dataframe
df <- mat %>% 
  dplyr::as_tibble() %>% 
  dplyr::bind_cols(
    tibble::tibble(Names = rownames(mat)))

# calculate the connections
purrr::map_df(df$Names, function(x){
  output <-purrr::map_df(df$Names, function(y){
    if(x >= y) return(tibble::tibble()) # avoid double counting
    tibble::tibble(
      siteA = x,
      siteB = y,
      weight = sum(as.integer(df[df$Names==x,1:7]) & as.integer(df[df$Names==y,1:7])))
    })
  })

祝你好运