


"""simple docstring"""







import warnings

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.metrics import pairwise_distances

warnings.filterwarnings("""ignore""")

__lowercase          = """K-MEANS-CLUST/ """


def 	lowercase     (							A_    ,  A_    ,  A_=None			)-> str:
  '''simple docstring'''







  if seed is not None:  # useful for obtaining consistent results
    np.random.seed(A_			)
  a		:						List[str] 				=     data.shape[0]  # number of data points

  # Pick K indices from range [0, N).
  a		:						List[Any] 				=     np.random.randint(0    ,  A_    ,  A_			)

  # Keep centroids as dense format, as many entries will be nonzero due to averaging.
  # As long as at least one document in a cluster contains a word,
  # it will carry a nonzero weight in the TF-IDF vector of the centroid.
  a		:						Union[str, Any] 				=     data[rand_indices, :]

  return centroids


def 	lowercase     (							A_    ,  A_			)-> str:
  '''simple docstring'''







  return pairwise_distances(A_    ,  A_    ,  metric="euclidean"			)


def 	lowercase     (							A_    ,  A_			)-> List[str]:
  '''simple docstring'''







  a		:						Any 				=     centroid_pairwise_dist(A_    ,  A_			)

  # Compute cluster assignments for each data point:
  # Fill in the blank (RHS only)
  a		:						str 				=     np.argmin(A_    ,  axis=1			)

  return cluster_assignment


def 	lowercase     (							A_    ,  A_    ,  A_			)-> List[Any]:
  '''simple docstring'''







  a		:						str 				=     []
  for i in range(A_			):
    # Select all data points that belong to cluster i. Fill in the blank (RHS only)
    a		:						str 				=     data[cluster_assignment == i]
    # Compute the mean of the data points. Fill in the blank (RHS only)
    a		:						int 				=     member_data_points.mean(axis=0			)
    new_centroids.append(A_			)
  a		:						str 				=     np.array(A_			)

  return new_centroids


def 	lowercase     (							A_    ,  A_    ,  A_    ,  A_			)-> List[str]:
  '''simple docstring'''







  a		:						Tuple 				=     0.0
  for i in range(A_			):
    # Select all data points that belong to cluster i. Fill in the blank (RHS only)
    a		:						Any 				=     data[cluster_assignment == i, :]

    if member_data_points.shape[0] > 0:  # check if i-th cluster is non-empty
      # Compute distances from centroid to data points (RHS only)
      a		:						List[str] 				=     pairwise_distances(
          A_    ,  [centroids[i]]    ,  metric="euclidean"			)
      a		:						List[Any] 				=     distances**2
      heterogeneity += np.sum(A_			)

  return heterogeneity


def 	lowercase     (							A_    ,  A_			)-> List[str]:
  '''simple docstring'''







  plt.figure(figsize=(7, 4)			)
  plt.plot(A_    ,  linewidth=4			)
  plt.xlabel("# Iterations"			)
  plt.ylabel("Heterogeneity"			)
  plt.title(F'''Heterogeneity of clustering over time, K={k:d}'''			)
  plt.rcParams.update({"font.size": 16}			)
  plt.show()


def 	lowercase     (							A_    ,  A_    ,  A_    ,  A_=500    ,  A_=None    ,  A_=False			)-> int:
  '''simple docstring'''







  a		:						List[str] 				=     initial_centroids[:]
  a		:						str 				=     None

  for itr in range(A_			):
    if verbose:
      print(A_    ,  end=""			)

    # 1. Make cluster assignments using nearest centroids
    a		:						Any 				=     assign_clusters(A_    ,  A_			)

    # 2. Compute a new centroid for each of the k clusters, averaging all data
    #    points assigned to that cluster.
    a		:						int 				=     revise_centroids(A_    ,  A_    ,  A_			)

    # Check for convergence: if none of the assignments changed, stop
    if (
        prev_cluster_assignment is not None
        and (prev_cluster_assignment == cluster_assignment).all()
    ):
      break

    # Print number of new assignments
    if prev_cluster_assignment is not None:
      a		:						List[Any] 				=     np.sum(prev_cluster_assignment != cluster_assignment			)
      if verbose:
        print(
            F'''    {num_changed:5d} elements changed their cluster assignment.'''			)

        # Record heterogeneity convergence metric
    if record_heterogeneity is not None:
      # YOUR CODE HERE
      a		:						int 				=     compute_heterogeneity(A_    ,  A_    ,  A_    ,  A_			)
      record_heterogeneity.append(A_			)

    a		:						Optional[int] 				=     cluster_assignment[:]

  return centroids, cluster_assignment


# Mock test below
if False:  # change to true to run this test case.
 from sklearn import datasets as ds

 __lowercase          = ds.load_iris()
 __lowercase          = 3
 __lowercase          = []
 __lowercase          = get_initial_centroids(dataset["""data"""], k, seed=0)
 __lowercase   ,				__lowercase          = kmeans(
     dataset["""data"""],
     k,
     initial_centroids,
     maxiter=400,
     record_heterogeneity=heterogeneity,
     verbose=True,
 )
 plot_heterogeneity(heterogeneity, k)


def 	lowercase     (							A_    ,  A_    ,  A_=None			)-> pd.DataFrame:
  '''simple docstring'''







  if fill_missing_report:
    a		:						Union[str, Any] 				=     df.fillna(value=A_			)
  a		:						List[Any] 				=     1
  a		:						str 				=     df.select_dtypes(np.number			).columns
  a		:						Tuple 				=     (
      df.groupby(["Cluster"]			)[  # construct report dataframe
          numeric_cols
      ]  # group by cluster number
      .agg(
          [
              ("sum", np.sum),
              ("mean_with_zeros", lambda A_			: np.mean(np.nan_to_num(A_			)			)),
              ("mean_without_zeros", lambda A_			: x.replace(0    ,  np.NaN			).mean()),
              (
                  "mean_25-75",
                  lambda A_			: np.mean(
                      np.nan_to_num(
                          sorted(A_			)[
                              round(len(A_			) * 25 / 100			) : round(len(A_			) * 75 / 100			)
                          ]			)			),
              ),
              ("mean_with_na", np.mean),
              ("min", lambda A_			: x.min()),
              ("5%", lambda A_			: x.quantile(0.0_5			)),
              ("25%", lambda A_			: x.quantile(0.2_5			)),
              ("50%", lambda A_			: x.quantile(0.5_0			)),
              ("75%", lambda A_			: x.quantile(0.7_5			)),
              ("95%", lambda A_			: x.quantile(0.9_5			)),
              ("max", lambda A_			: x.max()),
              ("count", lambda A_			: x.count()),
              ("stdev", lambda A_			: x.std()),
              ("mode", lambda A_			: x.mode()[0]),
              ("median", lambda A_			: x.median()),
              ("# > 0", lambda A_			: (x > 0).sum()),
          ]			)
      .T.reset_index()
      .rename(index=A_    ,  columns={"level_0": "Features", "level_1": "Type"}			)
  )  # rename columns
  # calculate the size of cluster(count of clientID's)
  a		:						Union[str, Any] 				=     report[
      (report["Features"] == "dummy") & (report["Type"] == "count")
  ].copy()  # avoid SettingWithCopyWarning
  a		:						Dict 				=     (
      "ClusterSize"  # rename created cluster df to match report column names
  )
  a		:						Dict 				=     "# of Customers"
  a		:						Optional[int] 				=     pd.DataFrame(
      clustersize.iloc[:, 2:].values
      / clustersize.iloc[:, 2:].values.sum()			)
  a		:						List[Any] 				=     "% of Customers"  # rename created cluster df to match report column names
  a		:						List[Any] 				=     "ClusterProportion"
  a		:						Union[str, Any] 				=     clusterproportion.columns.tolist()
  a		:						Optional[Any] 				=     cols[-2:] + cols[:-2]
  a		:						List[Any] 				=     clusterproportion[cols]  # rearrange columns to match report
  a		:						List[Any] 				=     report.columns
  a		:						Optional[int] 				=     pd.DataFrame(
      abs(
          report[report["Type"] == "count"].iloc[:, 2:].values
          - clustersize.iloc[:, 2:].values			)			)  # generating df with count of nan values
  a		:						Optional[Any] 				=     0
  a		:						Any 				=     "# of nan"
  a		:						Any 				=     report[
      report["Type"] == "count"
  ].Features.tolist()  # filling values in order to match report
  a		:						Optional[Any] 				=     a.columns.tolist()
  a		:						Any 				=     cols[-2:] + cols[:-2]
  a		:						List[str] 				=     a[cols]  # rearrange columns to match report
  a		:						Dict 				=     report.columns  # rename columns to match report
  a		:						Any 				=     report.drop(
      report[report.Type == "count"].index			)  # drop count values except cluster size
  a		:						int 				=     pd.concat(
      [report, a, clustersize, clusterproportion]    ,  axis=0			)  # concat report with clustert size and nan values
  a		:						Optional[int] 				=     report["Features"].isin(A_			)
  a		:						List[str] 				=     report.columns.tolist()
  a		:						List[Any] 				=     cols[0:2] + cols[-1:] + cols[2:-1]
  a		:						Union[str, Any] 				=     report[cols]
  a		:						Optional[Any] 				=     {
      "ClusterSize": 9,
      "ClusterProportion": 8,
      "mean_with_zeros": 7,
      "mean_with_na": 6,
      "max": 5,
      "50%": 4,
      "min": 3,
      "25%": 2,
      "75%": 1,
      "# of nan": 0,
      "# > 0": -1,
      "sum_with_na": -2,
  }
  a		:						Union[str, Any] 				=     (
      report.assign(
          Sortera=lambda A_			: x.Type.map(A_			)    ,  Sortera=lambda A_			: list(reversed(range(len(A_			)			)			)			)    ,  )
      .sort_values(["Sorter1", "Mark", "Sorter2"]    ,  ascending=A_			)
      .drop(["Sorter1", "Sorter2"]    ,  axis=1			)
  )
  a		:						Union[str, Any] 				=     ""
  a		:						Dict 				=     report.reset_index()
  a		:						Tuple 				=     report.drop(columns=["index"]			)
  return report


if __name__ == "__main__":
 import doctest

 doctest.testmod()










"""simple docstring"""







class 		_A    :







    """simple docstring"""




    def __init__(   self							:       Any):
      a		:						Tuple 				=     [0]
      a		:						str 				=     0






    def       __snake_case				(   self							:       Optional[Any]							,					__UpperCAmelCase							:       int):
      a		:						Any 				=     self.__heap[i]
      while i // 2 > 0:
        if self.__heap[i] > self.__heap[i // 2]:
          a		:						Tuple 				=     self.__heap[i // 2]
          a		:						Any 				=     temporary
        i //= 2






    def       __snake_case				(   self							:       Dict							,					__UpperCAmelCase							:       int):
      self.__heap.append(__UpperCAmelCase)
      self.__size += 1
      self.__swap_up(self.__size)






    def       __snake_case				(   self							:       str							,					__UpperCAmelCase							:       int):
      while self.__size >= 2 * i:
        if 2 * i + 1 > self.__size:
          a		:						List[Any] 				=     2 * i
        else:
          if self.__heap[2 * i] > self.__heap[2 * i + 1]:
            a		:						Tuple 				=     2 * i
          else:
            a		:						str 				=     2 * i + 1
        a		:						Optional[int] 				=     self.__heap[i]
        if self.__heap[i] < self.__heap[bigger_child]:
          a		:						Any 				=     self.__heap[bigger_child]
          a		:						Union[str, Any] 				=     temporary
        a		:						Dict 				=     bigger_child






    def       __snake_case				(   self							:       List[str]):
      a		:						int 				=     self.__heap[1]
      a		:						Tuple 				=     self.__heap[self.__size]
      self.__size -= 1
      self.__heap.pop()
      self.__swap_down(1)
      return max_value






    @property
    def       __snake_case				(   self							:       List[str]):
      return self.__heap[1:]






    def __len__(   self							:       Optional[int]):
      return self.__size


if __name__ == "__main__":
 import doctest

 doctest.testmod()
 # create an instance of BinaryHeap
 __lowercase          = BinaryHeap()
 binary_heap.insert(6)
 binary_heap.insert(10)
 binary_heap.insert(15)
 binary_heap.insert(12)
 # pop root(max-values because it is max heap)
 print(binary_heap.pop())  # 15
 print(binary_heap.pop())  # 12
 # get the list and size after operations
 print(binary_heap.get_list)
 print(len(binary_heap))






