k=1 的最近邻距离(以时间为单位)

Nearest neighbor distance for k=1 in units of time

我有以下数据框

A_key      Date
      A1      2016-05-03
      A1      2016-09-25
      A2      2015-02-25
      A2      2015-02-25
      A3      2015-10-04
      A3      2016-03-15
      A3      2016-04-10
      A4      2015-09-26
      A4      2015-09-26

我想为 n_neighbor(k) = 1 以天为单位获取每个不同 A_key 的最近邻距离,这样输出如下所示

      A_key      Date       Distance
      A1      2016-05-03     145
      A1      2016-09-25     145
      A2      2015-02-25     0
      A2      2015-02-25     0
      A3      2015-10-04     163
      A3      2016-03-15     26
      A3      2016-04-10     26
      A4      2015-09-26     0
      A4      2015-09-26     0

这基于 groupby 将您的原始 df 拆分为小的唯一关键数据帧,然后我们使用 numpy 广播来加速整个计算

df.Date=pd.to_datetime(df.Date)
l=[]
for _, x in df.groupby('A_key'):
    s=np.abs((x['Date'].values - x['Date'].values[:,None])).astype('timedelta64[D]').astype(int)
    s[[np.arange(len(s))] * 2]=9999
    l.append(np.min(s,1))

df['New']=np.concatenate(l)
df
Out[501]: 
  A_key       Date  New
0    A1 2016-05-03  145
1    A1 2016-09-25  145
2    A2 2015-02-25    0
3    A2 2015-02-25    0
4    A3 2015-10-04  163
5    A3 2016-03-15   26
6    A3 2016-04-10   26
7    A4 2015-09-26    0
8    A4 2015-09-26    0

您可以使用以下代码将日期转换为纪元:

import time
date_time = '2016-05-03 00:00:00'
pattern = '%Y-%m-`enter code here`%d %H:%M:%S'
epoch = int(time.mktime(time.strptime(date_time, pattern)))

然后,简单地从它的邻居值中减去该值。请注意,结果将以毫秒为单位,因此您必须除以 (1000*60*60*24) 才能将其转换为天数。

您已经在每个键中按日期排序。 因此,您所需要的只是计算同一个键中下一个和上一个日期的距离。 我试过这个(在 Swift 中)来计算格式为 2015-05-22

的 2 个日期之间的距离
func dist(_ d1: String, _ d2: String) -> Int {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "YYYY-MM-DD"

    if let date1 = dateFormatter.date(from: d1), let date2 = dateFormatter.date(from: d2) {
        let distance = date1.timeIntervalSince(date2) / 86400
        return abs(Int(distance))
    } else { return 0 }
}

print(dist("2015-05-25", "2015-05-22"))

您现在可以遍历一个键中的值来计算给定键的最小距离(当然除了它本身)

您好,这是一个仅使用 Pandas

的可能解决方案

让我们给当前索引起个名字(为了方便,并确保我们能很好地恢复一切)

df['Date'] = df['Date'].astype('datetime64[ns]')
df.index.name = 'id'

我们首先要按日期排序并对每个组应用一个函数, 重要的是要注意,我们将依赖 pandas 保留组内的行顺序这一事实(参见文档)

sorted_df = sorted_df = df.sort_values('Date')
result_df = sorted_df.groupby('A_key').apply(nearest_date_distance)

所以现在让我们看看 nearest_date_distance 函数中有什么 该函数依赖于日期将被排序这一事实,因此我们计算到日期之前的时间和之后日期的时间,当天和第二天之间的差异是负数,这就是我们添加 [=16 的原因=]。最后我们取这两个距离之间的最小值(顺便说一下,最小运算符不会取 time_to_before 的第一行和之后的最后一行时间的缺失值 (NaT))

def nearest_date_distance(sub):
    time_to_before = sub['Date'].diff()
    time_to_after = sub['Date'].diff(-1).abs()
    nearest_date_distance = pd.concat([time_to_before, time_to_after],axis=1).min(axis=1)
    nearest_date_distance.name = 'Distance'
    return nearest_date_distance

最后我撒了个谎 result_df 将是这种形式的 MultiIndex Serie(不是数据帧):

A_key  id
A1     0    145 days
       1    145 days
A2     2      0 days
       3      0 days
A3     4    163 days
       5     26 days
       6     26 days
A4     7      0 days
       8      0 days

我们可以轻松地将其转换为 DataFrame,并且为我们的原始索引正确命名有助于查看所有内容都与原始 df 中的索引相同。

result_df =sorted_df.groupby('A_key').apply(nearest_date_distance).reset_index(level=0)

    A_key   Distance
id      
0   A1  145 days
1   A1  145 days
2   A2  0 days
3   A2  0 days
4   A3  163 days
5   A3  26 days
6   A3  26 days
7   A4  0 days
8   A4  0 days

如果您需要结果 Dataframe 上的日期 result_df['Date'] = df['Date'] 应该可以解决问题:)