【问题标题】:R Interclass distance matrixR类间距离矩阵
【发布时间】:2016-12-29 15:14:44
【问题描述】:

这个问题是how to extract intragroup and intergroup distances from a distance matrix? in R 的后续问题。在那个问题中,他们首先计算了所有点的距离矩阵,然后简单地提取了类间距离矩阵。我有一种情况,我想绕过初始计算并直接跳到提取,即我想直接计算类间距离矩阵。从链接的示例中提取,经过调整,假设我在一个名为 df 的数据框中有一些数据:

values<-c(0.002,0.3,0.4,0.005,0.6,0.2,0.001,0.002,0.3,0.01)
class<-c("A","A","A","B","B","B","B","A","B","A")
df<-data.frame(values, class)

我想要的是一个距离矩阵:

    1    2    3    8   10
4 .003 .295 .395 .003 .005
5 .598 .300 .200 .598 .590
6 .198 .100 .200 .198 .190
7 .001 .299 .399 .001 .009
9 .298 .000 .100 .298 .290

R 中是否已经存在一种优雅而快速的方法来做到这一点?

编辑在收到上述一维案例的好解决方案后,我想到了一个额外的问题:更高维的案例呢,说如果df看起来像这样:

values1<-c(0.002,0.3,0.4,0.005,0.6,0.2,0.001,0.002,0.3,0.01)
values2<-c(0.001,0.1,0.1,0.001,0.1,0.1,0.001,0.001,0.1,0.01)
class<-c("A","A","A","B","B","B","B","A","B","A")
df<-data.frame(values1, values2, class)

我有兴趣再次获得 B 类中的点与 A 类中的点之间的欧几里得距离矩阵。

【问题讨论】:

    标签: r distance-matrix


    【解决方案1】:

    对于一般n维欧几里得距离,我们可以利用方程(不是R,而是代数):

    square_dist(b,a) = sum_i(b[i]*b[i]) + sum_i(a[i]*a[i]) - 2*inner_prod(b,a)
    

    总和超过向量ab 对于i=[1,n] 的维度。这里,ab 是来自 AB 的一对。这里的关键是这个方程可以写成AB中所有对的矩阵方程。

    在代码中:

    ## First split the data with respect to the class
    n <- 2   ## the number of dimensions, for this example is 2
    tmp <- split(df[,1:n], df$class)
    
    d <- sqrt(matrix(rowSums(expand.grid(rowSums(tmp$B*tmp$B),rowSums(tmp$A*tmp$A))),
                     nrow=nrow(tmp$B)) - 
              2. * as.matrix(tmp$B) %*% t(as.matrix(tmp$A)))
    

    注意事项:

    1. 内部rowSums 分别计算B 中的每个bA 中的a sum_i(b[i]*b[i])a
    2. expand.grid 然后生成 BA 之间的所有对。
    3. 外部rowSums 计算所有这些对的sum_i(b[i]*b[i]) + sum_i(a[i]*a[i])
    4. 然后将此结果重新整形为matrix。请注意,此矩阵的行数是您要求的 B 类的点数。
    5. 然后减去所有对的内积的两倍。这个内积可以写成矩阵乘法tmp$B %*% t(tmp$A),为了清楚起见,我省略了对矩阵的强制。
    6. 最后,取平方根。

    将此代码用于您的数据:

    print(d)
    ##          1         2         3         8         10
    ##4 0.0030000 0.3111688 0.4072174 0.0030000 0.01029563
    ##5 0.6061394 0.3000000 0.2000000 0.6061394 0.59682493
    ##6 0.2213707 0.1000000 0.2000000 0.2213707 0.21023796
    ##7 0.0010000 0.3149635 0.4110985 0.0010000 0.01272792
    ##9 0.3140143 0.0000000 0.1000000 0.3140143 0.30364453
    

    请注意,此代码适用于任何n &gt; 1。我们可以通过将n 设置为1 而不执行内部rowSums 来恢复您之前的一维结果(因为现在tmp$Atmp$B 中只有一列):

    n <- 1   ## the number of dimensions, set this now to 1
    tmp <- split(df[,1:n], df$class)
    
    d <- sqrt(matrix(rowSums(expand.grid(tmp$B*tmp$B,tmp$A*tmp$A)),
                     nrow=length(tmp$B)) - 
              2. * as.matrix(tmp$B) %*% t(as.matrix(tmp$A)))
    print(d)
    ##      [,1]  [,2]  [,3]  [,4]  [,5]
    ##[1,] 0.003 0.295 0.395 0.003 0.005
    ##[2,] 0.598 0.300 0.200 0.598 0.590
    ##[3,] 0.198 0.100 0.200 0.198 0.190
    ##[4,] 0.001 0.299 0.399 0.001 0.009
    ##[5,] 0.298 0.000 0.100 0.298 0.290
    

    【讨论】:

    • 感谢您非常详细的回复!这非常有效。
    • 这行得通,但我发现了一个问题:每个类中有 1000 个点的更大数据集,我在d 中得到了 NaN。我正在使用a &lt;- which(is.nan(d), arr.ind=T) 检查tmp$Atmp$B 的条目,然后执行,例如tmp$A[a[1],]tmp$A[a[nrow(a) + 1],],我发现当两个类的向量相同时,NaN 似乎出现了。这是一个精密的东西吗?抱歉,由于我的数据集太大,我无法提供具体示例。
    • 有可能。计算中出现 NaN 的唯一原因是平方距离是否为负,这可能是由于精度。要检查,请删除平方根并查看生成的矩阵是否有负数。这些应该很小。如果这是问题所在,那么在取平方根之前只需将阈值设为零。如果这不是问题,请告诉我。
    【解决方案2】:

    这是一种尝试,通过生成每个组合然后简单地从每个值中获取差异:

    abs(matrix(Reduce(`-`, expand.grid(split(df$values, df$class))), nrow=5, byrow=TRUE))
    #      [,1]  [,2]  [,3]  [,4]  [,5]
    #[1,] 0.003 0.295 0.395 0.003 0.005
    #[2,] 0.598 0.300 0.200 0.598 0.590
    #[3,] 0.198 0.100 0.200 0.198 0.190
    #[4,] 0.001 0.299 0.399 0.001 0.009
    #[5,] 0.298 0.000 0.100 0.298 0.290
    

    【讨论】:

    • 这太完美了!一个简单的问题:Reduce() 可以采用哪些可能的函数类型?例如,是否可以采用 n 维欧几里得距离,而不仅仅是 1-D 距离(这里是通过采用绝对差的绝对值来破解的),在上面的示例中只有一列值,但是如果我有三个不同的值列,比如value1value2value3
    • @itf - 不幸的是,我不认为这真的可以扩展到 >1D 距离。我必须多加考虑。
    • 没问题,我没有发布关于高维距离的问题是我的错。我会相应地编辑我的问题,看看是否有更多的解决方案出现。感谢您的一维解决方案!