


"""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''')

_lowerCAmelCase				:  Any 					=     '''K-MEANS-CLUST/ '''







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase=None    )  ->			Any:
   '''simple docstring'''





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

   # Pick K indices from range [0, N).
   _lowerCamelCase					:							Optional[int]				=			np.random.randint(0       ,						lowercase_       ,						lowercase_    )

   # 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.
   _lowerCamelCase					:							int				=			data[rand_indices, :]

   return centroids







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase    )  ->			Union[str, Any]:
   '''simple docstring'''





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







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase    )  ->			str:
   '''simple docstring'''





   _lowerCamelCase					:							Any				=			centroid_pairwise_dist(lowercase_       ,						lowercase_    )

   # Compute cluster assignments for each data point:
   # Fill in the blank (RHS only)
   _lowerCamelCase					:							List[str]				=			np.argmin(lowercase_       ,						axis=1    )

   return cluster_assignment







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase    )  ->			Any:
   '''simple docstring'''





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

   return new_centroids







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase    )  ->			Optional[int]:
   '''simple docstring'''





   _lowerCamelCase					:							Dict				=			0.0
   for i in range(lowercase_    ):
      # Select all data points that belong to cluster i. Fill in the blank (RHS only)
      _lowerCamelCase					:							List[str]				=			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)
         _lowerCamelCase					:							Union[str, Any]				=			pairwise_distances(
             lowercase_       ,						[centroids[i]]       ,						metric="euclidean"    )
         _lowerCamelCase					:							Tuple				=			distances**2
         heterogeneity += np.sum(lowercase_    )

   return heterogeneity







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase    )  ->			int:
   '''simple docstring'''





   plt.figure(figsize=(7, 4)    )
   plt.plot(lowercase_       ,						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     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase=500       ,						_lowerCamelCase=None       ,						_lowerCamelCase=False    )  ->			Any:
   '''simple docstring'''





   _lowerCamelCase					:							Optional[int]				=			initial_centroids[:]
   _lowerCamelCase					:							str				=			None

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

      # 1. Make cluster assignments using nearest centroids
      _lowerCamelCase					:							Optional[Any]				=			assign_clusters(lowercase_       ,						lowercase_    )

      # 2. Compute a new centroid for each of the k clusters, averaging all data
      #    points assigned to that cluster.
      _lowerCamelCase					:							List[Any]				=			revise_centroids(lowercase_       ,						lowercase_       ,						lowercase_    )

      # 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:
         _lowerCamelCase					:							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
         _lowerCamelCase					:							str				=			compute_heterogeneity(lowercase_       ,						lowercase_       ,						lowercase_       ,						lowercase_    )
         record_heterogeneity.append(lowercase_    )

      _lowerCamelCase					:							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

  _lowerCAmelCase				:  Union[str, Any] 					=     ds.load_iris()
  _lowerCAmelCase				:  Any 					=     3
  _lowerCAmelCase				:  Optional[int] 					=     []
  _lowerCAmelCase				:  Tuple 					=     get_initial_centroids(dataset['''data'''], k, seed=0)
  _lowerCAmelCase					,   _lowerCAmelCase				:  Optional[int] 					=     kmeans(
      dataset['''data'''],
      k,
      initial_centroids,
      maxiter=400,
      record_heterogeneity=heterogeneity,
      verbose=True,
  )
  plot_heterogeneity(heterogeneity, k)







def     lowerCamelCase_(						_lowerCamelCase       ,						_lowerCamelCase       ,						_lowerCamelCase=None    )  ->			Tuple:
   '''simple docstring'''





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


if __name__ == "__main__":
  import doctest

  doctest.testmod()
from __future__ import annotations


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int						,  lowercase_:		int ):
       assert n > 1 and a > 0 and greatest_common_divisor(lowercase_						,  lowercase_ ) == 1
       ((_UpperCamelCase)  ,					(_UpperCamelCase)  ,					(_UpperCamelCase))   :				Optional[int]       =     extended_gcd(lowercase_						,  lowercase_ )  # Implemented below
       _UpperCamelCase   :				str       =     (b * s) % n
       return x


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int ):
       ((_UpperCamelCase)  ,					(_UpperCamelCase))   :				Union[str, Any]       =     extended_euclid(lowercase_						,  lowercase_ )  # Implemented below
       if b < 0:
              _UpperCamelCase   :				Union[str, Any]       =     (b % n + n) % n
       return b


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int						,  lowercase_:		int ):
       _UpperCamelCase   :				Union[str, Any]       =     invert_modulo(lowercase_						,  lowercase_ )
       _UpperCamelCase   :				str       =     (b * s) % n
       return x


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int ):
       assert a >= 0 and b >= 0

       if b == 0:
              _UpperCamelCase  ,					_UpperCamelCase  ,					_UpperCamelCase   :				Any       =     a, 1, 0
       else:
              ((_UpperCamelCase)  ,					(_UpperCamelCase)  ,					(_UpperCamelCase))   :				Dict       =     extended_gcd(lowercase_						,  a % b )
              _UpperCamelCase   :				List[str]       =     q
              _UpperCamelCase   :				Any       =     p - q * (a // b)

       assert a % d == 0 and b % d == 0
       assert d == a * x + b * y

       return (d, x, y)


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int ):
       if b == 0:
              return (1, 0)
       ((_UpperCamelCase)  ,					(_UpperCamelCase))   :				Optional[Any]       =     extended_euclid(lowercase_						,  a % b )
       _UpperCamelCase   :				List[Any]       =     a // b
       return (y, x - k * y)


def SCREAMING_SNAKE_CASE (   lowercase_:		int						,  lowercase_:		int ):
       if a < b:
              _UpperCamelCase  ,					_UpperCamelCase   :				Optional[int]       =     b, a

       while a % b != 0:
              _UpperCamelCase  ,					_UpperCamelCase   :				Union[str, Any]       =     b, a % b

       return b


if __name__ == "__main__":
 from doctest import testmod

 testmod(name='''modular_division''', verbose=True)
 testmod(name='''modular_division2''', verbose=True)
 testmod(name='''invert_modulo''', verbose=True)
 testmod(name='''extended_gcd''', verbose=True)
 testmod(name='''extended_euclid''', verbose=True)
 testmod(name='''greatest_common_divisor''', verbose=True)



