<jupyter_start><jupyter_text>GMM with Model SelectionOne of the features of Gaussian Mixture Models is [model selection](https://scikit-learn.org/stable/auto_examples/mixture/plot_gmm_selection.htmlsphx-glr-auto-examples-mixture-plot-gmm-selection-py). This is done by selecting the number of components and covariance structure that results in the best (lowest) BIC. This removes the need for the analyst to guess the number of clusters. We can instead let the clustering algorithm select the best number of clusters for us and instead focus on interpreting what the segments represent.<jupyter_code>import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

from cluster.cluster_functions import *
from cluster.plotting import *
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import pairwise_distances_argmin_min

# sns.set()
%load_ext autoreload
%autoreload 2
invoices = pd.read_csv("data/retail/invoices_2009_2011_no_returns.csv", low_memory=False)
invoices['InvoiceDate'] = pd.to_datetime(invoices['InvoiceDate'])
invoices.dropna(inplace=True)
cohort_customer_ids = invoices[invoices.InvoiceDate<'2010-12-1'].CustomerID.unique()
cohort_invoices = invoices[invoices.CustomerID.isin(cohort_customer_ids)]
cohort_invoices[cohort_invoices['CustomerID']==13087.0]  # Print transaction history for Customer 13087<jupyter_output><empty_output><jupyter_text><jupyter_code>features = get_money_aggregation_features(cohort_invoices[cohort_invoices.InvoiceDate<'2010-12-1'])
features = get_time_aggregation_features(features, cohort_invoices[cohort_invoices.InvoiceDate<'2010-12-1'])
features = get_rfm_features(features, cohort_invoices[cohort_invoices.InvoiceDate<'2010-12-1'])
features = get_second_year_rlv(features, cohort_invoices[cohort_invoices.InvoiceDate>='2010-12-1'])
features.head()
features_to_transform = [
    'MedianPurchaseValue', # 'MeanPurchaseValue', 
    'StDevPurchaseValue',
    'MinPurchaseValue', 
    'MaxPurchaseValue', 
    'SumPurchaseValue',
    'SizePurchaseValue',
    'MedianTimeBetweenPurchase',
    'StDevTimeBetweenPurchase',
    'MinTimeBetweenPurchase',
    'MaxTimeBetweenPurchase',
#     'T_Minus_Recency'
]
features, transformed_feature_names, yjt = transform_features(features, features_to_transform, transform="yjt")
features.head()
cluster_feature_names = transformed_feature_names
scaler = StandardScaler()
cluster_features = features[cluster_feature_names].copy()
cluster_features = scaler.fit_transform(cluster_features)
cluster_features = pd.DataFrame(cluster_features, columns=cluster_feature_names)<jupyter_output><empty_output><jupyter_text>Let the GMM find the best number of components and covariance structure to use<jupyter_code>best_gmm = select_best_gmm(cluster_features, state=3)
cluster_features['Cluster'] = best_gmm.predict(cluster_features)
cluster_centers = best_gmm.means_
num_clusters = best_gmm.n_components
cluster_features['SecondYearRLV'] = features['SecondYearRLV']
evaluate_clusters(cluster_features, num_clusters)
pca = cluster_pca_plot(cluster_features, cluster_centers, scaled=True)
cluster_rf_plot(cluster_features, features)
# Print component loadings
pd.DataFrame(
    data=pca.components_,
    columns=features_to_transform,
    index=['Component 1', 'Component 2']
)
df = cluster_features.groupby('Cluster')['SecondYearRLV'].sum().reset_index()
df['Cluster'] = df['Cluster'].astype(str)
pareto_plot(df, x='Cluster', y='SecondYearRLV', show_pct_y=True, title='RLV Pareto')<jupyter_output><empty_output><jupyter_text>**Mean Cluster features**<jupyter_code>features_copy = features.copy()

# Inverse transform to see cluster features
if yjt:
    features_copy.drop(columns=transformed_feature_names, inplace=True)
    features_copy = pd.concat([
        pd.DataFrame(yjt.inverse_transform(features[transformed_feature_names]), columns=features_to_transform),
        features_copy
    ], axis=1)
    features_copy['Cluster'] = cluster_features['Cluster'].values 
else:
    for transf_col, col in zip(transformed_feature_names, features_to_transform):
        features_copy[transf_col] = np.exp(features_copy[transf_col]) - 1
        features_copy.rename(columns={transf_col:col}, inplace=True)
    features_copy['Cluster'] = cluster_features['Cluster'].values 
features_copy.groupby("Cluster").mean().sort_values(by='SecondYearRLV')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10192962542565266, 'fraction_numerical': 0.01362088535754824, 'mean_word_length': 7.9371196754563895, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 18, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '141647', 'license': 'MIT', 'n_tokens_mistral': 1480, 'n_tokens_neox': 1391, 'n_words': 251, 'path': 'gmm_with_model_selection.ipynb', 'repo_name': 'W-Tran/Online-Retail', 'size': '4405'}