code: def kmeans(
      x: np.ndarray,
      n_clusters: int = 1,
      max_iterations: int = 100,
      tol: float = 1e-4,
  ) -> np.ndarray:
      """Performs the K-Means clustering over the input data.
  
      Args:
          x: Input array with a shape equal to (n_samples, n_variables, n_dimensions).
          n_clusters: Number of clusters.
          max_iterations: Maximum number of clustering iterations.
          tol: Tolerance value to stop the clustering.
  
      Returns:
          (np.ndarray): An array holding the assigned cluster per input sample.
  
      """
  
      n_samples, n_variables, n_dimensions = (
          x.shape[0],
          x.shape[1],
          x.shape[2],
      )
  
      centroids = np.zeros((n_clusters, n_variables, n_dimensions))
      labels = np.zeros(n_samples)
  
      for i in range(n_clusters):
          idx = r.generate_integer_random_number(0, n_samples)
          centroids[i] = x[idx]
  
      for _ in range(max_iterations):
          dists = np.squeeze(
              np.array(
                  [np.linalg.norm(x - c, axis=1) for c in centroids]
              )
          )
          updated_labels = np.squeeze(
              np.array(np.argmin(dists, axis=0))
          )
  
          ratio = np.sum(labels != updated_labels) / n_samples
          if ratio <= tol:
              break
  
          labels = updated_labels
  
          for i in range(n_clusters):
              centroid_samples = x[labels == i]
              if centroid_samples.shape[0] > 0:
                  centroids[i] = np.mean(centroid_samples, axis=0)
  
      return labels
  
file_path: opytimizer/opytimizer/math/general.py
imports: from itertools import islice
  from typing import Any, Iterable, List, Optional
  import numpy
  import opytimizer.math.random
label_desc: Use image node_id 2 for calling a global function with example usage: kmeans(x, n_clusters, max_iterations, tol) and returns: labels