37

I would like to get the center point(x,y) of a figure created by a set of points.

How do I do this?

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
Dominik Szopa
  • 1,909
  • 1
  • 15
  • 16
  • 7
    Define "center". Center of gravity? Centroid? Something else? – Karl Knechtel Dec 04 '10 at 21:22
  • This is more like a math related question. I think in this exellent book: http://www.openbookproject.net/thinkcs/ I dont remember if in python or C++, there are some examples of what you are trying to achieve. – mRt Dec 04 '10 at 21:28

4 Answers4

46

If you mean centroid, you just get the average of all the points.

x = [p[0] for p in points]
y = [p[1] for p in points]
centroid = (sum(x) / len(points), sum(y) / len(points))
Colin
  • 10,447
  • 11
  • 46
  • 54
  • 4
    But be careful with integer division in Python 2.x: if every point has an integer x value, the x value of your centroid will be rounded down to an integer. Use `from __future__ import division`, explicitly convert to a float before division, or use Python 3. – Thomas K Dec 04 '10 at 21:33
  • 12
    If `points` is a two-dimensional Numpy array, you can probably just use `points.mean(0)`. – Philipp Dec 04 '10 at 21:53
  • 10
    that is not the centroid, is just the average of the points. If you want to compute the centroid, you have to use Green's theorem for discrete segments, as in https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon – chuse Oct 15 '15 at 13:39
15

If the set of points is a numpy array positions of sizes N x 2, then the centroid is simply given by:

centroid = positions.mean(axis=0)

It will directly give you the 2 coordinates a a numpy array.

In general, numpy arrays can be used for all these measures in a vectorized way, which is compact and very quick compared to for loops.

meduz
  • 3,903
  • 1
  • 28
  • 40
10

I assume that a point is a tuple like (x,y), so you can use zip to join the x's and y's. Then using the min and max of x and y's, you can determine the center point.

x,y=zip(*points)
center=(max(x)+min(x))/2., (max(y)+min(y))/2.

Sample output

Points in an array : [(411, 148), (304, 148), (357, 241)]
x:(411, 304, 357)
y:(148, 148, 241)
center: (357.5, 194.5)
nycynik
  • 7,371
  • 8
  • 62
  • 87
Kabie
  • 10,489
  • 1
  • 38
  • 45
  • Shouldn't that be max + min, not max - min? – Thomas K Dec 04 '10 at 21:54
  • trying to understand what this is doing... why do we 'add' the min to the max? The answer from @colin makes sense to me, but wasn't sure why this works too. – Futile32 Feb 22 '15 at 04:48
  • you are using min max instead of subtraction and addition. As an example, if min was 10 and max was 40 - min is 10 and max is 40, so that is 50/2=25. You can arrive at the same answer with 10 + ((40-10)/2) - both work perfectly well. – nycynik Sep 12 '20 at 03:23
  • 2
    Just another note: This center and the other answer are not the same center - for polygons there are multiple "center" formulas https://en.wikipedia.org/wiki/Centroid – nycynik Sep 12 '20 at 19:38
3

In this case the average of the points isn't the centroid. Generally speaking the center of area is the first moment of area. So you have to calculate the areas of the polygons that define the shape of your figure, then compute the first moment of area for each axis: sum((r_i * A_i), for i in range(N))/sum(A_i). So we can have a set of points lying on the contour of the figure:

Contents of data.dat:

x,y
0.99159,0.00467
0.97822,0.00828
0.96383,0.01237
0.94834,0.01703
0.93166,0.02231
0.91374,0.02816
0.89456,0.03443
0.87415,0.04092
0.85265,0.04755
0.83029,0.05426
0.80736,0.06099
0.78414,0.06766
0.76087,0.07423
0.73768,0.08064
0.71456,0.08687
0.69143,0.09294
0.6681,0.09886
0.64446,0.10469
0.62058,0.11041
0.59684,0.11598
0.57378,0.12127
0.55182,0.12613
0.53101,0.13048
0.51113,0.13432
0.49187,0.13766
0.47287,0.14054
0.4538,0.14301
0.43445,0.14514
0.4148,0.14694
0.39496,0.14844
0.37527,0.14964
0.3561,0.15051
0.33766,0.151
0.31999,0.15111
0.303,0.15081
0.28655,0.15011
0.27048,0.149
0.25467,0.14748
0.23907,0.14558
0.22372,0.14331
0.20869,0.14071
0.19411,0.13782
0.1801,0.1347
0.16677,0.13138
0.1542,0.12788
0.1424,0.12422
0.13136,0.12042
0.12106,0.1165
0.11142,0.11244
0.1024,0.10826
0.09391,0.10393
0.08588,0.09944
0.07824,0.09486
0.07098,0.0903
0.06412,0.08592
0.05772,0.08177
0.05182,0.07782
0.04641,0.07403
0.04142,0.07034
0.03683,0.06673
0.03258,0.0632
0.02864,0.05975
0.025,0.05637
0.02164,0.05307
0.01853,0.04985
0.01567,0.04669
0.01307,0.04357
0.01073,0.04047
0.00864,0.03735
0.00679,0.03423
0.00517,0.0311
0.00377,0.02798
0.00258,0.02487
0.00158,0.02177
0.00078,0.0187
0.00017,0.01565
-0.00025,0.01262
-0.00049,0.00962
-0.00055,0.00663
-0.00042,0.00367
-9.00E-05,0.00073
0.00043,-0.00218
0.00114,-0.00508
0.00206,-0.00793
0.00316,-0.01073
0.00447,-0.01346
0.00599,-0.0161
0.00772,-0.01865
0.00968,-0.02106
0.01188,-0.02333
0.01435,-0.02541
0.01711,-0.02728
0.02016,-0.02894
0.02351,-0.03043
0.02714,-0.03179
0.03101,-0.03309
0.03514,-0.03434
0.03955,-0.03555
0.04429,-0.03675
0.04937,-0.03795
0.05483,-0.03918
0.06069,-0.04048
0.06697,-0.0418
0.07372,-0.04312
0.081,-0.04436
0.0889,-0.0455
0.0976,-0.04653
0.10725,-0.04749
0.11801,-0.0484
0.13002,-0.04926
0.1434,-0.05007
0.15815,-0.05085
0.17419,-0.05161
0.19136,-0.05227
0.20955,-0.05274
0.22884,-0.05293
0.2494,-0.05286
0.27136,-0.05256
0.29473,-0.05206
0.3192,-0.05146
0.3442,-0.05081
0.36921,-0.05008
0.39401,-0.04924
0.41866,-0.04826
0.44342,-0.04713
0.46845,-0.04586
0.49376,-0.04447
0.51919,-0.04299
0.54445,-0.04142
0.56941,-0.03976
0.5941,-0.03796
0.61869,-0.036
0.64343,-0.03387
0.66852,-0.03162
0.69403,-0.0293
0.7199,-0.02698
0.74597,-0.02469
0.77207,-0.02242
0.79809,-0.02016
0.82397,-0.0179
0.84958,-0.01566
0.8746,-0.01347
0.8986,-0.01136
0.92114,-0.00939
0.94183,-0.00758
0.96037,-0.00596
0.97676,-0.00453
0.99124,-0.00326
1,-0.0025

Code: Calculate the first moment of area

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_table('data.dat',delim_whitespace=True,skiprows=[0],names=['x','y'],index_col=False)
x = data.x.to_numpy() 
y = data.y.to_numpy()

N = range(len(data)-1)
M = np.array([(x[i]-x[i+1])*(y[i]+y[i+1])/2 for i in N]) #Area of each trapezoid
My = np.array([(x[i]+x[i+1])/2 for i in N])*M #Moment of area (area*distance to centroid) with respect to the Y axis of each trapezoid
Mx = np.array([(y[i]+y[i+1])/4 for i in N])*M #Moment of area (area*distance to centroid) with respect to the X axis of each trapezoid
X = sum(My)/sum(M)
Y = sum(Mx)/sum(M)

centroid = [X , Y]

points_ave = data.mean(axis=0)

plt.plot(data.x, data.y, 'r',marker='.',markeredgecolor='black', markersize=3)
plt.plot(*centroid, 'blue', marker='o',markeredgecolor='black', markersize=7)
plt.plot(*points_ave, 'green', marker='o',markeredgecolor='black', markersize=7)
plt.axis('equal')
plt.xlim((-0.05, 1.05))
plt.legend(['GOE 383 AIRFOIL','Centroid','Average of points'])

In the following image you can very clearly see how the non-uniform point sampling skews the results. The average of points is only useful for point masses or concentrated properties.

Figure Centroid

Michel G
  • 116
  • 6