Принятый здесь ответ ИМХО не применяется к типичным случаям использования в реальной жизни, когда вы хотите вычислить центр тяжести формы, определяемой набором (x, y) вершин (также известных как многоугольник). Поэтому, пожалуйста, извините меня за ответ на вопрос, который был задан почти 8 лет назад, но он все еще вышел на первое место в моем поиске SO, поэтому он может подойти и другим. Я не говорю, что принятый ответ неверен в конкретном случае вопроса, но я думаю, что большинство людей, которые находят эту тему, на самом деле ищут центроид в соответствии с другим определением.
Центроид не определяется как среднее арифметическое вершин.
...что противоречит общепринятому мнению. Мы должны признать, что обычно под центроидом мы подразумеваем «среднее арифметическое положение всех точек на фигуре». Неформально это точка, в которой вырез формы может быть идеально сбалансирован на кончике булавки» (цитируя Википедию, которая цитирует здесь актуальную литературу). Обратите внимание, что это ВСЕ точки на рисунке, а не только среднее значение координат вершин. И именно здесь вы ошибетесь, если примете большинство ответов SO, которые подразумевают, что центроид является средним арифметическим координат x и y вершин, и примените это к реальным данным, которые вы могли бы собрать, выполнив эксперимент .
Плотность точек, описывающих вашу фигуру, может варьироваться вдоль линии вашей фигуры. Это лишь одно из многих возможных ограничений указанного метода. Тогда простое среднее значение координат, безусловно, не то, что вам нужно. Я проиллюстрирую это примером.
Пример
![Пример многоугольника](https://i.stack.imgur.com/RM3UE.jpg)
Здесь мы видим многоугольник, состоящий из 8 вершин. Наша интуиция правильно подсказывает нам, что мы могли бы сбалансировать эту форму на кончике булавки в точке (x,y)=(0,0), сделав центроид (0,0). Но в области вокруг (-1,1) плотность точек/вершин, которые нам дали для описания этого многоугольника, выше, чем в других областях вдоль линии. Теперь, если мы вычислим центроид, взяв среднее значение вершин, результат будет смещен в сторону области с высокой плотностью.
Точка «центроид поли» соответствует истинному центроиду. Эта точка была рассчитана путем реализации описанного здесь алгоритма: https://en.wikipedia.org/wiki/Centroid#Of_a_polygon (единственное отличие: возвращает абсолютное значение площади)
Применяется к фигурам, описываемым координатами x и y N вершин типа X = x_0, x_1, …, x_(N -1), то же самое для Y. Эта фигура может быть любым многоугольником, если он не является самопересекающимся, а вершины даны в порядке появления.
Это можно использовать для вычисления, например. «настоящий» центроид контурной линии matplotlib.
Код
Вот код для приведенного выше примера и реализация указанного алгоритма:
import matplotlib.pyplot as plt
def centroid_poly(X, Y):
"""https://en.wikipedia.org/wiki/Centroid#Of_a_polygon"""
N = len(X)
# minimal sanity check
if not (N == len(Y)): raise ValueError('X and Y must be same length.')
elif N < 3: raise ValueError('At least 3 vertices must be passed.')
sum_A, sum_Cx, sum_Cy = 0, 0, 0
last_iteration = N-1
# from 0 to N-1
for i in range(N):
if i != last_iteration:
shoelace = X[i]*Y[i+1] - X[i+1]*Y[i]
sum_A += shoelace
sum_Cx += (X[i] + X[i+1]) * shoelace
sum_Cy += (Y[i] + Y[i+1]) * shoelace
else:
# N-1 case (last iteration): substitute i+1 -> 0
shoelace = X[i]*Y[0] - X[0]*Y[i]
sum_A += shoelace
sum_Cx += (X[i] + X[0]) * shoelace
sum_Cy += (Y[i] + Y[0]) * shoelace
A = 0.5 * sum_A
factor = 1 / (6*A)
Cx = factor * sum_Cx
Cy = factor * sum_Cy
# returning abs of A is the only difference to
# the algo from above link
return Cx, Cy, abs(A)
# ********** example ***********
X = [-1, -0.8, -0.6, 1, 2, 1, -1, -2]
Y = [ 1, 1, 1, 1, 0.5, -1, -1, -0.5]
Cx, Cy, A = centroid_poly(X, Y)
# calculating centroid as shown by the accepted answer
Cx_accepted = sum(X)/len(X)
Cy_accepted = sum(Y)/len(Y)
fig, ax = plt.subplots()
ax.scatter(X, Y, label='vertices')
ax.scatter(Cx_accepted, Cy_accepted, label="mean of vertices")
ax.scatter(Cx, Cy, label='centroid poly')
# just so the line plot connects xy_(N-1) and xy_0
X.append(X[0]), Y.append(Y[0])
ax.plot(X, Y, label='polygon')
ax.legend(bbox_to_anchor=(1, 1))
ax.grid(), ax.set_aspect('equal')
person
XIII_
schedule
25.06.2021