在R中将距离矩阵转换为成对距离列表

8

如果我的输入文件是一个距离矩阵,是否有任何R包可以获得成对距离列表?例如,如果我的输入数据框长这样:

        A1      B1      C1      D1
 A1     0      0.85    0.45    0.96 
 B1            0       0.85    0.56
 C1                    0       0.45
 D1                            0

我希望得到的输出为:

我想要输出:

A1  B1  0.85
A1  C1  0.45
A1  D1  0.96
B1  C1  0.85
B1  D1  0.56
C1  D1  0.45

我找到了一个使用“reshape”包执行相反函数的问题,但无法调整它以获得我想要的结果。


1
请发布 dput(your-distance-object) 的输出,这样我们就不必猜测您是否真正处理的是 data.framematrixtable、实际距离矩阵或其他完全不同的东西。这肯定会影响到目前为止所提供答案的适用性。我之所以这样问,是因为您的标题说“距离矩阵”(通常使用 dist 函数创建),但您的问题描述说您正在处理一个 data.frame。这些是非常不同的。 - A5C1D2H2I1M1N2O1R2T1
我也对此持怀疑态度...使用dist生成的距离矩阵默认打印下三角,而不是上三角。你的空白单元格是NA还是仅仅被隐藏了(就像dist对象的print方法一样)? - jbaums
5个回答

14
另外还有几种选择:

  1. Generate some data

    D <- dist(cbind(runif(4), runif(4)), diag=TRUE, upper=TRUE) # generate dummy data
    m <- as.matrix(D) # coerce dist object to a matrix
    dimnames(m) <- dimnames(m) <- list(LETTERS[1:4], LETTERS[1:4]) 
    
  2. Assuming you just want the distances for pairs defined by the upper triangle of the distance matrix, you can do:

    xy <- t(combn(colnames(m), 2))
    data.frame(xy, dist=m[xy])
    
    #  X1 X2      dist
    # 1 A  B 0.3157942
    # 2 A  C 0.5022090
    # 3 A  D 0.3139995
    # 4 B  C 0.1865181
    # 5 B  D 0.6297772
    # 6 C  D 0.8162084
    
  3. Alternatively, if you want distances for all pairs (in both directions):

    data.frame(col=colnames(m)[col(m)], row=rownames(m)[row(m)], dist=c(m))
    
    #    col row      dist
    # 1    A   A 0.0000000
    # 2    A   B 0.3157942
    # 3    A   C 0.5022090
    # 4    A   D 0.3139995
    # 5    B   A 0.3157942
    # 6    B   B 0.0000000
    # 7    B   C 0.1865181
    # 8    B   D 0.6297772
    # 9    C   A 0.5022090
    # 10   C   B 0.1865181
    # 11   C   C 0.0000000
    # 12   C   D 0.8162084
    # 13   D   A 0.3139995
    # 14   D   B 0.6297772
    # 15   D   C 0.8162084
    # 16   D   D 0.0000000
    

    or the following, which excludes any NA distances, but doesn't keep the column/row names (though this would be easy to rectify since we have the column/row indices):

    data.frame(which(!is.na(m), arr.ind=TRUE, useNames=FALSE), dist=c(m))
    

我收到了以下错误信息。有任何想法是为什么?m[xy]中的错误:下标超出范围。 - Anurag Mishra
@AnuragMishra 当您运行我的代码时?还是当您将其应用于您的数据时? - jbaums
当我将其应用于我的数据时,数据是一个数据框。 - Anurag Mishra
@AnuragMishra 请编辑您的问题并添加 dput(d) 的输出,其中 d 是您的数据框。如果 d 太大无法以此方式包含,则提供其小部分以供我们使用。 - jbaums
我正在使用数据框中的两列作为X和Y坐标来计算距离。dput()给出了以下结果: Size = 121L, Diag = TRUE, Upper = TRUE, method = "euclidean", call = dist(x = cbind(x$da1, x$da2), diag = TRUE, upper = TRUE), class = "dist") x$da1和x$da2是数据框'x'中的两列。这符合您的要求吗? - Anurag Mishra

7
如果您有一个data.frame,可以这样做:
df <- structure(list(A1 = c(0, 0, 0, 0), B1 = c(0.85, 0, 0, 0), C1 = c(0.45, 
0.85, 0, 0), D1 = c(0.96, 0.56, 0.45, 0)), .Names = c("A1", "B1", 
"C1", "D1"), row.names = c(NA, -4L), class = "data.frame")

data.frame( t(combn(names(df),2)), dist=t(df)[lower.tri(df)] )
  X1 X2 dist
1 A1 B1 0.85
2 A1 C1 0.45
3 A1 D1 0.96
4 B1 C1 0.85
5 B1 D1 0.56
6 C1 D1 0.45

如果你的数据是一个带有行和列名的矩阵,那么另一种方法是直接使用 reshape2

mat <- structure(c(0, 0, 0, 0, 0.85, 0, 0, 0, 0.45, 0.85, 0, 0, 0.96, 
0.56, 0.45, 0), .Dim = c(4L, 4L), .Dimnames = list(c("A1", "B1", 
"C1", "D1"), c("A1", "B1", "C1", "D1")))

library(reshape2)
subset(melt(mat), value!=0)

   Var1 Var2 value
5    A1   B1  0.85
9    A1   C1  0.45
10   B1   C1  0.85
13   A1   D1  0.96
14   B1   D1  0.56
15   C1   D1  0.45

3

我假设你有一个按以下方式定义的列联表或矩阵:

mat = matrix(c(0, 0.85, 0.45, 0.96, NA, 0, 0.85, 0.56, NA, NA, 0, 0.45, NA,NA,NA,0), ncol=4)
cont = as.table(t(mat))

#     A    B    C    D
#A 0.00 0.85 0.45 0.96
#B      0.00 0.85 0.56
#C           0.00 0.45
#D                0.00

然后,您只需要进行数据框转换,并删除NA / 0:

df = as.data.frame(cont)
df = df[complete.cases(df),]
df[df[,3]!=0,]

#   Var1 Var2 Freq
#5     A    B 0.85
#9     A    C 0.45
#10    B    C 0.85
#13    A    D 0.96
#14    B    D 0.56
#15    C    D 0.45

1

Tidymodels答案

这正是broom包擅长的类型。它是一个tidymodels包。

借用jbaums答案中的虚拟数据。

D <- dist(cbind(runif(4), runif(4))) # generate dummy data

这是一个单一的函数调用。
library(broom)
tidy(D)

这句话的意思是“返回什么”。
 A tibble: 6 x 3
  item1 item2 distance
  <fct> <fct>    <dbl>
1 1     2        0.702
2 1     3        0.270
3 1     4        0.292
4 2     3        0.960
5 2     4        0.660
6 3     4        0.510

注意,它同样适用于不同的diagupper值。
tidy(dist(cbind(runif(4), runif(4)), diag=TRUE, upper=TRUE))
tidy(dist(cbind(runif(4), runif(4)), diag=FALSE, upper=TRUE))
tidy(dist(cbind(runif(4), runif(4)), diag=TRUE, upper=FALSE))

0

这里是一个使用spaa-package的例子。

exampleInput <- structure(list(A1 = c(0, 0, 0, 0), B1 = c(0.85, 0, 0, 0), 
C1 = c(0.45, 0.85, 0, 0), D1 = c(0.96, 0.56, 0.45, 0)), 
.Names = c("A1", "B1", "C1", "D1"), row.names = c(NA, -4L), class = "data.frame")

library(spaa)
pairlist <- dist2list(as.dist(t(exampleInput)))
pairlist[as.numeric(pairlist$col) > as.numeric(pairlist$row),]

输出:

   col row value
2   B1  A1  0.85
3   C1  A1  0.45
4   D1  A1  0.96
7   C1  B1  0.85
8   D1  B1  0.56
12  D1  C1  0.45

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接