-1
$\begingroup$

Problem Statement

The goal is to have the K-Means customer code run for clusters and not use scikit-learn libraries. Learning exercise. This K-means has the standard predict, fix, centroids, cluster means functions.

This is a data science coding programming question on creating a custom K-Means for machine learning so it is appropriate to ask here in the data science forum since resources will be keen on K-Means algorithms.

My Attempt to Fix the Problem

The TypeError unhashable appears to suggests that slicing on self.Centroids[:, k] is not working properly. I have already used a numpy arrays (np.asarray) to properly shape the array.

In the fix() function, first self attribute is casted to a numpy array before mutating the contents. The function name is has_fit(self, n_iterations) and the self.Centroids gets the self.getCentroids for the centroids in a numpy array.

Shapes

disparity_matrix.shape is (2700, 1)

Error

TypeError                                 Traceback (most recent call last)
Cell In[218], line 5
      3 disparity_matrix = np.asarray(disparity_matrix)
      4 stars_model = CustomKMeans(disparity_matrix, n_clusters=2)
----> 5 start_model_fit = stars_model.has_fit(n_iterations=300)
      7 # Get cluster labels
      8 stars = np.asarray(stars)

Cell In[217], line 37, in CustomKMeans.has_fit(self, n_iterations)
     34 _euclidian = np.array([]).reshape(self.m, 0)
     35 for k in range(self.K):
     36     # Reshape the centroid column vector to be a 2D array
---> 37     centroid_column = self.Centroids[:, k].reshape(-1, 1)
     38     _distance = np.sum((self.X - centroid_column.T) ** 2, axis=1)
     39     _euclidian = np.c_[_euclidian, _distance]

TypeError: unhashable type: 'slice'

Base Invocation Calls / Sequences

My invoke python calls for my object:

disparity_matrix = np.asarray(disparity_matrix)
stars_model = CustomKMeans(disparity_matrix, n_clusters=2)
start_model_fit = stars_model.has_fit(n_iterations=300)

My Python K-Means Code

My class code is listed:

class CustomKMeans:
    def __init__(self, X, n_clusters=8, max_iter=300, tol=1e-4):
        self.X=np.asarray(X)
        self.Cluster={}
        self.Centroids=np.array([]).reshape(self.X.shape[1],0)
        self.K=n_clusters
        self.m=self.X.shape[0]
        
    def getCentroids(self,X,K):
        i = random.randint(0,X.shape[0])
        _centroid = np.array([X[i]])
        for z in range(1,K):
            _array = np.array([]) 
            for item in X:
                _array = np.append(_array, np.min(np.sum((item-_centroid)**2)))
            _probability = _array/np.sum(_array)
            _cummulative_probability = np.cumsum(_probability)
            _random = random.random()
            _jmp = 0
            for k, l in enumerate(_cummulative_probability):
                if _random < l:
                    _jmp = k
                    break
            _centroid = np.append(_centroid,[X[i]],axis=0)
        return np.asarray(_centroid.T)

    def has_fit(self, n_iterations):
        self.Centroids = self.getCentroids(self.X, self.K)

        for n in range(n_iterations):
            _euclidian = np.array([]).reshape(self.m, 0)
            for k in range(self.K):
                # Reshape the centroid column vector to be a 2D array
                centroid_column = self.Centroids[:, k].reshape(-1, 1)
                _distance = np.sum((self.X - centroid_column.T) ** 2, axis=1)
                _euclidian = np.c_[_euclidian, _distance]
            _centroid_adj = np.argmin(_euclidian, axis=1) + 1

            # Adjust the centroids
            _centroids = {}
            for n in range(self.K):
                _centroids[n + 1] = np.array([]).reshape(self.X.shape[1], 0)

            for o in range(self.m):
                # Reshape self.X[o] to match the shape of _centroids[_centroid_adj[o]]
                reshaped_X_o = self.X[o].reshape(-1, 1)  # Use -1 to infer the size along the first axis
                _centroids[_centroid_adj[o]] = np.c_[_centroids[_centroid_adj[o]], reshaped_X_o]

            for p in range(self.K):
                _centroids[p + 1] = _centroids[p + 1].T

            for q in range(self.K):
                self.Centroids[:, q] = np.mean(_centroids[q + 1], axis=0)

            self.Centroids = _centroids
        
    def has_cluster_means(self, X):
        cluster_means = []
        for i in range(self.n_clusters):
            points = X[self.labels == i]
            if len(points) > 0:
                cluster_mean = np.mean(points, axis=0)
                cluster_means.append(cluster_mean)
        return cluster_means
    
    def has_predict(self):
        return self.Clusters,self.Centroids.T
$\endgroup$

1 Answer 1

1
$\begingroup$

The self.Centroids attribute starts by being a numpy array, which you can you index by row and column. However, later on, you overwrite self.Centroids by _centroids (last line of code in has_fit), which is a dictionary that cannot be index in the same way giving you the error.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.