在Python中无numpy快速计算坐标元组集的重心方法

34 投票
4 回答
94561 浏览
提问于 2025-04-18 02:24

我正在做一个非常紧急的项目(不幸的是必须用Python),其中有一个函数被广泛使用,这个函数用来计算一系列(x, y)坐标的中心点。举个例子:

def centroid(*points):
    x_coords = [p[0] for p in points]
    y_coords = [p[1] for p in points]
    _len = len(points)
    centroid_x = sum(x_coords)/_len
    centroid_y = sum(y_coords)/_len
    return [centroid_x, centroid_y]

其中

>>> centroid((0, 0), (10, 0), (10, 10), (0, 10))
[5, 5]

这个函数运行得相当快,上面的例子在我的系统上平均只需1.49e-05秒,但我想找到计算中心点的最快方法。你们有什么建议吗?

我还有一个其他的解决方案,就是这样做(其中l是元组的列表):

map(len(l).__rtruediv__, map(sum, zip(*l)))

这个方法的运行时间在1.01e-05到9.6e-06秒之间,但不幸的是,把整个语句用list( ... )包裹起来转换成列表,几乎会让计算时间增加一倍。

编辑:欢迎提供纯Python的建议,但不要用numpy。

编辑2:我刚发现,如果单独保存一个变量来记录元组列表的长度,那么我之前用map的方法运行时间稳定在9.2e-06秒以下,但仍然有转换回列表的问题。

编辑3:

现在我只接受纯Python的答案,不要用numpy(对已经用numpy回答的人表示抱歉!)

4 个回答

0

这是一个简单的numpy实现,我在这里不能计时,所以我想知道它是怎么做的:

import numpy as np

arr = np.asarray(points)
length = arr.shape[0]
sum_x = np.sum(arr[:, 0])
sum_y = np.sum(arr[:, 1])
return sum_x / length, sum_y / length

你把点作为单独的参数传递给centroid(),然后用*points把它们放到一个元组里。其实直接传一个包含点的列表或者迭代器会更快。

5

为了完整起见,我对Retozi的函数进行了修改,使它可以接受任何维度的向量:

def centeroidnp(arr):
    length, dim = arr.shape
    return np.array([np.sum(arr[:, i])/length for i in range(dim)])
9

在笛卡尔坐标系中,质心就是各个分量的平均值:

data = ((0,0), (1,1), (2,2))
np.mean(data, axis=0)
>>> array([1., 1.])
34
import numpy as np

data = np.random.randint(0, 10, size=(100000, 2))

这里的速度很快

def centeroidnp(arr):
    length = arr.shape[0]
    sum_x = np.sum(arr[:, 0])
    sum_y = np.sum(arr[:, 1])
    return sum_x/length, sum_y/length

%timeit centeroidnp(data)
10000 loops, best of 3: 181 µs per loop

令人惊讶的是,这个速度要慢得多:

%timeit data.mean(axis=0)
1000 loops, best of 3: 1.75 ms per loop

我觉得numpy非常快...

为了完整性:

def centeroidpython(data):
    x, y = zip(*data)
    l = len(x)
    return sum(x) / l, sum(y) / l
#take the data conversion out to be fair!
data = list(tuple(i) for i in data)

%timeit centeroidpython(data)
10 loops, best of 3: 57 ms per loop

撰写回答