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

Python k=1的最近邻距离(以时间为单位),python,pandas,datediff,nearest-neighbor,date-difference,Python,Pandas,Datediff,Nearest Neighbor,Date Difference,我有以下数据帧 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

我有以下数据帧

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_键的最近邻距离,以天为单位,这样输出如下所示

      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

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在一个组中保持行顺序(参见文档)

现在让我们看看
最近的日期距离
函数中的内容
函数依赖于日期将被排序的事实,因此我们计算到日期之前的时间和到日期之后的时间,当前日期和第二天之间的差值为负值,这就是为什么我们添加
.abs()
。最后我们取这两个距离之间的最小值(顺便说一句,min运算符不会获取第一行时间_到_之前和最后一行时间到之后的缺失值(NaT))

最后我说了一点
结果_df
将是这种形式的多索引系列(不是数据帧):

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
我们可以很容易地将其转换为数据帧,并且对原始索引进行适当的命名有助于查看所有内容的索引是否与原始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

如果您需要结果数据框上的日期
result\u df['Date']=df['Date']
应该这样做:)

mmm为什么第二个A3不是163?你使用什么标准?为什么第二个A3是26而不是163?我的意思是这和行动输出是一样的,只是不明白为什么so@yatu所有日期中差异的最小值different@yatu,它不像groupby和diff那样直截了当。OP希望从groupAh中的每个点找到最近的点,现在想一想
最近的
,明白了。Thanks@abcdaire这是可以解决的,将原始索引保存为idx,然后使用_键对_值进行排序,然后执行上面的操作,然后使用idx为输出DataFrameThank重新编制索引,但这不仅仅是连续日期之间的距离,对于给定的_键,如果有三条记录,对于每个记录,计算其与其他两条记录的时间距离,并选择表示最近邻距离的最小值,除非数组已经排序;然后只需与之前和未来日期(如果存在)进行比较;所有其他人都将比这一人走得更远;abcdaire正在寻找最接近的…
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
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
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