def __train(self, n_clusters=4):
        """
            Calculate cluster's centroids and standard deviations. If there are at least the number of threshold rows \
            then:

            * Observations will be normalised.

            * Standard deviations will be returned.

            * Clusters will be returned.

            * Centroids are ordered based on their distance from an arbitrary -100, -100 point.

            If there are not enough Observations, then centroids and standard deviations will be set to the empty list.

            General strategy: Use numpy.array for calculations. Keep everything in float. Convert arrays back to lists \
            at the end.

            :param n_clusters: the number of clusters
            :type n_clusters: int
        """

        try:
            for obs in self.observations:
                features, ids = self.__get_features_for_observation(observation=obs, last_column_is_id=True)
                # the last column is the observation id
                normalised_data = whiten(features)

                # skip any rows that contain just zero values... they create nans
                first_safe_row = pdkit.utils.non_zero_index(normalised_data)
                observation_ids = features.tolist()
                sd = features[first_safe_row] / normalised_data[first_safe_row]

                # Calculate centroids and sort result
                centroids_array, _ = kmeans(normalised_data, n_clusters)
                sorted_centroids = pdkit.utils.centroid_sort(centroids_array)

                if not self.clusters:
                    self.clusters = [[obs, sd.tolist(), sorted_centroids.tolist()]]
                else:
                    self.clusters.append([obs, sd.tolist(),sorted_centroids.tolist()])
        except IOError as e:
            ierr = "({}): {}".format(e.errno, e.strerror)
            logging.error("Error training UPDRS, file not found, I/O error %s", ierr)

        except ValueError as verr:
            logging.error("Error training UPDRS ValueError ->%s", verr.message)

        except:
            logging.error("Unexpected error on training UPDRS init: %s", sys.exc_info()[0])
Calculate cluster's centroids and standard deviations. If there are at least the number of threshold rows \
            then:

            * Observations will be normalised.

            * Standard deviations will be returned.

            * Clusters will be returned.

            * Centroids are ordered based on their distance from an arbitrary -100, -100 point.

            If there are not enough Observations, then centroids and standard deviations will be set to the empty list.

            General strategy: Use numpy.array for calculations. Keep everything in float. Convert arrays back to lists \
            at the end.

            :param n_clusters: the number of clusters
            :type n_clusters: int