【问题标题】:R: Euclidian distances between objects in a groupR:组中对象之间的欧几里得距离
【发布时间】:2014-08-05 09:25:06
【问题描述】:

我想根据两个标识符创建一个具有相似性的矩阵,请考虑以下矩阵:

x1  <- c(2,2,2,3,1,2,4,6,4)
y1  <- c(5,4,3,3,4,2,1,6,3)
x2  <- c(8,2,7,3,1,2,2,2,6)
y2  <- c(1,3,3,3,1,2,4,3,8)
x3  <- c(4,4,1,2,4,6,3,2,9)
y3  <- c(1,2,3,3,1,2,4,6,1)
id1 <- c("a","a","a","a","b","b","b","b","b")
id2 <- c(2002,2002,2003,2003,2002,2002,2003,2003,2003)
dat <- data.frame(x1,y1,x2,y2,x3,y3,id1,id2)

对于由id1id2 标记的组,我想在数据集中的行之间创建欧几里德距离(sqrt((x1a-x1b)^2+(y1a-y1b)^2 + ... + (y3a-y3b)^2))。在最好的情况下,将有一个新变量指示每条线与另一条线之间的距离,具有相同的id1id2。请注意,每个组可以有不同数量的成员,例如 2003 年的 b 组有 3 种情况。 任何建议都会很棒!!!

【问题讨论】:

    标签: r euclidean-distance


    【解决方案1】:

    我认为首先区分要计算其距离的线是个好主意。例如,对于 id1 == b 和 id2 == 2003,您有 3 条线,并且您想要计算 3 个不同的距离(每个可能对之间)。因此,让我们首先为它们中的每一个分配一个唯一的 id。

    f <- function(n) {
        # Returns a vector 
        # 1, 2, 1, 3, ..., 1, n, 2, 3, 2, 4, ..., 2, n, ..., (n-1), n
        m <- matrix(ncol = 2, nrow = n * (n-1) / 2)
        m[, 1] <- rep(1:(n-1), (n-1):1)
        m[, 2] <- unlist(lapply(2:n, function(x) x:n))
        as.numeric(t(m))
    }
    # Alternatively,
    # f <- function(n) {
    #     d <- expand.grid(a = 1:n, b = 1:n)
    #     d <- d[d$a < d$b, ]
    #     unlist(d)
    # }
    # but this is slower
    
    
    
    # Using plyr...
    library(plyr)
    dat <- ddply(dat, .(id1, id2), function(d) {
        d <- d[f(nrow(d)), ]
        d$id3 <- paste0(d$id1, rep(1:(nrow(d) / 2), each = 2))
        d
    })
    
    # ...or using base R
    dat <- do.call(rbind, 
         by(dat, list(dat$id1, dat$id2), function(d) {
         d <- d[f(nrow(d)), ]
         d$id3 <- paste0(d$id1, rep(1:(nrow(d) / 2), each = 2))
         d
    }))
    

    现在每个 (id3, id2) 对只有两行,您可以按如下方式计算差异

    # Using plyr
    result <- ddply(dat, .(id3, id2), function(d) {
        d <- d[paste0(rep(c("x", "y"), 3), 1:3)]
        d$dist <- sqrt(sum((d[1, ] - d[2, ])^2))
        d
    })
    
    # Base R
    result <- do.call(rbind, 
        by(dat[paste0(rep(c("x", "y"), 3), 1:3)],
            list(dat$id3, dat$id2), 
            function(d){
                d$dist <- sqrt(sum((d[1, ] - d[2, ])^2))
                d
            }
    ))
    result[c("id3", "id2")] <- dat[c("id3", "id2")]
    result
    #     x1 y2 x3 y1 x2 y3      dist id3  id2
    # 1    2  1  4  5  8  1  6.480741  a1 2002
    # 2    2  3  4  4  2  2  6.480741  a1 2002
    # 5    1  1  4  4  1  1  3.464102  b1 2002
    # 6    2  2  6  2  2  2  3.464102  b1 2002
    # 3    2  3  1  3  7  3  4.242641  a1 2003
    # 4    3  3  2  3  3  3  4.242641  a1 2003
    # 7    4  4  3  1  2  4  5.916080  b1 2003
    # 8    6  3  2  6  2  6  5.916080  b1 2003
    # 7.1  4  4  3  1  2  4  9.000000  b2 2003
    # 9    4  8  9  3  6  1  9.000000  b2 2003
    # 8.1  6  3  2  6  2  6 11.313708  b3 2003
    # 9.1  4  8  9  3  6  1 11.313708  b3 2003
    

    【讨论】:

    • 感谢您的回答,代码有效,但我希望在一组内同时在一年内获得案例的距离
    • 我对 OP 中的 y3a-y3b 之类的术语感到困惑,这让我相信你想要 a 组和 b 组之间的距离......我想我现在明白你想要做什么了,让我知道它是否有效...
    • 这看起来已经很不错了,konvas 并且非常适合只有两个人的团体。然而,对于有三个的组(即最后一个,2003,b),只有一个值。拥有每条线之间的距离以及组中的其他线之间的距离会很棒。因此,理想情况下,每个值都将在该组中分配两个距离 - 我很抱歉不清楚
    • 谢谢!!这已经很有帮助了,最后一个问题,我们可以将它移动到列而不是新行吗?已经非常感谢了
    • 没问题!你的意思是b1,b2,b3有额外的列而不是新行?我认为这行不通,因为与 id1 == "a" 对应的数据的列数将少于 id1 == "b" 的列数,因此您无法重新绑定它们。最简单的方法可能是重塑您想要格式的最终​​数据框(使用来自包reshape2spread 的基本R reshape()dcast() 来自tidyr
    【解决方案2】:

    也许这会有所帮助。

    dist(dat[which(dat[,"id1"]=="a" & dat[,"id2"]=="2002"),], method ="euclidean") dist(dat[which(dat[,"id1"]=="b" & dat[,"id2"]=="2003"),], method ="euclidean")

    【讨论】:

    • 与 dist() 函数比较,下面的结果是不同的。我也有点困惑。当组中有超过 2 个元素时,您更喜欢在新列和元素的同一行中显示哪个距离?比如 b-group 2003?
    • 应该有 2 个值,所以是一个距离矩阵。 dist(x, upper=TRUE) 可能有用,我刚刚找到了。但我不知道如何遍历组并获得具有相同列数的矩阵......即我不确定如何将列附加到数据集
    • > dat$new=NULL > for (i in 1:length(dat)){ + dat$new[i]=i + }
    • 也许这会有所帮助
    • 不确定,怎么做? (很抱歉给您带来不便,非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2016-02-15
    • 2011-01-29
    • 2013-02-12
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多