【问题标题】:Temporal distance matrix from dates日期的时间距离矩阵
【发布时间】:2016-10-24 09:23:49
【问题描述】:

来自一个非常简单的数据框,例如

    time1 <- as.Date("2010/10/10")
    time2 <- as.Date("2010/10/11")
    time3 <- as.Date("2010/10/12")
    test <- data.frame(Sample=c("A","B", "C"), Date=c(time1, time2, time3))

如何获得样本 A、B、C 之间具有成对时间距离(样本之间经过的天数)的矩阵?

   A  B  C
A  0  1  2
B  1  0  1
C  2  1  0

/edit:更改了日期的格式。不便之处敬请见谅

【问题讨论】:

  • @ZheyuanLi 写一个答案,然后你也可以正确格式化你的代码。
  • 一般来说,R中这类问题的解决方案是dist函数。在您的情况下,dist(test$Date) 或多或少“有效”;但是,dist 不知道时间,因此结果只是数字,而不是 timediff 对象,这可能是个问题。因此,哲元的上述评论给出了更好的答案。

标签: r date datetime


【解决方案1】:

分两步使用data.table 方法的真正快速解决方案

# load library
 library(reshape)
 library(data.table)

# 1. Get all possible combinations of pairs of dates in long format
df <- expand.grid.df(test, test)
colnames(df) <- c("Sample", "Date", "Sample2", "Date2")

# 2. Calculate distances in days, weeks or hours, minutes etc
setDT(df)[, datedist := difftime(Date2, Date, units ="days")]

df
#>    Sample       Date Sample2      Date2 datedist
#> 1:      A 2010-10-10       A 2010-10-10   0 days
#> 2:      B 2010-10-11       A 2010-10-10  -1 days
#> 3:      C 2010-10-12       A 2010-10-10  -2 days
#> 4:      A 2010-10-10       B 2010-10-11   1 days
#> 5:      B 2010-10-11       B 2010-10-11   0 days
#> 6:      C 2010-10-12       B 2010-10-11  -1 days
#> 7:      A 2010-10-10       C 2010-10-12   2 days
#> 8:      B 2010-10-11       C 2010-10-12   1 days
#> 9:      C 2010-10-12       C 2010-10-12   0 days

【讨论】:

    【解决方案2】:

    使用外部()

    您不需要使用数据框。在您的示例中,我们可以在单个向量中收集您的日期并使用 outer()

    x <- c(time1, time2, time3)
    abs(outer(x, x, "-"))
    
         [,1] [,2] [,3]
    [1,]    0    1    2
    [2,]    1    0    1
    [3,]    2    1    0
    

    注意我在外面加了一个abs(),这样你只会得到正时差,即“今天-昨天”和“昨天-今天”的时差都是1。

    如果您的数据预先存储在数据框中,您可以将该列提取为向量,然后继续。

    使用dist()

    正如 Konrad 提到的,dist() 通常用于计算距离矩阵。最大的优点是它只会计算下/上三角矩阵(对角线为0),而复制其余部分。另一方面,outer() 强制计算所有矩阵元素,不知道对称性。

    但是,dist() 采用数值向量,并且仅计算某些类别的距离。见?dist

    Arguments:
    
           x: a numeric matrix, data frame or ‘"dist"’ object.
    
      method: the distance measure to be used. This must be one of
              ‘"euclidean"’, ‘"maximum"’, ‘"manhattan"’, ‘"canberra"’,
              ‘"binary"’ or ‘"minkowski"’.  Any unambiguous substring can
              be given.
    

    但我们实际上可以解决问题,使用它。

    日期对象,如果你给它一个原点,可以被强制转换成整数。由

    x <- as.numeric(x - min(x))
    

    我们得到自记录的第一天以来的天数。现在我们可以使用dist() 和默认的Euclidean 距离:

    y <- as.matrix(dist(x, diag = TRUE, upper = TRUE))
    rownames(y) <- colnames(y) <- c("A", "B", "C")
    
      A B C
    A 0 1 2
    B 1 0 1
    C 2 1 0
    

    为什么把outer()作为我的第一个例子

    原则上,时间差不是无符号的。在这种情况下,

    outer(x, x, "-")
    

    更合适。我后来添加了abs(),因为您似乎故意想要积极的结果。

    此外,outer() 的用途远比 dist() 广泛。看看my answer here。该OP要求计算汉明距离,这实际上是一种按位距离。

    【讨论】:

      【解决方案3】:

      这是一种使用combn 和矩阵索引的方法。

      # data
      Sample=c("A","B", "C")
      Date=as.Date(c("02/10/10", "02/10/11", "02/10/12"), format="%y/%m/%d")
      # build a matrix to be filled
      myMat <- matrix(0, length(Sample), length(Sample), dimnames=list(Sample, Sample))
      
      # get all pairwise combinations (upper triangle)
      samplePairs <- t(combn(Sample, 2))
      # add the reverse combination (lower triangle)
      samplePairs <- rbind(samplePairs, cbind(samplePairs[,2], samplePairs[,1]))
      # calculate differences
      diffs <- combn(Date, 2, FUN=diff)
      
      # fill in differences using matrix indexing
      myMat[samplePairs] <- diffs
      

      【讨论】:

        【解决方案4】:

        要获得实际天数计算,您可以将天数转换为某个预定义日期以来的日期,然后使用dist。下面的例子(换算成你的日子,我怀疑他们的表现是你期望的):

        time1 <- as.Date("02/10/10","%m/%d/%y")
        time2 <- as.Date("02/10/11","%m/%d/%y")
        time3 <- as.Date("02/10/12","%m/%d/%y")
        test <- data.frame(Sample=c("A","B", "C"), Date=c(time1, time2, time3))
        days_s2010 <- difftime(test$Date,as.Date("01/01/10","%m/%d/%y"))
        dist_days <- as.matrix(dist(days_s2010,diag=TRUE,upper=TRUE))
        rownames(dist_days) <- test$Sample; colnames(dist_days) <- test$Sample
        

        dist_days 然后打印出来:

        > dist_days
            A   B   C
        A   0 365 730
        B 365   0 365
        C 730 365   0
        

        实际上dist 不需要将日期转换为天数,只需执行dist(test$Date) 就可以工作几天。

        【讨论】:

        • OP 在我写这个答案时编辑了日期。在更新的日期中,只需执行 dist(test$Date) 即可给出答案。日期的格式以前我认为它们应该是不同的年份。
        • 我不知道 dist() 也适合这种正式场合!到目前为止,谢谢。
        猜你喜欢
        • 1970-01-01
        • 2016-12-29
        • 2019-09-04
        • 1970-01-01
        • 1970-01-01
        • 2016-12-06
        • 2022-11-21
        • 2011-08-08
        • 1970-01-01
        相关资源
        最近更新 更多