<jupyter_start><jupyter_text>Clustering   Importación de librerías<jupyter_code># Dataframes 
import pandas as pd
import numpy as np

# Gráficas
import seaborn as sns
import matplotlib.pyplot as plt

# Reduccion de dimensionalidad
from sklearn.decomposition import PCA

# Clustering
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from sklearn.preprocessing import LabelEncoder
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import MinMaxScaler


# Evaluacion de clusters
from sklearn.metrics import silhouette_score
from sklearn import metrics

# Dendograma
from scipy.cluster.hierarchy import dendrogram<jupyter_output><empty_output><jupyter_text>Extracción y manipulación de datos originales<jupyter_code>## Abrir datos
DataX = open("./source/trainData.txt","r")
DataY = open("./source/trainClasses.txt","r")
DatatestX = open("./source/testData.txt","r")
DatatestY = open("./source/testClasses.txt","r")

## Parseo de datos
def splitFile(File,List):
    for line in File:
        line = line.replace("\n","")
        List.append(line)
    

## Convertimos cada línea de nuestros archivos en una entrada de un vector
trainX= []
trainY= []
testX = []
testY = []

## Generamos la lista de oraciones y clases
splitFile(DataX,trainX)
splitFile(DataY,trainY)
splitFile(DatatestX,testX)
splitFile(DatatestY,testY)


DataX.close()
DataY.close()
DatatestX.close()
DatatestY.close()<jupyter_output><empty_output><jupyter_text>Extracción y manipulación de datos lematizados<jupyter_code># Funcion para generar oraciones lematizadas
def generateLemma(LemmaFile):
    sentences = []
    sentence = ""
    for line in LemmaFile:
        if line == "\n":
            continue
        line = line.split("\t")
        if line[2] != ".":
            sentence = sentence + line[2] + " "
            continue
        if line[2] == ".":
            sentence = sentence + "."
            sentences.append(sentence)
            sentence = ""
            continue
            
    return sentences

# Función para generar las oraciones originales de entrada
def originalFile(LemmaFile):
    sentences = []
    sentence = ""
    for line in LemmaFile:
        if line == "\n":
            continue
        line = line.split("\t")
        if line[1] != ".":
            sentence = sentence + line[1] + " "
            continue
        if line[1] == ".":
            sentence = sentence + "."
            sentences.append(sentence)
            sentence = ""
            continue
            
    return sentences
# Generar lista de oraciones lematizadas
DataSetLemma = open("source/trainData.txt.conll","r")

DataLemma = generateLemma(DataSetLemma)
print(f'Número de oraciones de entrenamiento lematizadas: {len(DataLemma)}')

# Generar lista de oraciones originales de entrada
DataSetLemma2 = open("source/trainData.txt.conll","r")
OriginalFile = originalFile(DataSetLemma2)

print(f'Número de oraciones de entrenamiento originales: {len(OriginalFile)}')
# Hacer lista de tuplas entre oración y categoria
dataValues = []
DataSetLemma2 = open("source/trainData.txt.conll","r")
contador = 0
for element in trainX:
    Value = (element,trainY[contador])
    dataValues.append(Value)
    contador = contador + 1

# Encontrar oraciones del primer archivo en las oraciones originales que regreso CoreNLP
LemmaDataX = []
for sentence1 in trainX:
    if sentence1 not in OriginalFile:
        continue
    LemmaDataX.append(sentence1)
    
LemmaDataYTrain = []
for element in LemmaDataX:
    for values in dataValues:
        if element == values[0]:
            LemmaDataYTrain.append(values[1])
            break

# generar lista de tupplas para asociar originales con lematizados

LemmaValues = []
contador = 0

for element in DataLemma:
    Value = (OriginalFile[contador],element)
    LemmaValues.append(Value)
    contador = contador + 1

LemmaXTrain = []

for values in LemmaValues:
    for element in LemmaDataX:
        if element == values[0]:
            LemmaXTrain.append(values[1])
            break

DataSetLemma.close()
DataSetLemma2.close()
# Generar lista de oraciones lematizadas
DataSetLemma = open("source/testData.txt.conll","r")

DataLemma = generateLemma(DataSetLemma)
print(f'Número de oraciones de prueba lematizadas: {len(DataLemma)}')


# Generar lista de oraciones originales de entrada
DataSetLemma2 = open("source/testData.txt.conll","r")
OriginalFile = originalFile(DataSetLemma2)

print(f'Número de oraciones de prueba originales: {len(OriginalFile)}')
# Hacer lista de tuplas entre oración y categoria
dataValues = []
DataSetLemma2 = open("source/testData.txt.conll","r")
contador = 0
for element in testX:
    Value = (element,testY[contador])
    dataValues.append(Value)
    contador = contador + 1
    
# Encontrar oraciones del primer archivo en las oraciones originales que regreso CoreNLP
LemmaDataX = []
for sentence1 in testX:
    if sentence1 not in OriginalFile:
        continue
    LemmaDataX.append(sentence1)
    
LemmaDataYTest = []
for element in LemmaDataX:
    for values in dataValues:
        if element == values[0]:
            LemmaDataYTest.append(values[1])
            break

# generar lista de tupplas para asociar originales con lematizados

LemmaValues = []
contador = 0

for element in DataLemma:
    Value = (OriginalFile[contador],element)
    LemmaValues.append(Value)
    contador = contador + 1

LemmaXTest = []

for values in LemmaValues:
    for element in LemmaDataX:
        if element == values[0]:
            LemmaXTest.append(values[1])
            break
        
DataSetLemma.close()
DataSetLemma2.close()
## Entrenar al archivo de oraciones

xData = LemmaXTrain + LemmaXTest
yData = LemmaDataYTrain + LemmaDataYTest

print(f'Data: {len(xData)}')
print(f'Labels: {len(yData)}')
print(f'{yData[:5]}')
## Codificar la clase de cada dato

encoder = LabelEncoder()
labels = encoder.fit_transform(yData)
print(f'Clases codificadas: {labels[:5]}')
# Visualización por nombre de clases
print(f'Nombre de clases: {encoder.classes_}')
## Vectorización de las oraciones

vectorizer = TfidfVectorizer()

xVec = vectorizer.fit_transform(xData)
xCount = pd.DataFrame(xVec.todense(), columns = vectorizer.get_feature_names())
xCount
## Escalamiento de los datos

scaler = MinMaxScaler()
scaledSet = scaler.fit_transform(xCount)
scaledSet = pd.DataFrame(scaledSet, columns = xCount.columns, index = xCount.index)
scaledSet<jupyter_output><empty_output><jupyter_text>Reducción de dimensionalidad por PCA<jupyter_code>## Aplicar reducción por PCA

pca = PCA(n_components = 0.95, svd_solver = 'full')
dataPCA = pca.fit_transform(scaledSet)
print(f'Número de componentes: {pca.n_components_}')<jupyter_output>Número de componentes: 1256<jupyter_text>Clustering con método K-Means de datos no reducidos<jupyter_code>## Crear función para usar el método de K-Means
def clustering_kmeans(n, data):  
    np.random.seed(9)
    ## Creamos el modelo con la funcion KMeans
    kmeans = KMeans(n_clusters = n, random_state = 42)
    ## Ajustar el modelo
    kmeans.fit(X = data)
    ## Predicción de clusters
    y_predict = kmeans.predict(X = data)
    ## Crear paleta
    palette = sns.color_palette("husl", n)
    
    ## Retornar valores de modelo, predicción y paleta
    return(kmeans, y_predict, palette)
## Obtener modelo y predicciones con la función creada
kmeans, yPredictRaw, palette = clustering_kmeans(2, scaledSet)

## Asignación de clases
class_corr = [[0, 1], ['OTHER', 'DOM']]
classesTag = ['DOM' if i == class_corr[0][0] else 'OTHER' if i == class_corr[0][1] else i for i in yPredictRaw]
classesTag = np.array(classesTag)

sns.scatterplot(dataPCA[:,0], dataPCA[:,1], hue = classesTag, legend = 'full', palette = palette)
plt.title('Clustering con datos no reducidos', size=15)
## Matriz de confusión
pd.crosstab(labels, yPredictRaw, dropna = False, rownames = ['True'], colnames = ['Pred'])<jupyter_output><empty_output><jupyter_text>Evaluación con "silhouette method"<jupyter_code>silhouettes = []

## Ajustamos modelos desde k = 2 hasta k = 12
for n in range(2, 12):
    ## Ajustamos el modelo con el valor de k de cada iteracion
    kmeans, yPredictRaw, palette = clustering_kmeans(n, scaledSet)
    silhouette = silhouette_score(scaledSet, yPredictRaw)
    silhouettes.append(silhouette)

## Graficamos
fig, ax = plt.subplots(1, 1)
ax.plot(range(2, 12), silhouettes, marker = 'o')
ax.set_title("Evolución de la silueta por numero de clusters \nclustering K-means con datos no reducidos")
ax.set_xlabel('Número de clusters')
ax.set_ylabel('Silueta');<jupyter_output><empty_output><jupyter_text>Clustering con método K-Means de datos reducidos<jupyter_code>## Obtener modelo y predicciones con la función creada
kmeans, yPredictPCA, palette = clustering_kmeans(2, dataPCA)

## Asignación de clases
class_corr = [[0, 1], ['OTHER', 'DOM']]
classesTag = ['DOM' if i == class_corr[0][0] else 'OTHER' if i == class_corr[0][1] else i for i in yPredictPCA]
classesTag = np.array(classesTag)

palette = sns.color_palette("Set2", 2)
sns.scatterplot(dataPCA[:,0], dataPCA[:,1], hue = classesTag, legend = 'full', palette = palette)
plt.title('Clustering con datos reducidos', size=15)
## Matriz de confusión
pd.crosstab(labels, yPredictPCA, dropna = False, rownames = ['True'], colnames = ['Pred'])<jupyter_output><empty_output><jupyter_text>Evaluación con "silhouette method"<jupyter_code>silhouettes = []

## Ajustamos modelos desde k = 2 hasta k = 12
for n in range(2, 12):
    ## Ajustamos el modelo con el valor de k de cada iteracion
    kmeans, yPredictPCA, palette = clustering_kmeans(n, dataPCA)
    silhouette = silhouette_score(dataPCA, yPredictPCA)
    silhouettes.append(silhouette)

## Graficamos
fig, ax = plt.subplots(1, 1)
ax.plot(range(2, 12), silhouettes, marker = 'o')
ax.set_title("Evolución de la silueta por numero de clusters \nclustering K-means con datos reducidos")
ax.set_xlabel('Número de clusters')
ax.set_ylabel('Silueta');<jupyter_output><empty_output><jupyter_text>Clusterización por método Jerárquico<jupyter_code>## Creamos funcion que ajuste los modelos de forma automatica
def hierarchical_clustering(data):
    np.random.seed(42)
    hierchclust = AgglomerativeClustering(affinity = 'euclidean', distance_threshold = 0, n_clusters = None)  # El parametro distance_threshold = 0 hace que se forme un cluster por observacion
    
    # Predecir las clases
    y_predict = hierchclust.fit_predict(X = data)
    
    # Obtener el numero de clusters con el metodo .n_clusters_
    nclust = hierchclust.n_clusters_ 
    
    # Devolvemos el modelo, las predicciones y el numero de clusters
    return(hierchclust, y_predict, nclust)
## Funcion para crear un dendrograma
def plot_dendrogram(model, **kwargs):
    counts = np.zeros(model.children_.shape[0])
    n_samples = len(model.labels_)
    for i, merge in enumerate(model.children_):
        current_count = 0
        for child_idx in merge:
            if child_idx < n_samples:
                current_count += 1  # leaf node
            else:
                current_count += counts[child_idx - n_samples]
        counts[i] = current_count

    linkage_matrix = np.column_stack([model.children_, model.distances_,
                                      counts]).astype(float)
    # Plot
    dendrogram(linkage_matrix, **kwargs)<jupyter_output><empty_output><jupyter_text>Clustering con método Jerárquico de no datos reducidos<jupyter_code>## Clusterización de los datos por método Jerárquico

hierchclust, y_predict, nclust = hierarchical_clustering(scaledSet)
print(f'Número de clusters: {nclust}')
## Graficar
# Especificiar el parametro color_threshold a 0, de este modo, cada observacion es un cluster

fig, ax = plt.subplots(1, 1)
plot_dendrogram(hierchclust, color_threshold = 0, ax = ax)
ax.set_title("Clustering jerárquico con threshold 0 \nde datos no reducidos")
fig, ax = plt.subplots(1, 1)
plot_dendrogram(hierchclust, color_threshold = 14, ax = ax)
ax.set_title("Clustering jerárquico con threshold 14 \nde datos no reducidos")<jupyter_output><empty_output><jupyter_text>Clustering con método Jerárquico de datos reducidos<jupyter_code>## Clusterización de los datos por método Jerárquico

hierchclust, y_predict, nclust = hierarchical_clustering(dataPCA)
print(f'Número de clusters: {nclust}')
## Graficar
# Especificiar el parametro color_threshold a 0, de este modo, cada observacion es un cluster

fig, ax = plt.subplots(1, 1)
plot_dendrogram(hierchclust, color_threshold = 0, ax = ax)
ax.set_title("Clustering jerárquico con threshold 0 \nde datos reducidos")
fig, ax = plt.subplots(1, 1)
plot_dendrogram(hierchclust, color_threshold = 14, ax = ax)
ax.set_title("Clustering jerárquico con threshold 14 \nde datos reducidos")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10288130284998434, 'fraction_numerical': 0.00963044159098027, 'mean_word_length': 4.280281107895824, 'pattern_counts': {'":': 6, '<': 45, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '635708', 'license': 'MIT', 'n_tokens_mistral': 4506, 'n_tokens_neox': 4299, 'n_words': 1124, 'path': 'clustering.ipynb', 'repo_name': 'Elizabeth-mqz-gmz/DataScience_FinalProject', 'size': '12772'}