0
\$\begingroup\$

I have two classes, the 1st one aims to display the Normal Distribution, and the 2nd one aims to perform K-means clustering.

The emphasis is to build classes of the mathematical methods as examples only, study material, demonstration. Visuals (plots) are very appreciated here. But it needs to be done neatly and very useful. How to improve the classes? Thanks.

The NormalDistribution can display multiple distributions at once.

The Kmeans2D can cluster the 2D points by the method .classify(n = ...) as many as n times. The implementation uses Point and Cluster class for readability.

Example implementation:

fig, ax = plt.subplots(1, 2)

model = NormalDistribution([0, 2, 3], [0.1, 0.2, 0.3])
model.plot(ax[0])

x = [random.uniform(0, 1) for i in range(100)]
y = [random.uniform(0, 1) for i in range(100)]
model2 = Kmeans2D(xp=x, yp=y, k=3)
model2.classify()
model2.plot(ax[1], colors = ['red', 'black', 'blue'], ms = 10)

fig.show()

Classes:

class NormalDistribution(object):

    def __init__(self, mu, s, bound = [-7, 7], N = 1000, name = 'Normal Distribution'):
        self.dx = (bound[1]-bound[0])/(N-1)
        self.x = [bound[0] + i*self.dx for i in range(N)]
        self.mu = mu
        self.s = s
        self.name = name

    def pdf(self, x, mu, s):
        factor = 1/sqrt(2*pi*(s**2))
        y = [ factor*e**(-((i-mu)**2)/(2*(s**2))) for i in x ]
        return y

    def cdf(self, pdf):
        y = [ self.dx*sum(pdf[0:i+1]) for i in range(len(pdf)) ]
        return y

    def plot(self, ax, func = 'pdf', color = 'blue', spectrum = None, legend = True):
        if isinstance(color, str):
            color = mplcolors.hex2color(mplcolors.cnames[color])
        for mu, s in zip(self.mu, self.s):
            if spectrum == 'mu':
                col = (color[0]*mu/max(self.mu), color[1]*mu/max(self.mu), color[2]*mu/max(self.mu), 0.8)
            elif spectrum == 's':
                col = (color[0]*s/max(self.s), color[1]*s/max(self.s), color[2]*s/max(self.s), 0.8)
            else:
                col = color
            if func=='pdf':
                ax.plot(self.x, self.pdf(self.x, mu, s), '-', color = col, lw = 1.5)
            elif func=='cdf':
                ax.plot(self.x, self.cdf(self.pdf(self.x, mu, s)), '-', color = col, lw = 1.5)
            if legend:
                ax.legend(labels = [r'$\mu$ = {}, s = {}'.format(mu, s) for mu,s in zip(self.mu, self.s)])


class Kmeans2D(object):

    class Point(object):
        def __init__(self, x, y, size = 2):
            self.x = x
            self.y = y

    class Cluster(object):
        def __init__(self, xp= None, yp= None, color = ''):
            self.x = xp 
            self.y = yp
            self.points = [Kmeans2D.Point(x, y) for x,y in zip(self.x, self.y)]
        def update(self, x, y):
            self.x.append(x)
            self.y.append(y)
            self.points.append(Kmeans2D.Point(x,y))

    def __init__(self, xp = None, yp = None, k = 2, init_centers = [], random_init = True):
        self.points = [Kmeans2D.Point(ix, iy) for ix, iy in zip(xp, yp)]
        self.np = len(xp)
        self.k = k
        if random_init:
            self.init_centers = []
            index = list(range(self.np))
            for i in range(self.k):
                rand = random.choice(index)
                self.init_centers.append(self.points[rand])
                index.remove(rand)
        else:
            self.init_centers = [Kmeans2D.Point(c[0], c[1]) for c in init_centers]

    def distance(p1, p2):
        return sqrt((p2[1]-p1[1])**2 + (p2[0]-p1[0])**2)

    def classify_once(self, centers):
        self.clusters = [Kmeans2D.Cluster(xp = [], yp = []) for i in range(self.k)]
        dum_points = self.points
        for i in dum_points:
            distances = [Kmeans2D.distance((j.x, j.y), (i.x, i.y)) for j in centers]
            shortest = min(distances)
            self.clusters[distances.index(shortest)].update(i.x, i.y)
        self.centers = [ Kmeans2D.Point( x = numpy.mean(i.x), y = numpy.mean(i.y) ) for i in self.clusters ]

    def classify(self, n=10):
        for i in range(n):
            if 'centers' in dir(self):
                self.classify_once(self.centers)
            else:
                self.classify_once(self.init_centers)

    def plot(self, ax, colors = [], ms = 5):
        for i in range(self.k):
            ax.plot(self.clusters[i].x, self.clusters[i].y, 'o', \
                    color = colors[i], markerfacecolor = (0,0,0,0), \
                    markeredgecolor = colors[i], markersize = ms)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.