



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       (   snake_case			,		snake_case			,		snake_case=None    ):





					"""simple docstring"""

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

					# Pick K indices from range [0, N).
					_lowerCAmelCase      			=					np.random.randint(0			,		snake_case			,		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.
					_lowerCAmelCase      			=					data[rand_indices, :]

					return centroids


def 	_UpperCAmelCase       (   snake_case			,		snake_case    ):





					"""simple docstring"""

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


def 	_UpperCAmelCase       (   snake_case			,		snake_case    ):





					"""simple docstring"""

					_lowerCAmelCase      			=					centroid_pairwise_dist(snake_case			,		snake_case    )

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

					return cluster_assignment


def 	_UpperCAmelCase       (   snake_case			,		snake_case			,		snake_case    ):





					"""simple docstring"""

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

					return new_centroids


def 	_UpperCAmelCase       (   snake_case			,		snake_case			,		snake_case			,		snake_case    ):





					"""simple docstring"""

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

					return heterogeneity


def 	_UpperCAmelCase       (   snake_case			,		snake_case    ):





					"""simple docstring"""

					plt.figure(figsize=(7, 4)    )
					plt.plot(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 	_UpperCAmelCase       (   snake_case			,		snake_case			,		snake_case			,		snake_case=5_00			,		snake_case=None			,		snake_case=False    ):





					"""simple docstring"""

					_lowerCAmelCase      			=					initial_centroids[:]
					_lowerCAmelCase      			=					None

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

										# 1. Make cluster assignments using nearest centroids
										_lowerCAmelCase      			=					assign_clusters(snake_case			,		snake_case    )

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

										_lowerCAmelCase      			=					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_00,
	    record_heterogeneity=heterogeneity,
	    verbose=True,
	)
	plot_heterogeneity(heterogeneity, k)


def 	_UpperCAmelCase       (   snake_case			,		snake_case			,		snake_case=None    ):





					"""simple docstring"""

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


if __name__ == "__main__":
	import doctest

	doctest.testmod()








# tests directory-specific settings - this file is run automatically
# by pytest before any tests are run

import sys
import warnings
from os.path import abspath, dirname, join


# allow having multiple repository checkouts and not needing to remember to rerun
# 'pip install -e .[dev]' when switching between checkouts and running tests.
A__			   = abspath(join(dirname(dirname(dirname(__file__))), """src"""))
sys.path.insert(1, git_repo_path)


# silence FutureWarning warnings in tests since often we can't act on them until
# they become normal warnings - i.e. the tests still need to test the current functionality
warnings.simplefilter(action="""ignore""", category=FutureWarning)


def 	_UpperCAmelCase       (   snake_case    ):





					"""simple docstring"""

					from transformers.testing_utils import pytest_addoption_shared

					pytest_addoption_shared(snake_case    )


def 	_UpperCAmelCase       (   snake_case    ):





					"""simple docstring"""

					from transformers.testing_utils import pytest_terminal_summary_main

					_lowerCAmelCase      			=					terminalreporter.config.getoption("""--make-reports"""    )
					if make_reports:
										pytest_terminal_summary_main(snake_case			,		id=snake_case    )



