我想根据时间信息计算项目(0,1,2,3 ..)之间的相似性。时间信息可以是时刻(startdate),时间间隔(startdate,enddate)或null(NaT);请参阅下面的dataframe( df_for )示例。
以下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.
答案 0 :(得分:2)
我可以看到几种简化代码的方法。
首先,我建议将比较代码移到一个单独的函数中,该函数将st1
,ed1
,st2
和ed2
作为参数。 (作为旁注,为什么st
(开始时间)但是ed
(结束日期)?一致的名字可能会很好。)你' d能够将return
结果发送到调用代码,调用代码将负责分配到结果数组中。
说到那个调用代码......它不需要为每对时间范围调用比较函数。结果应始终是对称的(例如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]
。
只需返回比较结果if some_test_expression: return 1; else: return 0
,而不是大量return some_test_expression
块。 Python的bool
类型是int
的子类,所以当你将它们分配到一个numpy数组时,它们应该自动转换为数字(如果你想要函数,你也可以显式转换为float
总是返回相同的类型。)
处理顶部空值的所有案例。它们是一个简单的例子,如果你先处理它们(然后返回,那么函数的其余部分就不会运行),你不需要在以后检查那么多的东西。您不需要单独处理null / null,null / instant和null / interval,如果任一起始值为null,则返回零:if isnull(st1) or isnull(st2): return 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])
一些注释我无法在代码中添加注释:
compare_interval
函数的最后一步不需要测试值是间隔值,因为所有其他情况(包括时刻和空值)都已经处理过了。
我已将sim
数组的赋值更改为使用元组索引,因为这比多维numpy数组的嵌套索引更自然(如果使用嵌套索引,则切片不起作用)。您可以使用data
查找执行相同的操作,但我没有更改它们,因为我不知道大熊猫几乎和我知道的一样。
说到我缺乏大熊猫的知识,可能有一种方法可以将比较函数直接应用于数据框与自身的“连接”。我不知道怎么做。如果它存在,它可能比我在调用代码中大部分保持不变的嵌套循环更快(即使它没有优化对称情况)。
答案 1 :(得分:0)
这里没有试图给出完整答案。 我创建了一个类来接收开始和结束时间,然后对构造函数执行所有null not null检查。 稍后在那个课程中,我将重载比较运算符。就像<=> eq 代表==而 ne 代表!= 在重载上,您可以尝试返回整数值而不是True和False。 有了这一切,我可能会重写 即时(data [i] [0],data [i] [1])== Instant(data [j] [0],data [j] [1])
从那里得到你需要的东西