faster gridf, cancel scaling
[imago.git] / src / k_means.py
1 """K-means module."""
2
3 import random
4
5 def cluster(k, d, data, i_centers=None):
6     """Find *k* clusters on *d* dimensional *data*."""
7     if i_centers:
8         old_centers = i_centers
9     else:
10         borders = [(min(p[0][i] for p in data), max(p[0][i] for p in data))
11                for i in range(d)]
12         old_centers = [[(h - l) * random.random() + l for (l, h) in borders]
13                for _ in range(k)]
14     clusters, centers = next_step(old_centers, data)
15     while delta(old_centers, centers) > 0:
16         old_centers = centers
17         clusters, centers = next_step(old_centers, data)
18
19     return clusters
20
21 def next_step(centers, data):
22     """Compute new clusters and centers."""
23     clusters = [[] for _ in centers]
24     for point in data:
25         clusters[nearest(centers, point)].append(point)
26     centers = [centroid(c) for c in clusters]
27     return clusters, centers
28
29 def nearest(centers, point):
30     """Find the nearest cluster *center* for *point*."""
31     d, i = min(((sum((p - c) ** 2 for (p, c) in zip(point[0], center)) ** 0.5 ,
32                 index) if center else (float('inf'), len(centers)))
33                for (index, center) in enumerate(centers))
34     return i
35
36 def centroid(cluster):
37     """Find the centroid of the *cluster*."""
38     # TODO is this just a mean of coordinates?
39     # TODO should we try different definitions?
40     l = float(len(cluster))
41     try:
42         d = len(cluster[0][0]) #TODO empty cluster error
43     except IndexError:
44         return None
45     return [sum(c[0][i] for c in cluster) / l for i in range(d)]
46
47 def delta(c1, c2):
48     """Find the absolute distance between two lists of points."""
49     # TODO rewrite this to a sane form
50     return sum((sum(abs(cc1 - cc2) for (cc1, cc2) in zip (ccc1, ccc2)) if ccc2
51                else 0.) for (ccc1, ccc2) in zip(c1, c2))