code: 
  
  
  
  """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 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		int		,						SCREAMING_SNAKE_CASE__			:		Dict		,						SCREAMING_SNAKE_CASE__			:		Any=None       )						->     Dict:
  
  
  
  
  
       '''simple docstring'''
  
       if seed is not None:  # useful for obtaining consistent results
            np.random.seed(SCREAMING_SNAKE_CASE__       )
       _UpperCAmelCase    :			List[str]              =					data.shape[0]  # number of data points
  
       # Pick K indices from range [0, N).
       _UpperCAmelCase    :			int              =					np.random.randint(0		,						SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       )
  
       # 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.
       _UpperCAmelCase    :			Tuple              =					data[rand_indices, :]
  
       return centroids
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		List[Any]		,						SCREAMING_SNAKE_CASE__			:		str       )						->     Dict:
  
  
  
  
  
       '''simple docstring'''
  
       return pairwise_distances(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__		,						metric="euclidean"       )
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		Optional[int]		,						SCREAMING_SNAKE_CASE__			:		Tuple       )						->     str:
  
  
  
  
  
       '''simple docstring'''
  
       _UpperCAmelCase    :			Optional[Any]              =					centroid_pairwise_dist(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       )
  
       # Compute cluster assignments for each data point:
       # Fill in the blank (RHS only)
       _UpperCAmelCase    :			Dict              =					np.argmin(SCREAMING_SNAKE_CASE__		,						axis=1       )
  
       return cluster_assignment
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		Any		,						SCREAMING_SNAKE_CASE__			:		Any		,						SCREAMING_SNAKE_CASE__			:		List[Any]       )						->     Tuple:
  
  
  
  
  
       '''simple docstring'''
  
       _UpperCAmelCase    :			Optional[int]              =					[]
       for i in range(SCREAMING_SNAKE_CASE__       ):
            # Select all data points that belong to cluster i. Fill in the blank (RHS only)
            _UpperCAmelCase    :			Dict              =					data[cluster_assignment == i]
            # Compute the mean of the data points. Fill in the blank (RHS only)
            _UpperCAmelCase    :			List[str]              =					member_data_points.mean(axis=0       )
            new_centroids.append(SCREAMING_SNAKE_CASE__       )
       _UpperCAmelCase    :			Optional[int]              =					np.array(SCREAMING_SNAKE_CASE__       )
  
       return new_centroids
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		Tuple		,						SCREAMING_SNAKE_CASE__			:		Union[str, Any]		,						SCREAMING_SNAKE_CASE__			:		Any		,						SCREAMING_SNAKE_CASE__			:		Optional[int]       )						->     str:
  
  
  
  
  
       '''simple docstring'''
  
       _UpperCAmelCase    :			Optional[int]              =					0.0
       for i in range(SCREAMING_SNAKE_CASE__       ):
            # Select all data points that belong to cluster i. Fill in the blank (RHS only)
            _UpperCAmelCase    :			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)
                 _UpperCAmelCase    :			Any              =					pairwise_distances(
                     SCREAMING_SNAKE_CASE__		,						[centroids[i]]		,						metric="euclidean"       )
                 _UpperCAmelCase    :			Dict              =					distances**2
                 heterogeneity += np.sum(SCREAMING_SNAKE_CASE__       )
  
       return heterogeneity
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		Optional[int]		,						SCREAMING_SNAKE_CASE__			:		Dict       )						->     str:
  
  
  
  
  
       '''simple docstring'''
  
       plt.figure(figsize=(7, 4)       )
       plt.plot(SCREAMING_SNAKE_CASE__		,						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 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		int		,						SCREAMING_SNAKE_CASE__			:		Optional[int]		,						SCREAMING_SNAKE_CASE__			:		List[str]		,						SCREAMING_SNAKE_CASE__			:		List[str]=500		,						SCREAMING_SNAKE_CASE__			:		Dict=None		,						SCREAMING_SNAKE_CASE__			:		int=False       )						->     Optional[Any]:
  
  
  
  
  
       '''simple docstring'''
  
       _UpperCAmelCase    :			str              =					initial_centroids[:]
       _UpperCAmelCase    :			Tuple              =					None
  
       for itr in range(SCREAMING_SNAKE_CASE__       ):
            if verbose:
                 print(SCREAMING_SNAKE_CASE__		,						end=""       )
  
            # 1. Make cluster assignments using nearest centroids
            _UpperCAmelCase    :			int              =					assign_clusters(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       )
  
            # 2. Compute a new centroid for each of the k clusters, averaging all data
            #    points assigned to that cluster.
            _UpperCAmelCase    :			Optional[Any]              =					revise_centroids(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       )
  
            # 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:
                 _UpperCAmelCase    :			Dict              =					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
                 _UpperCAmelCase    :			Optional[Any]              =					compute_heterogeneity(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       )
                 record_heterogeneity.append(SCREAMING_SNAKE_CASE__       )
  
            _UpperCAmelCase    :			str              =					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       :      Optional[Any]							=							ds.load_iris()
   _lowerCAmelCase       :      str							=							3
   _lowerCAmelCase       :      List[str]							=							[]
   _lowerCAmelCase       :      List[Any]							=							get_initial_centroids(dataset["data"], k, seed=0)
   _lowerCAmelCase       :      Union[str, Any]							=							kmeans(
       dataset["data"],
       k,
       initial_centroids,
       maxiter=4_00,
       record_heterogeneity=heterogeneity,
       verbose=True,
   )
   plot_heterogeneity(heterogeneity, k)
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		pd.DataFrame		,						SCREAMING_SNAKE_CASE__			:		np.ndarray		,						SCREAMING_SNAKE_CASE__			:		Union[str, Any]=None       )						->     pd.DataFrame:
  
  
  
  
  
       '''simple docstring'''
  
       if fill_missing_report:
            _UpperCAmelCase    :			Dict              =					df.fillna(value=SCREAMING_SNAKE_CASE__       )
       _UpperCAmelCase    :			Optional[Any]              =					1
       _UpperCAmelCase    :			Optional[Any]              =					df.select_dtypes(np.number       ).columns
       _UpperCAmelCase    :			str              =					(
           df.groupby(["Cluster"]       )[  # construct report dataframe
               numeric_cols
           ]  # group by cluster number
           .agg(
               [
                   ("sum", np.sum),
                   ("mean_with_zeros", lambda SCREAMING_SNAKE_CASE__       : np.mean(np.nan_to_num(SCREAMING_SNAKE_CASE__       )       )),
                   ("mean_without_zeros", lambda SCREAMING_SNAKE_CASE__       : x.replace(0		,						np.NaN       ).mean()),
                   (
                       "mean_25-75",
                       lambda SCREAMING_SNAKE_CASE__       : np.mean(
                           np.nan_to_num(
                               sorted(SCREAMING_SNAKE_CASE__       )[
                                   round(len(SCREAMING_SNAKE_CASE__       ) * 25 / 100       ) : round(len(SCREAMING_SNAKE_CASE__       ) * 75 / 100       )
                               ]       )       ),
                   ),
                   ("mean_with_na", np.mean),
                   ("min", lambda SCREAMING_SNAKE_CASE__       : x.min()),
                   ("5%", lambda SCREAMING_SNAKE_CASE__       : x.quantile(0.05       )),
                   ("25%", lambda SCREAMING_SNAKE_CASE__       : x.quantile(0.25       )),
                   ("50%", lambda SCREAMING_SNAKE_CASE__       : x.quantile(0.50       )),
                   ("75%", lambda SCREAMING_SNAKE_CASE__       : x.quantile(0.75       )),
                   ("95%", lambda SCREAMING_SNAKE_CASE__       : x.quantile(0.95       )),
                   ("max", lambda SCREAMING_SNAKE_CASE__       : x.max()),
                   ("count", lambda SCREAMING_SNAKE_CASE__       : x.count()),
                   ("stdev", lambda SCREAMING_SNAKE_CASE__       : x.std()),
                   ("mode", lambda SCREAMING_SNAKE_CASE__       : x.mode()[0]),
                   ("median", lambda SCREAMING_SNAKE_CASE__       : x.median()),
                   ("# > 0", lambda SCREAMING_SNAKE_CASE__       : (x > 0).sum()),
               ]       )
           .T.reset_index()
           .rename(index=SCREAMING_SNAKE_CASE__		,						columns={"level_0": "Features", "level_1": "Type"}       )
       )  # rename columns
       # calculate the size of cluster(count of clientID's)
       _UpperCAmelCase    :			Tuple              =					report[
           (report["Features"] == "dummy") & (report["Type"] == "count")
       ].copy()  # avoid SettingWithCopyWarning
       _UpperCAmelCase    :			Optional[Any]              =					(
           "ClusterSize"  # rename created cluster df to match report column names
       )
       _UpperCAmelCase    :			int              =					"# of Customers"
       _UpperCAmelCase    :			Union[str, Any]              =					pd.DataFrame(
           clustersize.iloc[:, 2:].values
           / clustersize.iloc[:, 2:].values.sum()       )
       _UpperCAmelCase    :			List[Any]              =					"% of Customers"  # rename created cluster df to match report column names
       _UpperCAmelCase    :			str              =					"ClusterProportion"
       _UpperCAmelCase    :			int              =					clusterproportion.columns.tolist()
       _UpperCAmelCase    :			int              =					cols[-2:] + cols[:-2]
       _UpperCAmelCase    :			Any              =					clusterproportion[cols]  # rearrange columns to match report
       _UpperCAmelCase    :			List[str]              =					report.columns
       _UpperCAmelCase    :			List[str]              =					pd.DataFrame(
           abs(
               report[report["Type"] == "count"].iloc[:, 2:].values
               - clustersize.iloc[:, 2:].values       )       )  # generating df with count of nan values
       _UpperCAmelCase    :			Union[str, Any]              =					0
       _UpperCAmelCase    :			Optional[Any]              =					"# of nan"
       _UpperCAmelCase    :			List[str]              =					report[
           report["Type"] == "count"
       ].Features.tolist()  # filling values in order to match report
       _UpperCAmelCase    :			Optional[Any]              =					a.columns.tolist()
       _UpperCAmelCase    :			List[Any]              =					cols[-2:] + cols[:-2]
       _UpperCAmelCase    :			str              =					a[cols]  # rearrange columns to match report
       _UpperCAmelCase    :			Optional[Any]              =					report.columns  # rename columns to match report
       _UpperCAmelCase    :			Optional[Any]              =					report.drop(
           report[report.Type == "count"].index       )  # drop count values except cluster size
       _UpperCAmelCase    :			Union[str, Any]              =					pd.concat(
           [report, a, clustersize, clusterproportion]		,						axis=0       )  # concat report with clustert size and nan values
       _UpperCAmelCase    :			str              =					report["Features"].isin(SCREAMING_SNAKE_CASE__       )
       _UpperCAmelCase    :			List[str]              =					report.columns.tolist()
       _UpperCAmelCase    :			List[Any]              =					cols[0:2] + cols[-1:] + cols[2:-1]
       _UpperCAmelCase    :			Any              =					report[cols]
       _UpperCAmelCase    :			Tuple              =					{
           "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,
       }
       _UpperCAmelCase    :			Any              =					(
           report.assign(
               Sortera=lambda SCREAMING_SNAKE_CASE__       : x.Type.map(SCREAMING_SNAKE_CASE__       )		,						Sortera=lambda SCREAMING_SNAKE_CASE__       : list(reversed(range(len(SCREAMING_SNAKE_CASE__       )       )       )       )		,						)
           .sort_values(["Sorter1", "Mark", "Sorter2"]		,						ascending=SCREAMING_SNAKE_CASE__       )
           .drop(["Sorter1", "Sorter2"]		,						axis=1       )
       )
       _UpperCAmelCase    :			Optional[Any]              =					""
       _UpperCAmelCase    :			List[str]              =					report.reset_index()
       _UpperCAmelCase    :			Optional[int]              =					report.drop(columns=["index"]       )
       return report
  
  
  if __name__ == "__main__":
   import doctest
  
   doctest.testmod()
  
style_context: 
  
  
  
  """simple docstring"""
  
  
  
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		list       )						->     bool:
  
  
  
  
  
       '''simple docstring'''
  
       if not isinstance(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       ):
            raise ValueError("Input series is not valid, valid series - [1, 2/3, 2]"       )
       if len(SCREAMING_SNAKE_CASE__       ) == 0:
            raise ValueError("Input list must be a non empty list"       )
       if len(SCREAMING_SNAKE_CASE__       ) == 1 and series[0] != 0:
            return True
       _UpperCAmelCase    :			Tuple              =					[]
       _UpperCAmelCase    :			Any              =					len(SCREAMING_SNAKE_CASE__       )
       for i in range(0		,						SCREAMING_SNAKE_CASE__       ):
            if series[i] == 0:
                 raise ValueError("Input series cannot have 0 as an element"       )
            rec_series.append(1 / series[i]       )
       _UpperCAmelCase    :			Dict              =					rec_series[1] - rec_series[0]
       for index in range(2		,						SCREAMING_SNAKE_CASE__       ):
            if rec_series[index] - rec_series[index - 1] != common_diff:
                 return False
       return True
  
  def 		__snake_case		(   SCREAMING_SNAKE_CASE__			:		list       )						->     float:
  
  
  
  
  
       '''simple docstring'''
  
       if not isinstance(SCREAMING_SNAKE_CASE__		,						SCREAMING_SNAKE_CASE__       ):
            raise ValueError("Input series is not valid, valid series - [2, 4, 6]"       )
       if len(SCREAMING_SNAKE_CASE__       ) == 0:
            raise ValueError("Input list must be a non empty list"       )
       _UpperCAmelCase    :			Any              =					0
       for val in series:
            answer += 1 / val
       return len(SCREAMING_SNAKE_CASE__       ) / answer
  
  
  if __name__ == "__main__":
   import doctest
  
   doctest.testmod()
  