Расстояние до ближайшего соседа для k = 1 в единицах времени - PullRequest
0 голосов
/ 17 января 2019

У меня есть следующий фрейм данных

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 в единицах дней для n_neighbor (k) = 1, чтобы результат выглядел следующим образом

      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

Ответы [ 4 ]

0 голосов
/ 17 января 2019

Привет, вот возможное решение с использованием только панд

Давайте дадим имя текущему индексу (для удобства и будем уверены, что мы все хорошо восстановим)

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

Сначала мы отсортируем по дате и применим функцию к каждой группе, важно отметить, что мы будем полагаться на тот факт, что панды сохраняют порядок строк внутри группы (см. документацию)

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

Итак, давайте посмотрим, что у нас есть внутри функции nearest_date_distance Функция основана на том факте, что Дата будет отсортирована, поэтому мы вычисляем время до даты до и после даты, разница между текущим днем ​​и следующим днем ​​отрицательная, поэтому мы добавляем .abs() . Наконец, мы берем минимум между этими двумя расстояниями (кстати, оператор min не примет пропущенное значение (NaT), которое у вас есть для первой строки time_to_before и последней строки времени после)

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

и если вам нужна дата в итоговом кадре данных result_df['Date'] = df['Date'], то добьетесь цели:)

0 голосов
/ 17 января 2019

Вы можете конвертировать даты в эпоху, используя этот код:

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), чтобы преобразовать его в дни.

0 голосов
/ 17 января 2019

Вы уже отсортировали по дате в каждом ключе. Так что все, что вам нужно, это вычислить расстояние до следующей и предыдущей даты в одном ключе. Я попробовал это (в Swift), чтобы вычислить расстояние между 2 датами в формате 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"))

Теперь вы можете циклически проходить значения в одном ключе, чтобы вычислить минимальное расстояние для данного ключа (кроме самого себя, конечно)

0 голосов
/ 17 января 2019

Это основано на 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
...