#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. Ваша функция смотрит на
(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 о, это мои плохие математические навыки, спасибо за исправление!