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

_A            = 'K-MEANS-CLUST/ '
def 		UpperCAmelCase					(		a_,						a_,						a_=None			):





		'''simple docstring'''


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

		# Pick K indices from range [0, N).
		lowerCamelCase     :			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.
		lowerCamelCase     :			Dict		=    data[rand_indices, :]

		return centroids
def 		UpperCAmelCase					(		a_,						a_			):





		'''simple docstring'''


		return pairwise_distances(a_,						a_,						metric='euclidean'			)
def 		UpperCAmelCase					(		a_,						a_			):





		'''simple docstring'''


		lowerCamelCase     :			Dict		=    centroid_pairwise_dist(a_,						a_			)

		# Compute cluster assignments for each data point:
		# Fill in the blank (RHS only)
		lowerCamelCase     :			Optional[int]		=    np.argmin(a_,						axis=1			)

		return cluster_assignment
def 		UpperCAmelCase					(		a_,						a_,						a_			):





		'''simple docstring'''


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

		return new_centroids
def 		UpperCAmelCase					(		a_,						a_,						a_,						a_			):





		'''simple docstring'''


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

		return heterogeneity
def 		UpperCAmelCase					(		a_,						a_			):





		'''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 		UpperCAmelCase					(		a_,						a_,						a_,						a_=500,						a_=None,						a_=False			):





		'''simple docstring'''


		lowerCamelCase     :			str		=    initial_centroids[:]
		lowerCamelCase     :			Tuple		=    None

		for itr in range(a_			):
				if verbose:
						print(a_,						end=''			)

				# 1. Make cluster assignments using nearest centroids
				lowerCamelCase     :			int		=    assign_clusters(a_,						a_			)

				# 2. Compute a new centroid for each of the k clusters, averaging all data
				#    points assigned to that cluster.
				lowerCamelCase     :			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:
						lowerCamelCase     :			Optional[int]		=    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     :			int		=    compute_heterogeneity(a_,						a_,						a_,						a_			)
						record_heterogeneity.append(a_			)

				lowerCamelCase     :			Any		=    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

					_A            = ds.load_iris()
					_A            = 3
					_A            = []
					_A            = get_initial_centroids(dataset['data'], k, seed=0)
					_A     ,  _A            = kmeans(
					    dataset['data'],
					    k,
					    initial_centroids,
					    maxiter=4_0_0,
					    record_heterogeneity=heterogeneity,
					    verbose=True,
					)
					plot_heterogeneity(heterogeneity, k)
def 		UpperCAmelCase					(		a_,						a_,						a_=None			):





		'''simple docstring'''


		if fill_missing_report:
				lowerCamelCase     :			str		=    df.fillna(value=a_			)
		lowerCamelCase     :			Union[str, Any]		=    1
		lowerCamelCase     :			int		=    df.select_dtypes(np.number			).columns
		lowerCamelCase     :			Dict		=    (
		    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)
		lowerCamelCase     :			Optional[Any]		=    report[
		    (report['Features'] == 'dummy') & (report['Type'] == 'count')
		].copy()  # avoid SettingWithCopyWarning
		lowerCamelCase     :			Tuple		=    (
		    'ClusterSize'  # rename created cluster df to match report column names
		)
		lowerCamelCase     :			Optional[Any]		=    '# of Customers'
		lowerCamelCase     :			Union[str, Any]		=    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     :			Optional[int]		=    'ClusterProportion'
		lowerCamelCase     :			List[str]		=    clusterproportion.columns.tolist()
		lowerCamelCase     :			Optional[Any]		=    cols[-2:] + cols[:-2]
		lowerCamelCase     :			List[str]		=    clusterproportion[cols]  # rearrange columns to match report
		lowerCamelCase     :			Optional[Any]		=    report.columns
		lowerCamelCase     :			Any		=    pd.DataFrame(
		    abs(
		        report[report['Type'] == 'count'].iloc[:, 2:].values
		        - clustersize.iloc[:, 2:].values			)			)  # generating df with count of nan values
		lowerCamelCase     :			Union[str, Any]		=    0
		lowerCamelCase     :			List[str]		=    '# of nan'
		lowerCamelCase     :			Tuple		=    report[
		    report['Type'] == 'count'
		].Features.tolist()  # filling values in order to match report
		lowerCamelCase     :			str		=    a.columns.tolist()
		lowerCamelCase     :			Optional[int]		=    cols[-2:] + cols[:-2]
		lowerCamelCase     :			List[str]		=    a[cols]  # rearrange columns to match report
		lowerCamelCase     :			Any		=    report.columns  # rename columns to match report
		lowerCamelCase     :			List[Any]		=    report.drop(
		    report[report.Type == 'count'].index			)  # drop count values except cluster size
		lowerCamelCase     :			Tuple		=    pd.concat(
		    [report, a, clustersize, clusterproportion],						axis=0			)  # concat report with clustert size and nan values
		lowerCamelCase     :			Any		=    report['Features'].isin(a_			)
		lowerCamelCase     :			Any		=    report.columns.tolist()
		lowerCamelCase     :			Any		=    cols[0:2] + cols[-1:] + cols[2:-1]
		lowerCamelCase     :			List[Any]		=    report[cols]
		lowerCamelCase     :			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,
		}
		lowerCamelCase     :			List[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			)
		)
		lowerCamelCase     :			Optional[int]		=    ''
		lowerCamelCase     :			Optional[Any]		=    report.reset_index()
		lowerCamelCase     :			Optional[int]		=    report.drop(columns=['index']			)
		return report


if __name__ == "__main__":
					import doctest

					doctest.testmod()








"""simple docstring"""

from __future__ import annotations
def 		UpperCAmelCase					(		a_			):





		'''simple docstring'''


		assert (
		    isinstance(a_,						a_			) and mask > 0
		), F"""mask needs to be positive integer, your input {mask}"""
		lowerCamelCase     :			Tuple		=    []
		lowerCamelCase     :			Tuple		=    mask

		while submask:
				all_submasks.append(a_			)
				lowerCamelCase     :			Optional[int]		=    (submask - 1) & mask

		return all_submasks


if __name__ == "__main__":
					import doctest

					doctest.testmod()






