Python / Pandas есть ли способ векторизовать сравнение со всеми остальными точками в противоположной категории? - PullRequest
0 голосов
/ 22 октября 2019

У меня есть набор данных x, y точек, которые находятся в двух отдельных категориях. Есть много «кадров» из десяти или около того точек, которые я хочу сгруппировать (или разделить на части) вместо того, чтобы перебирать. Я хочу сравнить каждую точку в категории A со всеми точками в категории B. В частности, я хочу расстояние между ними. Я не нашел правильной комбинации групповых операций для ее векторизации.

Вот пример df:


   frame_id point_id      x      y cat
0         1        1  1.769  2.491   A
1         1        2  1.024  0.981   A
2         1        3  4.327   9.81   A
3         1        4  5.407   4.33   A
4         1        5  0.936  0.019   B
5         1        6    5.1  7.639   B
6         1        7  9.139  6.721   B
7         1        8  1.954  5.424   B
8         2        1  5.835  9.702   A
9         2        2  1.784  1.374   A
10        2        3   0.23  1.921   A
11        2        4  9.328  5.836   A
12        2        5  5.516  8.971   B
13        2        6  9.108  8.917   B
14        2        7  4.412  1.033   B
15        2        8   1.33  5.898   B

В идеале, в этом примере я бы добавил четыре столбца. Один столбец для каждого расстояния, чтобы указать в другой категории. Я предполагаю, что есть какой-то способ сделать df.groupby (['frame_id']) или df.groupby (['frame_id', 'cat']) и сравнить их таким образом, я просто не понял этого.

Мне удалось сделать это путем итерации:

import scipy.spatial


for idx, fid in enumerate(frame_ids):

    if idx % 1000 == 0:
        print(idx)

    # separate categories
    cat_a = df.loc[(df.frame_id==fid)&(df.Cat=="A")]
    cat_b = df.loc[(df.frame_id==fid)&(df.Cat=="B")]

    # get distance to every opposing category point
    a_mat = scipy.spatial.distance.cdist(cat_a[['X','Y']], cat_b[['X','Y']], metric='euclidean')
    b_mat = scipy.spatial.distance.cdist(cat_b[['X','Y']], cat_a[['X','Y']], metric='euclidean')

    a_ids = cat_a[['frame_id','point_id']].values
    b_ids = cat_b[['frame_id','point_id']].values

    a_dist = np.concatenate((a_ids, a_mat),axis=1)
    b_dist = np.concatenate((b_ids, b_mat),axis=1)


    ### then concat one by one w/ larger dataframe (takes forever) ###

Вывод (для ясности отброшено несколько столбцов):

   frame_id point_id Dist_Opp1 Dist_Opp2 Dist_Opp3 Dist_Opp4
0         1        1   2.60858   6.13168   8.49763   2.93883
1         1        2  0.966017   7.80658   9.93986   4.53929
2         1        3   10.3616   2.30451   5.71815    4.9868
3         1        4   6.21084   3.32321   4.43223   3.62216
4         1        5   2.60858  0.966017   10.3616   6.21084
5         1        6   6.13168   7.80658   2.30451   3.32321
6         1        7   8.49763   9.93986   5.71815   4.43223
7         1        8   2.93883   4.53929    4.9868   3.62216
8         2        1  0.797573   3.36582   8.78502   5.89622
9         2        2   8.46417   10.5137   2.65003   4.54672
10        2        3    8.8116   11.3032   4.27524   4.12632
11        2        4   4.93554   3.08884   6.87284   7.99824
12        2        5  0.797573   8.46417    8.8116   4.93554
13        2        6   3.36582   10.5137   11.3032   3.08884
14        2        7   8.78502   2.65003   4.27524   6.87284
15        2        8   5.89622   4.54672   4.12632   7.99824

Нет необходимости сравнивать точки в пределахтой же категории.

1 Ответ

0 голосов
/ 26 октября 2019

Разобрался, в конце концов. Это просто потребовало творческого изменения / повторения с клочковатыми матрицами.


    df['loc'] = list(zip(df['x'],df['y']))
    groupA = df.loc[df.Cat==1]
    groupB = df.loc[df.Cat==0]

    groupA = groupA[['frame_id','point_id','loc']]
    groupB = groupB[['frame_id','point_id','loc']]

    acol = groupA['loc'].values
    bcol = groupB['loc'].values

    group_size = 4
    acol = np.repeat(acol,group_size,axis=0)

    bcol = bcol.reshape(-1,group_size)
    bcol = np.repeat(bcol,group_size,axis=0)
    bcol = bcol.reshape(-1)

    # numpy requires replacing tuple with 2d point
    acol = np.array([*acol])
    bcol = np.array([*bcol])

    # distance calc
    desired_matrix = np.linalg.norm(acol - bcol, axis=-1)


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...