Создайте матрицу расстояний с помощью пользовательской функции подобия

#python #pandas #numpy #matrix #distance

Вопрос:

У меня есть фрейм данных, который выглядит следующим образом:

 data = pd.DataFrame({'id':[1,1,1,2,2,2,3,3,3],
        'age':[20, 21,18,54,23,11, 19, 18,12],
       'experience':[5,4,3,8,2,11,2,8,6]},columns=['id','age','experience'])

   id  age experience
0   1   20  5
1   1   21  4
2   1   18  3
3   2   54  8
4   2   23  2
5   2   11  11
6   3   19  2
7   3   18  8
8   3   12  6
 

Я использую пользовательскую функцию расстояния под названием dtw_path, которая вычисляет расстояние между кортежами. Я не буду вдаваться в то, как именно эта функция вычисляет расстояние, поскольку это сложная процедура, но она просто выводит скалярное значение расстояния между кортежами.

Кортеж формируется следующим образом:

 data['age_exp'] = data[['age', 'experience']].apply(tuple, axis=1)

    id  age experience  age_exp
0   1   20   5          (20, 5)
1   1   21   4          (21, 4)
2   1   18   3          (18, 3)
3   2   54   8          (54, 8)
4   2   23   2          (23, 2)
5   2   11   11         (11, 11)
6   3   19   2          (19, 2)
7   3   18   8          (18, 8)
8   3   12   6          (12, 6)
 

Поэтому для приведенного выше фрейма данных, если мне нужно рассчитать расстояние между идентификаторами 1 и 2, я бы рассчитал расстояние следующим образом:

 data1 = data[data['id']==1]
data1 = np.array(data1['age_exp'].tolist())
data1

array([[20,  5],
       [21,  4],
       [18,  3]])

data2 = data[data['id']==2]
data2 = np.array(data2['age_exp'].tolist())
data2

array([[54,  8],
       [23,  2],
       [11, 11]])

dtw_path(data1,data2)[1]

1.5
 

Мне нужна помощь в том, как перебрать фрейм данных и создать матрицу расстояний для столбца идентификатора, т. е. что-то вроде этого

      1    2     3
1    0    1.5   2          
2    1.5  0     2.3
3    2    2.3   0
 

Комментарии:

1. что именно это dtw_path такое ?

Ответ №1:

В вашем вопросе неясно, что dtw_path это такое. Я использовал здесь tslearn.metrics.dtw_path , что дает мне разные результаты. Тем не менее, обоснование должно быть тем же самым.

Давайте сначала немного изменим исходный фрейм данных:

 data2 = (data.groupby('id')
             .apply(lambda x: np.array(list(zip(x['age'], x['experience']))))
        ).to_frame()
 
                                0
id                              
1    [[20, 5], [21, 4], [18, 3]]
2   [[54, 8], [23, 2], [11, 11]]
3    [[19, 2], [18, 8], [12, 6]]
 

nb. Для следующего шага он должен быть двумерным (фрейм данных), поэтому .to_frame()

Затем примените свою dtw_path функцию, используя scipy.spatial.distance.pdist , которая может принимать произвольную функцию расстояния с помощью параметра metric и сохранять только второй элемент вывода. Наконец, измените форму вывода в виде квадратной матрицы, используя scipy.spatial.distance.squareform :

 squareform(pdist(data2, metric=lambda x,y: dtw_path(x[0], y[0])[1]))
 

выход:

 array([[ 0.        , 35.86084215,  8.94427191],
       [35.86084215,  0.        , 36.7151195 ],
       [ 8.94427191, 36.7151195 ,  0.        ]])
 

Комментарии:

1. Это идеально , и да, значение вашего dtw_path правильное, я только что изменил его в иллюстративных целях.