如何解决应用由单独的固定点确定的旋转矩阵 - python
我将旋转矩阵应用于一组点,目的是沿水平轴对齐这些点。使用下面,我要调整的xy点记录在x
和y
中。
我希望使用 X_Ref
和 Y_Ref
以及 X_Fixed
和 Y_Fixed
之间的角度来转换点。我还希望在旋转完成后转换点,使 X_Ref
和 Y_Ref
处于 0,0。
旋转点目前不会为此进行调整。我不确定是否应该在旋转之前或之后考虑参考点。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import pandas as pd
df = pd.DataFrame({
'Period' : ['1','1','2','2'],'Label' : ['A','B','C','D','A','D'],'x' : [2.0,3.0,2.0,1.0],'y' : [2.0,-1.0,0.0,'X_Ref' : [1,1,2,2],'Y_Ref' : [1,0],'X_Fixed' : [0,'Y_Fixed' : [0,})
np.random.seed(1)
xy = df[['x','y']].values
Ref = df[['X_Ref','Y_Ref']].values
Fix = df[['X_Fixed','Y_Fixed']].values
fig,ax = plt.subplots()
plot_kws = {'alpha': 0.75,'edgecolor': 'white','linewidths': 0.75}
ax.scatter(xy[:,xy[:,1],**plot_kws)
ax.scatter(Ref[:,Ref[:,marker = 'x')
ax.scatter(Fix[:,Fix[:,marker = '+')
pca = PCA(2)
# Fit the PCA object,but do not transform the data
pca.fit(xy)
# pca.components_ : array,shape (n_components,n_features)
# cos theta
ct = pca.components_[0,0]
# sin theta
st = pca.components_[0,1]
# One possible value of theta that lies in [0,pi]
t = np.arccos(ct)
# If t is in quadrant 1,rotate CLOCKwise by t
if ct > 0 and st > 0:
t *= -1
# If t is in Q2,rotate COUNTERclockwise by the complement of theta
elif ct < 0 and st > 0:
t = np.pi - t
# If t is in Q3,rotate CLOCKwise by the complement of theta
elif ct < 0 and st < 0:
t = -(np.pi - t)
# If t is in Q4,rotate COUNTERclockwise by theta,i.e.,do nothing
elif ct > 0 and st < 0:
pass
# Manually build the ccw rotation matrix
rotmat = np.array([[np.cos(t),-np.sin(t)],[np.sin(t),np.cos(t)]])
# Apply rotation to each row of 'm'. The output (m2)
# will be the rotated FIFA input coordinates.
m2 = (rotmat @ xy.T).T
# Center the rotated point cloud at (0,0)
m2 -= m2.mean(axis=0)
初始分发期 1:
预期分发期 1:
初始分发期 2:
预期分发期 2:
解决方法
您的问题不清楚,因为如果您绘制已经计算出的m2
,则问题中提到的“预期旋转”已经可以实现:
fig,ax = plt.subplots()
ax.scatter(m2[:,0],m2[:,1],**plot_kws)
输出:
但是您在问题中也提到了以下内容:
旋转角度由 X_Ref,Y_Ref 和 X_Fixed,Y_Fixed 之间的角度决定。
这是一个完全不同的场景。您可以通过计算两点之间的 arctan
来计算两点之间的角度,而根本不必使用 PCA。这可以使用 numpy.arctan 完成,如下所示:
t = np.arctan((Y_Fixed - Y_Ref/ X_Fixed - X_Ref))
这里假设 (X_Fixed,Y_Fixed) 和 (X_Ref,Y_Ref) 为两点。
对于数据框中的每一行,您可以计算旋转后的 x
和 y
值,该值相对于该特定行中的 (X_Fixed,Y_Ref) 之间的角度.这可以使用以下代码片段来完成;
def rotate_points(row):
t = np.arctan((row['Y_Fixed'] - row['Y_Ref']/ row['X_Fixed'] - row['X_Ref']))
rotmat = np.array([[np.cos(t),-np.sin(t)],[np.sin(t),np.cos(t)]])
xy = row[['x','y']].values
rotated = rotmat @ xy
return rotated
df['rotated_x'] = df.apply(lambda row: rotate_points(row)[0],axis = 1)
df['rotated_y'] = df.apply(lambda row: rotate_points(row)[1],axis = 1)
您的数据框现在看起来像这样,右侧添加了两个新列:
+----+----------+---------+-----+-----+---------+---------+-----------+-----------+-------------+-------------+-------------+
| | Period | Label | x | y | X_Ref | Y_Ref | X_Fixed | Y_Fixed | Direction | rotated_x | rotated_y |
|----+----------+---------+-----+-----+---------+---------+-----------+-----------+-------------+-------------+-------------|
| 0 | 1 | A | -1 | 1 | 1 | 3 | -2 | 0 | Left | -1.34164 | 0.447214 |
| 1 | 1 | B | 0 | 4 | 1 | 3 | -2 | 0 | Left | -1.78885 | 3.57771 |
| 2 | 1 | C | 2 | 2 | 1 | 3 | -2 | 0 | Left | 0.894427 | 2.68328 |
| 3 | 1 | D | 2 | 3 | 1 | 3 | -2 | 0 | Left | 0.447214 | 3.57771 |
| 4 | 2 | E | 2 | 4 | 1 | 3 | -2 | 0 | Right | 0 | 4.47214 |
| 5 | 2 | F | 1 | 4 | 1 | 3 | -2 | 0 | Right | -0.894427 | 4.02492 |
| 6 | 2 | G | 3 | 5 | 1 | 3 | -2 | 0 | Right | 0.447214 | 5.81378 |
| 7 | 2 | H | 0 | 2 | 1 | 3 | -2 | 0 | Right | -0.894427 | 1.78885 |
+----+----------+---------+-----+-----+---------+---------+-----------+-----------+-------------+-------------+-------------+
现在您已根据需要旋转了 x
和 y
点。
更新:
根据修改后的问题,您可以在图中添加 (0,0) 处的参考点,如下所示:
fig,**plot_kws)
ax.scatter(list(np.repeat(0,len(Ref))),list(np.repeat(0,**plot_kws)
plt.show()
输出:
,如果我理解您要实现的目标,则不需要任何 PCA。我会使用复数,这看起来更简单:
编辑
之前翻译的步骤顺序有一个小错误。此编辑将更正它并使用您的新数据集,包括在不同时期更改参考/固定点。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({
'Period' : ['1','1','2','2'],'Label' : ['A','B','C','D','A','D'],'x' : [2.0,3.0,2.0,1.0],'y' : [2.0,-1.0,0.0,'X_Ref' : [1,1,2,2],'Y_Ref' : [1,'X_Fixed' : [0,'Y_Fixed' : [0,})
首先,将固定/参考点转换为复数:
for f in ['Ref','Fixed']:
df[f] = df['X_'+f] + 1j*df['Y_'+f]
df.drop(['X_'+f,'Y_'+f],axis=1,inplace=True)
计算旋转(请注意,它与您在问题中陈述的角度相反,以符合您的预期结果):
df['angle'] = - np.angle(df['Ref'] - df['Fixed'])
计算每个点的旋转(包括参考/固定):
df['rotated'] = (df['x'] + 1j*df["y"]) * np.exp(1j*df['angle'])
for f in ['Ref','Fixed']:
df[f+'_Rotated'] = df[f] * np.exp(1j*df['angle'])
以“ref”点为中心:
df['translation'] = - df['Ref_Rotated']
df['NewPoint'] = df['rotated'] + df['translation']
for f in ['Ref','Fixed']:
df[f+'_Transformed'] = df[f+'_Rotated'] + df['translation']
恢复到笛卡尔坐标:
df['x2'] = np.real(df['NewPoint'])
df['y2'] = np.imag(df['NewPoint'])
for f in ['Ref','Fixed']:
df['NewX_'+f] = np.real(df[f+'_Transformed'])
df['NewY_'+f] = np.imag(df[f+'_Transformed'])
然后绘制您喜欢的任何时期的输出:
output = df[['Period','Label','x2','y2','NewX_Ref','NewY_Ref','NewX_Fixed','NewY_Fixed']]
output.set_index('Period',inplace=True)
fig,ax = plt.subplots()
plot_kws = {'alpha': 0.75,'edgecolor': 'white','linewidths': 0.75}
plt.xlim(-5,5)
plt.ylim(-5,5)
period = '1'
ax.scatter(output.loc[period,'NewX_Ref'],output.loc[period,'NewY_Ref'])
ax.scatter(output.loc[period,'NewX_Fixed'],'NewY_Fixed'])
ax.scatter(output.loc[period,'x2'],'y2'],**plot_kws,marker = '+')
plt.gca().set_aspect('equal',adjustable='box')
plt.show()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。