<jupyter_start><jupyter_text>PA005: High Value Customer Identification ( Insiders )  0.0. Planejamento da Solução ( IOT )  Input - Entrada 1. Problema de Negócio    - Selecionar os clientes mais valiosos para integrar um programa de Fidelização.        2. Conjunto de Dados    - Vendas de um e-commerce online, durante o período de um ano.  Output - Saída 1. A indicação das pessoas que farão parte do programa de Insiders    - Lista: client_id | is_insider                    1023| yes/1                   2. Relatório com as respostas das perguntas de negócio    - Quem são as pessoas elegíveis para participar do programa de Insiders ?    - Quantos clientes farão parte do grupo?    - Quais as principais características desses clientes ?    - Qual a porcentagem de contribuição do faturamento, vinda do Insiders ?    - Qual a expectativa de faturamento desse grupo para os próximos meses ?    - Quais as condições para uma pessoa ser elegível ao Insiders ?    - Quais as condições para uma pessoa ser removida do Insiders ?    - Qual a garantia que o programa Insiders é melhor que o restante da base ?    - Quais ações o time de marketing pode realizar para aumentar o faturamento?  Task - Tarefas 1. Quem são as pessoas elegíveis para participar do programa de Insiders?    - O que é ser elegível? O que são clientes de maior "valor"?    - Faturamento:        - Alto Ticket Médio - Quanto o cliente gastou em média na empresa;        - Alto LTV - Durante seu tempo na empresa, quanto você já gastou?;        - Baixa Recência - Tempo entre as compras;        - Alto Basket Size - Tamanho da cesta de compras;        - Baixa probabilidade de Churn - Quando a pessoa parou de comprar de você;        - Alta previsão de LTV;        - Alta propensão de compra            - Custo:        - Baixa taxa de devolução;            - Experiência de comora:        - Média alta das avaliações2. Quantos clientes farão parte do grupo?    - Número total de clientes;    - % do grupo Insiders.3. Quais as principais características desses clientes?    - Escrever características do cliente:        - Idade;        - Localização.        - Escrever características do consumo        - Atributos da clusterização4. Qual a porcentagem de contribuição do faturamento, vinda do Insiders?    - Faturamento total do ano;    - Faturamento total do grupo.5. Qual a expectativa de faturamento desse grupo para os próximos meses?    - LTV do grupo Insiders;    - Análise de Cohort - Análise onde eu marco uma pessoa no tempo.    6. Quais as condições para uma pessoa ser elegível ao Insiders?    - Definir a periodicidade (1 mes, 3 meses);    - A pessoa precisa ser similar com uma pessoa do grupo7. Quais as condições para uma pessoa ser removida do Insiders?    - Definir a periodicidade (1 mes, 3 meses);    - A pessoa precisa ser não similar com uma pessoa do grupo8. Qual a garantia que o programa Insiders é melhor que o restante da base?    - Teste A/B;    - Teste A/B Bayesiano;    - Teste de Hipóteses.9. Quais ações o time de marketing pode realizar para aumentar o faturame1.    - Desconto;    - Preferência de compra;    - Frete;    - Visita a empresa.  0.0. Imports<jupyter_code>import numpy as np
import pandas as pd
import seaborn as sns

import umap.umap_ as umap

from matplotlib import pyplot as plt
from IPython.display import HTML

from sklearn import cluster as c
from sklearn import metrics as m
from sklearn import preprocessing as pp

from plotly import express as px

from yellowbrick.cluster import KElbowVisualizer, SilhouetteVisualizer<jupyter_output><empty_output><jupyter_text>0.1. Helper Functions<jupyter_code>def jupyter_settings():
    %matplotlib inline
    %pylab inline
    
    plt.style.use('ggplot')
    plt.rcParams['figure.figsize'] = [24, 9]
    plt.rcParams['font.size'] = 24
    
    display(HTML('<style>.container{width:100% !important;}</style>'))
    pd.options.display.max_columns = None
    pd.options.display.max_rows = None
    pd.set_option('display.expand_frame_repr', False)
    
    sns.set()
    
jupyter_settings()<jupyter_output>Populating the interactive namespace from numpy and matplotlib<jupyter_text>0.2. Load dataset<jupyter_code># read data
url='https://drive.google.com/file/d/1P2xz5nr3c8lPJ2esMb-N0uQs0TiTAeNW/view?usp=sharing'
url_='https://drive.google.com/uc?id=' + url.split('/')[-2]
df_raw = pd.read_csv(url_, encoding='windows-1252')

# drop extra column
df_raw = df_raw.drop(columns=['Unnamed: 8'], axis=1)
df_raw.columns
df_raw.head()<jupyter_output><empty_output><jupyter_text>1.0. Decrição dos Dados<jupyter_code>df1 = df_raw.copy()<jupyter_output><empty_output><jupyter_text>1.1. Rename Columns<jupyter_code>df1.columns
cols_new = ['invoice_no', 'stock_code', 'description', 'quantity', 'invoice_date', 'unit_price', 'customer_id', 'country']
df1.columns = cols_new
df1.sample()<jupyter_output><empty_output><jupyter_text>1.2. Data Dimensions<jupyter_code>print('Number of rows: {}'.format(df1.shape[0]))
print('Number of columns: {}'.format(df1.shape[1]))<jupyter_output>Number of rows: 541909
Number of columns: 8<jupyter_text>1.3. Data Types<jupyter_code>df1.dtypes
df1.head()<jupyter_output><empty_output><jupyter_text>1.4. Chek NA<jupyter_code>df1.isna().sum()<jupyter_output><empty_output><jupyter_text>1.5. Replace NA  1.5.1. df_missing<jupyter_code>df_missing = df1.loc[df1['customer_id'].isna(), :]
df_not_missing = df1.loc[~df1['customer_id'].isna(), :]
df_missing.shape
df_not_missing.shape
df_nm = df_not_missing.loc[:, ['invoice_no', 'customer_id']]
df_nm['customer_id'] = df_nm['customer_id'].astype(int)
df_merge = pd.merge(df_missing, df_nm, how='left', on='invoice_no')
df_merge.head()
# Não consigo fazer essa substitução
# fazendo o replace
df_backup = pd.DataFrame(df_missing['invoice_no'].drop_duplicates())
df_backup['customer_id'] = np.arange(19000, 19000+len(df_backup), 1)

# merge original data
df1 = pd.merge(df1, df_backup, how='left', on='invoice_no')

# coalesce
df1['customer_id'] = df1['customer_id_x'].combine_first(df1['customer_id_y'])

# drio extra column 
df1 = df1.drop(columns=['customer_id_x', 'customer_id_y'], axis=1)
# remova NA
df1 = df1.dropna(subset=['description'])
print('Removed Data: {:.2f}%'.format(100 * (1 - (df1.shape[0]/df_raw.shape[0]))))
df1.isna().sum()<jupyter_output><empty_output><jupyter_text>1.6. Change Dtypes<jupyter_code>df1.dtypes
# invoice date
df1['invoice_date'] = pd.to_datetime(df1['invoice_date'], format='%d-%b-%y')

# customer id
df1['customer_id'] = df1['customer_id'].astype(int)
df1.dtypes<jupyter_output><empty_output><jupyter_text>1.7. Descriptive Statistics<jupyter_code>num_attributes = df1.select_dtypes(include=['int64', 'float64'])
cat_attributes = df1.select_dtypes(exclude=['int64', 'float64', 'datetime64[ns]'])<jupyter_output><empty_output><jupyter_text>1.7.1. Numerical Attributes<jupyter_code># Central Tendency - Mean, Median
ct1 = pd.DataFrame(num_attributes.apply(np.mean)).T
ct2 = pd.DataFrame(num_attributes.apply(np.median)).T

# Dispersion - Desvio Padrão, Mínimo, Máximo, Range, Skew, Kurtosis
d1 = pd.DataFrame(num_attributes.apply(np.std)).T
d2 = pd.DataFrame(num_attributes.apply(np.min)).T
d3 = pd.DataFrame(num_attributes.apply(np.max)).T
d4 = pd.DataFrame(num_attributes.apply(lambda x: x.max() - x.min())).T
d5 = pd.DataFrame(num_attributes.apply(lambda x: x.skew())).T
d6 = pd.DataFrame(num_attributes.apply(lambda x: x.kurtosis())).T

# Concatenate
m = pd.concat([d2, d3, d4, ct1, ct2, d1, d5, d6]).T.reset_index()
m.columns = ['attributes', 'min', 'max', 'range', 'mean', 'median', 'std', 'skew', 'kurtosis']

m<jupyter_output><empty_output><jupyter_text>1.7.1.1. Numerical Attributes - Investigating 1. Quantity Negativa (Pode ser devolução)2. Preço unitário igual a zero (pode ser promoção?)  1.7.2. Categorical Attributes<jupyter_code>cat_attributes.head()<jupyter_output><empty_output><jupyter_text>Invoice No<jupyter_code># Problema: Temos invoice com letras e numeros
#df1['invoice_no'].astype(int)

# indentificacao:
df_letter_invoices = df1.loc[df1['invoice_no'].apply(lambda x: bool(re.search('[^0-9]+', x))), :]
df_letter_invoices.head()
# Conferindo se todos os valores com letras são negativos
print('Total Number of invoices: {}'.format(len(df_letter_invoices)))
print('Total Number of negative quantity: {}'.format(len(df_letter_invoices[df_letter_invoices['quantity'] < 0])))


# Assumption: Letras indicam retornos<jupyter_output>Total Number of invoices: 9291
Total Number of negative quantity: 9288<jupyter_text>Stock Code<jupyter_code>df1.head()
# stock_code que são apenas letras
df1.loc[df1['stock_code'].apply(lambda x: bool(re.search('^[a-zA-Z]+$', x))), 'stock_code'].unique()

# Acao:
## 1. Remove stock_code in ['POST', 'D', 'M', 'PADS', 'DOT', 'CRUK']<jupyter_output><empty_output><jupyter_text>Description<jupyter_code>df1.head()

# Acao:
## 2. Delete description<jupyter_output><empty_output><jupyter_text>Country<jupyter_code>df1['country'].unique()
df1['country'].value_counts(normalize=True).head()
df1[['customer_id', 'country']].drop_duplicates().groupby('country').count().reset_index().sort_values('customer_id', ascending=False).head()<jupyter_output><empty_output><jupyter_text>2.0. Filtragem de Variáveis<jupyter_code>df2 = df1.copy()
# unit_price
df2 = df2.loc[df2['unit_price'] >= 0.04, :]

# stock_code
df2 = df2[~df2['stock_code'].isin(['POST', 'D', 'M', 'PADS', 'DOT', 'CRUK'])]
df2 = df2[~df2['stock_code'].isin(['POST', 'D', 'DOT', 'M', 'S', 'AMAZONFEE', 'm', 'DCGSSBOY', 'DCGSSGIRL', 'PADS', 'B', 'CRUK'])]

# description
df2 = df2.drop(columns='description', axis=1)

# map
df2 = df2[~df2['country'].isin(['European Comunity', 'Unspecified'])]


df2_returns = df2.loc[df2['quantity'] < 0, :]
df2_purchases = df2.loc[df2['quantity'] >= 0, :]<jupyter_output><empty_output><jupyter_text>3.0. Feature Engineering<jupyter_code># Feature ideas:
## Moving Average - 7d - 15d - 30d
## Sazonalidade
df3 = df2.copy()<jupyter_output><empty_output><jupyter_text>3.1. Feature Creation<jupyter_code># data reference
df_ref = df3.drop(['invoice_no', 'stock_code', 'quantity', 'invoice_date', 'unit_price', 'country'], axis=1).drop_duplicates(ignore_index=True)<jupyter_output><empty_output><jupyter_text>3.1.1. Gross Revenue<jupyter_code># Gross Revenue - Faturamento - Quantity * Price
df2_purchases.loc[:, 'gross_revenue']  = df2_purchases.loc[:, 'quantity'] * df2_purchases.loc[:, 'unit_price']

# Monetary
df_monetary = df2_purchases.loc[:, ['customer_id', 'gross_revenue']].groupby('customer_id').sum().reset_index()
df_ref = pd.merge(df_ref, df_monetary, on='customer_id', how='left')
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.2. Recency - Day from last purchase<jupyter_code># Recency - Last Day purchase
df_recency = df2_purchases.loc[:, ['customer_id', 'invoice_date']].groupby('customer_id').max().reset_index()
df_recency['recency_days'] = (df2_purchases['invoice_date'].max() - df_recency['invoice_date']).dt.days
df_recency = df_recency[['customer_id', 'recency_days']].copy()
df_ref = pd.merge(df_ref, df_recency, on='customer_id', how='left')

df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.3. Quantity of purchases<jupyter_code># Numero de produtos
df_freq = (df2_purchases.loc[:, ['customer_id', 'invoice_no']].drop_duplicates()
                                                             .groupby( 'customer_id' )
                                                             .count()
                                                             .reset_index()
                                                             .rename( columns={'invoice_no': 'qtde_invoices'}) )
df_ref = pd.merge( df_ref, df_freq, on='customer_id', how='left' )
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.4. Quantity of product purchases<jupyter_code># Numero de produtos
df_freq = (df2_purchases.loc[:, ['customer_id', 'quantity']].groupby( 'customer_id' ).sum()
                                                           .reset_index()
                                                           .rename( columns={'quantity': 'qtde_items'} ) )
df_ref = pd.merge( df_ref, df_freq, on='customer_id', how='left' )
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.5. Quantity of Products<jupyter_code># Numero de produtos
df_freq = (df2_purchases.loc[:, ['customer_id', 'stock_code']].groupby( 'customer_id' ).count()
                                                           .reset_index()
                                                           .rename( columns={'stock_code': 'qtde_products'} ) )
df_ref = pd.merge( df_ref, df_freq, on='customer_id', how='left' )
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.6. Average Ticket Value<jupyter_code># Avg Ticket
avg_ticket = df2_purchases.loc[:, ['customer_id', 'gross_revenue']].groupby('customer_id').mean().reset_index().rename(columns={'gross_revenue':'avg_ticket'})
df_ref = pd.merge(df_ref, avg_ticket, on='customer_id', how='left')

df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.7. Average Recency Days<jupyter_code># Average recency days
df_aux = df2[['customer_id', 'invoice_date']].drop_duplicates().sort_values(['customer_id', 'invoice_date'], ascending=['False', 'False'])
df_aux['next_customer_id'] = df_aux['customer_id'].shift() # next customer
df_aux['previous_date'] = df_aux['invoice_date'].shift() # next invoice date
df_aux.head()

df_aux['avg_recency_days'] = df_aux.apply(lambda x: x['invoice_date'] - x['previous_date'] if x['customer_id'] == x['next_customer_id'] else np.nan, axis=1)

df_aux = df_aux.dropna(subset=['avg_recency_days'])

df_aux = df_aux[['customer_id', 'avg_recency_days']].groupby('customer_id').mean().reset_index()

# merge
df_ref = pd.merge(df_ref, df_aux, how='left', on='customer_id')

df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.8. Frequency Purchase<jupyter_code>df_max = df2[['customer_id', 'invoice_date']].drop_duplicates().groupby('customer_id').max()
df_max = df2[['customer_id', 'invoice_date']].drop_duplicates().groupby('customer_id').min()
df_purchase = df2[['customer_id', 'invoice_date']].drop_duplicates().groupby('customer_id').count()
df_aux = df2[['customer_id', 'invoice_no', 'invoice_date']].drop_duplicates().groupby('customer_id').agg(max_=('invoice_date', 'max'),
                                                                                                         min_=('invoice_date', 'min'),
                                                                                                         days_=('invoice_date', lambda x: ((x.max() - x.min()).days) + 1),
                                                                                                         buy_=('invoice_no', 'count')).reset_index()

# frequency
df_aux['frequency'] = df_aux[['buy_', 'days_']].apply(lambda x: x['buy_'] / x['days_'] if x['days_'] != 0 else 0, axis=1)

# merge
df_ref = pd.merge(df_ref, df_aux[['customer_id', 'frequency']], how='left', on='customer_id')
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.9. Number of Returns<jupyter_code># Number of Returns
df_returns = df2_returns[['customer_id', 'quantity']].groupby( 'customer_id' ).sum().reset_index().rename( columns={'quantity':'qtde_returns'} )
df_returns['qtde_returns'] = df_returns['qtde_returns'] * -1

df_ref = pd.merge( df_ref, df_returns, how='left', on='customer_id' )
df_ref.loc[df_ref['qtde_returns'].isna(), 'qtde_returns'] = 0

df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.10. Basket Size<jupyter_code>df_aux = ( df2_purchases.loc[:, ['customer_id', 'invoice_no', 'quantity']].groupby( 'customer_id' )
                                                                            .agg( n_purchase=( 'invoice_no', 'nunique'),
                                                                                  n_products=( 'quantity', 'sum' ) )
                                                                            .reset_index() )

# calculation
df_aux['avg_basket_size'] = df_aux['n_products'] / df_aux['n_purchase']

# merge
df_ref = pd.merge( df_ref, df_aux[['customer_id', 'avg_basket_size']], how='left', on='customer_id' )
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>3.1.11 Unique Basket Size<jupyter_code>df_aux = ( df2_purchases.loc[:, ['customer_id', 'invoice_no', 'stock_code']].groupby( 'customer_id' )
                                                                            .agg( n_purchase=( 'invoice_no', 'nunique'),
                                                                                   n_products=( 'stock_code', 'nunique' ) )
                                                                            .reset_index() )

# calculation
df_aux['avg_unique_basket_size'] = df_aux['n_products'] / df_aux['n_purchase']

# merge
df_ref = pd.merge( df_ref, df_aux[['customer_id', 'avg_unique_basket_size']], how='left', on='customer_id' )
df_ref.isna().sum()<jupyter_output><empty_output><jupyter_text>4.0. EDA - Exploratory Data Analysis<jupyter_code>df4 = df_ref.dropna()
df4.isna().sum()<jupyter_output><empty_output><jupyter_text>4.1. Univariate Analysis  4.2. Bivariate Analysis  5.0. Data Preparation<jupyter_code>df5 = df4.copy()
df5.head()
## Standard Scalaer
ss = pp.MinMaxScaler()

df5['gross_revenue'] = ss.fit_transform( df5[['gross_revenue']] )
df5['recency_days'] = ss.fit_transform( df5[['recency_days']] )
df5['qtde_invoices'] = ss.fit_transform( df5[['qtde_invoices']] )
df5['qtde_items'] = ss.fit_transform( df5[['qtde_items']] )
df5['qtde_products'] = ss.fit_transform( df5[['qtde_products']] )
df5['avg_ticket'] = ss.fit_transform( df5[['avg_ticket']] )
df5['avg_recency_days'] = ss.fit_transform( df5[['avg_recency_days']] )
df5['frequency'] = ss.fit_transform( df5[['frequency']] )
df5['qtde_returns'] = ss.fit_transform( df5[['qtde_returns']] )
df5['avg_basket_size'] = ss.fit_transform( df5[['avg_basket_size']] )
df5['avg_unique_basket_size'] = ss.fit_transform( df5[['avg_unique_basket_size']] )<jupyter_output><empty_output><jupyter_text>6.0. Feature Selection<jupyter_code>df6 = df5.copy()<jupyter_output><empty_output><jupyter_text>7.0. Hyperparameter Fine-Tunning<jupyter_code>X = df6.drop(columns = ['customer_id', 'avg_recency_days'])
clusters = [2, 3, 4, 5, 6, 7]
kmeans = KElbowVisualizer(c.KMeans(), k=clusters, timings=False)
kmeans.fit(X)
kmeans.show()
kmeans = KElbowVisualizer(c.KMeans(), k=clusters, timings=False, metric = 'silhouette')
kmeans.fit(X)
kmeans.show()<jupyter_output><empty_output><jupyter_text>7.1 Silhouette Analysis<jupyter_code>fig, ax = plt.subplots(3, 2, figsize=(25,18))

for k in clusters:
    km = c.KMeans(n_clusters=k, init='random', n_init=10, max_iter=100, random_state=42)
    q, mod = divmod(k,2)
    
    visualizer = SilhouetteVisualizer(km, colors='yellowbrick', ax=ax[q-1][mod])
    visualizer.fit(X)
    visualizer.finalize()<jupyter_output><empty_output><jupyter_text>8.0. Model Training  8.1. K-Means<jupyter_code># model definition
k = 3
kmeans = c.KMeans(init='random', n_clusters=k, n_init=10, max_iter=300, random_state=42)

# model training
kmeans.fit(X)

# clustering
labels = kmeans.labels_<jupyter_output><empty_output><jupyter_text>8.1.2. Cluster Validation<jupyter_code>from sklearn import metrics as m
## WSS (Within-cluster sum of square)
print('WSS Value: {}'.format(kmeans.inertia_))

## SS (Silhouette Score)
print('SS Value: {}'.format(m.silhouette_score(X, labels, metric='euclidean')))<jupyter_output><empty_output><jupyter_text>9.0. Cluster Analysis<jupyter_code>df9 = df6.copy()
df9['cluster'] = labels
df9.head()<jupyter_output><empty_output><jupyter_text>9.1. Visualization Inspection<jupyter_code>visualizer = SilhouetteVisualizer(kmeans, colors='yellowbrick')
visualizer.fit(X)
visualizer.finalize()<jupyter_output><empty_output><jupyter_text>9.2. 2d plot<jupyter_code>df_viz = df9.drop(columns='customer_id', axis=1)
sns.pairplot(df_viz, hue='cluster')<jupyter_output><empty_output><jupyter_text>9.3. UMAP<jupyter_code>reducer = umap.UMAP(n_neighbors = 90, random_state=42)
embedding = reducer.fit_transform(X)
# embedding
df_viz['embedding_x'] = embedding[:, 0]
df_viz['embedding_y'] = embedding[:, 1]

# plot UMAP
sns.scatterplot(x='embedding_x', y='embedding_y',
                hue = 'cluster',
                palette = sns.color_palette('hls', n_colors=len(df_viz['cluster'].unique())),
                data = df_viz)<jupyter_output><empty_output><jupyter_text>9.4. Cluster Profile<jupyter_code># Number of customer
df_cluster = df9[['customer_id', 'cluster']].groupby('cluster').count().reset_index()
df_cluster['perc_customer'] = 100*(df_cluster['customer_id'] / df_cluster['customer_id'].sum())

# Avg gross revenue
df_avg_gross_revenue = df9[['gross_revenue', 'cluster']].groupby('cluster').mean().reset_index()
df_cluster = pd.merge(df_cluster, df_avg_gross_revenue, how='inner', on='cluster')

# Avg recency days
df_avg_recency_days = df9[['recency_days', 'cluster']].groupby('cluster').mean().reset_index()
df_cluster = pd.merge(df_cluster, df_avg_recency_days, how='inner', on='cluster')

# Avg invoice_no
df_avg_invoice_no = df9[['invoice_no', 'cluster']].groupby('cluster').mean().reset_index()
df_cluster = pd.merge(df_cluster, df_avg_invoice_no, how='inner', on='cluster')

# Avg Ticket
df_ticket = df9[['avg_ticket', 'cluster']].groupby('cluster').mean().reset_index()
df_cluster = pd.merge(df_cluster, df_ticket, how='inner', on='cluster')

df_cluster<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14927314105243603, 'fraction_numerical': 0.01908875574752682, 'mean_word_length': 4.34558093346574, 'pattern_counts': {'":': 0, '<': 174, '<?xml version=': 0, '>': 174, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 49, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '43', 'dir': 'jupyter-structured-clean-dedup', 'id': '164259', 'license': 'MIT', 'n_tokens_mistral': 7962, 'n_tokens_neox': 7767, 'n_words': 1440, 'path': 'notebooks/c04_insiders_clustering-feature-engineering.ipynb', 'repo_name': 'tadeucbm/insiders_clustering', 'size': '21531'}