简体   繁体   English

如何旋转一组二维点以最小化 numpy 中的垂直跨度?

[英]How to rotate a set of 2D points to minimize vertical span in numpy?

I have a collection of 2D points and I want to rotate them so as to display them spanning the least vertical height .我有一个 2D 点的集合,我想旋转它们以显示它们跨越最小垂直高度 Is there a way to find the angle without using optimization ?有没有办法在不使用优化的情况下找到角度?

As a brief illustration:作为一个简短的说明:

import numpy as np

# some initial points spread out vertically
xy = np.random.randn(2, 100)
xy[1, :] *= 2

def rotate(xy, theta):
    """Return a rotated set of points.
    """
    s = np.sin(theta)
    c = np.cos(theta)
    
    xyr = np.empty_like(xy)
    xyr[0, :] = c * xy[0, :] - s *  xy[1, :]
    xyr[1, :] = s * xy[0, :] + c *  xy[1, :]
    
    return xyr

def span(xy):
    """Return the vertical span of the points.
    """
    return xy[1, :].max() - xy[1, :].min()

def plot(xy):
    """2D plot with fixed aspect ratio.
    """
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots(figsize=(3, 3))
    ax.scatter(*xy, alpha=0.3)
    ax.set_aspect(1)
    plt.show()
    plt.show()

Have a look at the original points:看看原点:

>>> span(xy)
11.503342270923472
>>> plot(xy)

原始点

Have a look at some rotated points:看看一些旋转的点:

>>> xyr = rotate(xy, np.pi / 2)
>>> span(xyr)
4.594620173868735
>>> plot(xyr)

在此处输入图像描述

The optimal answer (which should here be around pi / 2) is easy to find by invoking scipy :通过调用scipy很容易找到最佳答案(此处应为 pi / 2 左右):

>>> from scipy.optimize import minimize_scalar
>>> minimize_scalar(lambda theta: span(rotate(xy, theta)))
     fun: 4.523188831276214
    nfev: 38
     nit: 34
 success: True
       x: 1.590391370976612

But surely there is a simpler way that doesn't require scipy - and 38 function evaluations!但肯定有一种更简单的方法不需要 scipy - 和 38 个 function 评估!

This is probably not necessarily optimal, but a good enough simple solution may be to take the main components of the data and rotate the data to align largest main component with the horizontal axis.这可能不一定是最佳的,但一个足够简单的解决方案可能是获取数据的主要成分并旋转数据以使最大的主要成分与水平轴对齐。

import numpy as np

# Posted code...

# Find eigenvector with largest eigenvalue
c = (xy @ xy.T) / (xy.shape[1] - 1)
evals, evecs = np.linalg.eig(c)
imax = np.abs(evals).argmax()
v = evecs[:, imax]
# Correction angle is opposite of vector angle
theta = -np.arctan2(v[1], v[0])
# Rotate
xy2 = rotate(xy, theta)
# Plot
plot(xy2)

Output: Output:

结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM