Python-基于时态信息(即时、间隔)计算项目之间的相似性

Python-基于时态信息(即时、间隔)计算项目之间的相似性,python,time,intervals,similarity,pdist,Python,Time,Intervals,Similarity,Pdist,我想根据项目(0,1,2,3..)的时间信息计算它们之间的相似性。时间信息可以是时间瞬间(startdate)、时间间隔(startdate、enddate)或空(NaT);请参见下面的数据帧(df_)示例 {instant,instant}:如果等于sim=1,否则sim=0 {instant,null}或反之亦然,sim=0 {instant,interval}:如果instant在interval内,则sim=1;如果interval包含一个instant,则sim=1 {interva

我想根据项目(0,1,2,3..)的时间信息计算它们之间的相似性。时间信息可以是时间瞬间(startdate)、时间间隔(startdate、enddate)或空(NaT);请参见下面的数据帧(df_)示例

  • {instant,instant}:如果等于sim=1,否则sim=0
  • {instant,null}或反之亦然,sim=0
  • {instant,interval}:如果instant在interval内,则sim=1;如果interval包含一个instant,则sim=1
  • {interval,interval}:如果间隔重叠,sim=两个间隔的交点/两个间隔的并集
  • {interval,interval}:如果一个区间包含另一个区间,则sim=1
  • 下面的python代码从数据帧获取时间信息,并执行上述条件(1-5)。代码很冗长,我想知道是否有一种聪明的方法/lib来使用python计算时间段和时间瞬间之间的相似性

    m, k = df_for.shape
    sim = np.zeros((m, m))
    data = df_for.values
    for i in range(m):
        for j in range(m):
            if i != j:
                st1 = data[i][0]
                ed1 = data[i][1]
                st2 = data[j][0]
                ed2 = data[j][1]
                #both items are null values
                if pd.isnull(st1) and pd.isnull(ed1) and pd.isnull(st2) and pd.isnull(ed2):
                    sim[i][j] = 0.
                # {instant, instant} => equal, not equal
                if pd.notnull(st1) and pd.isnull(ed1) and pd.notnull(st2) and pd.isnull(ed2):
                    if st1 == st2:
                        sim[i][j] = 1.
                    else:
                        sim[i][j] = 0.
                # {instant, null} => sim is 0
                if pd.notnull(st1) and pd.isnull(ed1) and pd.isnull(st2) and pd.isnull(ed2):
                    sim[i][j] = 0.
                # {instant, interval} => meets, during
                if pd.notnull(st1) and pd.isnull(ed1) and pd.notnull(st2) and pd.notnull(ed2):
                        if(st2 <= st1 <= ed2):
                            sim[i][j] = 1. #a time is between two other times
                        else:
                            sim[i][j] = 0.
                # {interval, instant} => meets, contains
                if pd.notnull(st1) and pd.notnull(ed1) and pd.notnull(st2) and pd.isnull(ed2):
                        if(st1 <= st2 <= ed1):
                            sim[i][j] = 1. #a time is between two other times
                        else:
                            sim[i][j] = 0.
                # {interval, interval} => equal, overlaps, not overlaps
                if pd.notnull(st1) and pd.notnull(ed1) and pd.notnull(st2) and pd.notnull(ed2): 
                    if (st1 <= st2 <= ed1) or (st2 <= st1 <= ed2):
                        intersect = min(ed1,ed2)- max(st1,st2) # earliestend-lateststart
                        union = max(st1,st2,ed1,ed2) - min(ed1,ed2,st1,st2)
                        overlaps = intersect/union
                        #print(intersect/np.timedelta64(1, 'D'),union/np.timedelta64(1, 'D'))
                        if (st1 > st2 and ed1 < ed2) or (st1 < st2 and ed1 > ed2): # contains, during
                            overlaps = 1.0
                        sim[i][j]=overlaps  
                    else:
                        sim[i][j] = 0.  
            else:
                sim[i][j] = 1.
    
    m,k=df_表示形状
    sim=np.零((m,m))
    数据=df_表示值
    对于范围内的i(m):
    对于范围内的j(m):
    如果我j:
    st1=数据[i][0]
    ed1=数据[i][1]
    st2=数据[j][0]
    ed2=数据[j][1]
    #这两项都是空值
    如果pd.isnull(st1)和pd.isnull(ed1)以及pd.isnull(st2)和pd.isnull(ed2):
    sim[i][j]=0。
    #{instant,instant}=>相等,不相等
    如果pd.notnull(st1)和pd.isnull(ed1)以及pd.notnull(st2)和pd.isnull(ed2):
    如果st1==st2:
    sim[i][j]=1。
    其他:
    sim[i][j]=0。
    #{instant,null}=>sim为0
    如果pd.notnull(st1)和pd.isnull(ed1)以及pd.isnull(st2)和pd.isnull(ed2):
    sim[i][j]=0。
    #{instant,interval}=>在
    如果pd.notnull(st1)和pd.isnull(ed1)以及pd.notnull(st2)和pd.notnull(ed2):
    
    如果(st2没有尝试在这里给出完整的答案。 我将创建一个类来接收开始和结束时间,然后对构造函数执行所有null notnull检查。 稍后,我将重载比较运算符。例如,eqfor==和nefor= 在重载上,您可以尝试返回整数值,而不是True和False。 有了这些,我可能会重写 即时(数据[i][0],数据[i][1])==即时(数据[j][0],数据[j][1])


    然后从中导出所需的内容

    我可以看到几种简化代码的方法

  • 首先,我建议将比较代码移动到一个单独的函数中,该函数将
    st1
    ed1
    st2
    ed2
    作为参数(顺便提一句,为什么
    st
    (开始时间)而
    ed
    (结束日期)?一致的名称可能更好。)您可以
    将您的结果返回给调用代码,调用代码将负责在结果数组中进行赋值

  • 说到调用代码……它不需要为每对时间范围调用比较函数。结果应该总是对称的(例如,
    compare(data[i][0]、data[i][1]、data[j][0]、data[j][1])
    将返回与
    compare(data[j][0]、data[j][1]、data[i][0]、data[i][1]相同的结果。
    )因此,只需调用其中一个,并将结果分配给
    sim[i][j]
    sim[j][i]

  • 如果某个测试表达式:返回1;否则:返回0
  • 块,则只返回比较结果:
    返回某个测试表达式
    。Python的
    bool
    类型是
    int
    的子类,因此当您将它们分配到numpy数组中时,它们应该自动转换为数字(如果希望函数始终返回相同的类型,也可以显式强制转换为
    float

  • 处理所有在顶部为null的情况。这是一个简单的情况,如果您先处理它们(然后返回,这样函数的其余部分就不会运行),以后不需要检查那么多内容是否为null。不需要分别处理null/null、null/instant和null/interval,如果起始值为null,只需返回零:
    如果isnull(st1)或isnull(st2):返回0。

  • 即时/间隔比较是对称的,因此只需单向写入逻辑,并在参数初始顺序错误时翻转参数:
    if isnull(ed2):st1,ed1,st2,ed2=st2,ed2,st1,ed1

  • 我看不出有太多可以改进的地方,比如在实例和间隔之间或两个间隔之间进行比较的具体实现(上面提到的一般情况除外)。但是,我想说的是,你的重叠间隔公式在相似性方面会有很大的不连续性,因为一个小间隔慢慢地与一个大间隔重叠更多,然后得到完全包含。例如,在一小时间隔之前仅开始一毫秒的一秒间隔将导致两者之间的相似性值在
    0.000277
    0.999/(3600+0.001)
    )中,但与较大间隔同时开始的一个将具有
    1.0
    的相似性(一个很大的差异)。更连续变化的相似性度量将来自重叠量除以较小间隔的大小(而不是联合的大小)。对于一个区间完全包含另一个区间,此公式不需要特殊情况,因为默认计算将给出一个相似性
    1.0
    (重叠将是较小区间的大小,因此您将其自身除以)

  • 所以,把所有这些放在一起,下面是我如何重写的:

    def compare_intervals(st1, et1, st2, et2): # note I've renamed the ed's to et
        # all nulls
        if pd.isnull(st1) or pd.isnull(st2):
            return 0.
    
        # {instant, instant} => equal, not equal
        if pd.isnull(et1) and pd.isnull(et2):
            return float(st1 == st2)
    
        # {instant, interval} => flip params (to be handled in next block)
        if pd.isnull(et1):
            st1, et1, st2, et2 = st2, et2, st1, et1
    
        # {interval, instant} => meets, contains
        if pd.isnull(et2):
            return float(st1 <= st2 <= et1)
    
        # {interval, interval} => equal, overlaps, not overlaps
        if (st1 <= st2 <= et1) or (st2 <= st1 <= et2):
            intersect = min(et1, et2) - max(st1, st2)
            min_interval = min(et1 - st1, et2 - st2) # using minimum instead of union
            return intersect / min_interval
    
        return 0. # intervals didn't overlap
    
    m, k = df_for.shape
    sim = np.zeros((m, m))
    data = df_for.values
    for i in range(m):
        for j in range(i, m): # we only iterate on j values >= i
            if i == j:
                sim[i,j] = 1.
            else:
                sim[i,j] = sim[j,i] = compare_intervals(data[i][0], data[i][1],
                                                        data[j][0], data[j][1])
    
    def compare_interval(st1、et1、st2、et2):#注意,我已将ed重命名为et
    #所有空值
    如果pd.isnull(st1)或pd.isnull(st2):