Более эффективный способ вычисления углов?

#python #pandas #lambda

Вопрос:

У меня есть фрейм данных с тремя точками, заданными в виде столбцов (таким образом, всего шесть).:

 x_measured  y_measured  x_calculated  y_calculated  x_fixedpoint  
0         142          37           143          37.5           138   
1         142          37           143          37.6           138   
2         142          37           143          37.7           138   
3         142          37           143          37.8           138   
4         142          37           143          37.9           138   
5          73          55            71          55.6            72   
6          73          55            71          55.7            72   
7          73          55            71          55.8            72   
8          73          55            71          55.9            72   
9          73          55            71          55.1            72   

   y_fixedpoint  
0            38  
1            38  
2            38  
3            38  
4            38  
5            55  
6            55  
7            55  
8            55  
9            55  
 

Теперь мне нужно рассчитать угол между (x_measured, y_measured) и (x_calculated y_calculated) относительно (x_fixedpoint, y_fixedpoint) . Для этого я создал эту функцию:

 def angle_calculator(x1,x2,x3,x4,x5,x6):
    All_points = np.array([[x1,x2],[x3,x4],[x5,x6]])
    A = All_points[2] - All_points[0]
    B = All_points[1] - All_points[0]
    C = All_points[2] - All_points[1]

    for e1, e2 in ((A, B), (A, C), (B, -C)):
        dotproduct = np.dot(e1, e2)
        norm = np.linalg.norm(e1) * np.linalg.norm(e2)
        if dotproduct !=0:
            angle = round(np.arccos(dotproduct/norm) * 180 / np.pi, 2)
        else:
            angle = 0
    return angle
 

принимая различные координаты x,y в качестве аргументов и возвращая угол. Это работает и дает:

 df['angles'] = df.apply(lambda x: angle_calculator(x.x_measured, x.y_measured, x.x_calculated, x.y_calculated, x.x_fixedpoint, x.y_fixedpoint), axis=1)

x_measured  y_measured  x_calculated  y_calculated  x_fixedpoint  
0         142          37           143          37.5           138   
1         142          37           143          37.6           138   
2         142          37           143          37.7           138   
3         142          37           143          37.8           138   
4         142          37           143          37.9           138   
5          73          55            71          55.6            72   
6          73          55            71          55.7            72   
7          73          55            71          55.8            72   
8          73          55            71          55.9            72   
9          73          55            71          55.1            72   

   y_fixedpoint     angles  
0            38  32.275644  
1            38  35.537678  
2            38  38.425651  
3            38  40.950418  
4            38  43.132975  
5            55  14.264512  
6            55  15.701974  
7            55  16.858399  
8            55  17.759467  
9            55   2.848188 
 

Обычно я был бы очень доволен этим BUT…..it это довольно медленно для кадров данных с более чем 200 000 строками. Медленно (я знаю!) — это реальный термин, но в этом случае для 200 000 строк требуется около 10 секунд.

Итак, мои вопросы таковы:

  1. Не слишком ли я все усложняю?
  2. Есть ли более эффективный способ сделать это?

Как всегда, благодарен за знания.

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

1. Ваша функция смотрит на (A, B), (A, C), (B, -C) пары, но так angle как переопределяется в каждом повороте, действует только (B, -C) . Но вместо этого, разве вы не должны смотреть (A, C) только на пару? То , что я понял из относительного угла, — это угол между fixed - measured и fixed - calculated , следовательно (A, C) , парой. Не могли бы вы, пожалуйста, прояснить это?

2. Да! Вы совершенно правы. Изменю это и проверю, будет ли это быстрее. Спасибо!

3. Насколько я понял, я попытался написать ответ, но не уверен, что конечный результат соответствует вашим требуемым выводам…

Ответ №1:

Учитывая значения в домене numpy, мы можем сделать:

 # extract the pairs (and go to numpy)
meas = df.filter(like="measured").to_numpy()
calc = df.filter(like="calculated").to_numpy()
fix = df.filter(like="fixed").to_numpy()

# calculate the differences of `meas` and `calc` from `fix`
meas_dist = fix - meas
calc_dist = fix - calc

# get the inner products
inners = (meas_dist * calc_dist).sum(axis=1)
# or with: inners = np.einsum("ij,ij->i", meas_dist, calc_dist); might be faster

# norm function for brevity
norm = lambda mat: np.linalg.norm(mat, axis=1)

# get the angles (in radians)
angles_in_rad = np.arccos(inners / (norm(meas_dist) * norm(calc_dist)))

# handling possible NaNs (by @Serge de Gosson de Varennes, thanks!)
where_nans = isnan(angles_in_rad)
angles_in_rad[where_nans ] = 0

# go to degrees
angles_in_deg = np.rad2deg(angles_in_rad)

# put back to df
df["angles"] = angles_in_deg
 

Я получаю:

 >>> df

   x_measured  y_measured  x_calculated  y_calculated  x_fixedpoint  y_fixed_point      angles
0         142          37           143          37.5           138             38    8.325650
1         142          37           143          37.6           138             38    9.462322
2         142          37           143          37.7           138             38   10.602613
3         142          37           143          37.8           138             38   11.745633
4         142          37           143          37.9           138             38   12.890481
5          73          55            71          55.6            72             55  149.036243
6          73          55            71          55.7            72             55  145.007980
7          73          55            71          55.8            72             55  141.340192
8          73          55            71          55.9            72             55  138.012788
9          73          55            71          55.1            72             55  174.289407
 

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

1. Отличная работа! Намного быстрее! Если бы я мог прийти с одним предложением, оно было бы следующим. Не всегда можно быть уверенным в том, что норма (или продукты норм ненулевые). Таким образом, это вполне может привести к nan появлению ценностей. Я предлагаю добавить angles_in_rad = np.arccos(inners/(norm(meas_dist) * norm(calc_dist))) where_nans = isnan(angles_in_rad) angles_in_rad[where_nans ] = 0 .

2. @SergedeGossondeVarennes о, это мои плохие математические навыки, спасибо за исправление!