📘 Lección 3: Tu Primera Interacción con Datos — Carga, Exploración y Preparación Básica

"Los datos no son números fríos. Son historias, patrones, errores y oportunidades. Aprende a escucharlos."


⏱️ Duración estimada de esta lección: 75-90 minutos


🧭 ¿Por qué esta lección es tan importante?

Porque aquí es donde la teoría se convierte en práctica.

En la Lección 2 aprendiste el mapa. Ahora, vas a caminar el camino.

Vas a:

  • Cargar un dataset real.
  • Explorarlo como un detective.
  • Limpiarlo como un cirujano.
  • Prepararlo como un chef.
  • Y dejarlo listo para que un modelo lo entienda.

⚠️ Advertencia amistosa: Esta lección tiene más código que las anteriores. Pero no te asustes. Lo haremos paso a paso, con explicaciones detalladas, errores comunes y consejos de expertos. No vas a estar solo.


🎯 Objetivos de esta lección

Al finalizar, serás capaz de:

✅ Cargar un dataset desde una URL o archivo local usando Pandas.
✅ Explorar su estructura, contenido y posibles problemas (nulos, duplicados, valores extraños).
✅ Crear visualizaciones simples para entender patrones.
✅ Preparar los datos para el modelo: codificar etiquetas, dividir en train/test, vectorizar texto.
✅ Entender por qué cada paso de preparación es necesario.
✅ Sentirte cómodo/a manipulando datos… ¡tu nueva materia prima!


🛠️ Herramientas que usarás

  • Pandas → La navaja suiza para manipular datos.
  • Matplotlib / Seaborn → Para visualizaciones básicas.
  • Scikit-learn → Para dividir datos y vectorizar texto.
  • Jupyter Notebook o Google Colab → Tu laboratorio de experimentación.

💡 Si no lo has hecho aún, abre Google Colab ahora: https://colab.research.google.com
Crea un nuevo notebook y ¡empecemos!


📥 Parte 1: Cargar los Datos — Tu Primera Importación

Vamos a usar el dataset de SMS Spam Collection. Es pequeño, limpio y perfecto para empezar.

🔹 Paso 1: Importar las librerías necesarias

# Siempre empieza importando lo que necesitas
import pandas as pd

📌 ¿Qué es Pandas?
Es una librería de Python para manipular y analizar datos. Piensa en ella como Excel, pero más potente y programable.


🔹 Paso 2: Cargar el dataset desde una URL

# URL del dataset (alojado en GitHub)
url = "https://raw.githubusercontent.com/justmarkham/DAT8/master/data/sms.tsv"

# Cargar con pandas
# El archivo está separado por tabuladores (\t), y no tiene encabezado
data = pd.read_csv(url, sep='\t', names=['label', 'message'])

# Mostrar las primeras 5 filas
print(data.head())

📌 Salida esperada:

  label                                            message
0   ham  Go until jurong point, crazy.. Available only ...
1   ham                      Ok lar... Joking wif u oni...
2  spam  Free entry in 2 a wkly comp to win FA Cup fina...
3   ham  U dun say so early hor... U c already then say...
4   ham  Nah I don't think he goes to usf, he lives aro...

✅ ¡Datos cargados! Ya tienes un DataFrame de Pandas.


🔹 Paso 3: Entender la estructura del dataset

# ¿Cuántas filas y columnas?
print(f"Forma del dataset: {data.shape}")  # (5572, 2)

# Nombres de las columnas
print(f"Columnas: {data.columns.tolist()}")  # ['label', 'message']

# Tipos de datos
print(data.dtypes)

📌 Salida:

label      object
message    object
dtype: object

→ Ambas columnas son de tipo object (texto en Pandas).


🔹 Paso 4: Ver estadísticas básicas

# Resumen estadístico (solo para columnas numéricas, pero útil para ver si hay nulos)
print(data.describe(include='all'))

📌 Salida clave:

       label           message
count   5572              5572
unique     2              5169
top      ham  Sorry, I'll call later
freq    4825                30

unique=2 en label: solo hay dos valores: 'ham' y 'spam'.
top=ham: el valor más frecuente es 'ham'.
freq=4825: 'ham' aparece 4825 veces.


🔍 Parte 2: Explorar los Datos — Sé un Detective de Datos

Ahora, vamos a profundizar. No asumas nada. Explora todo.


🔸 Pregunta 1: ¿Cuántos spam y cuántos ham hay?

print(data['label'].value_counts())

📌 Salida:

ham     4825
spam     747
Name: label, dtype: int64

→ ¡Tenemos un dataset desbalanceado! Hay 6.5 veces más ham que spam.
→ Esto es normal en detección de spam… pero afectará cómo evaluamos el modelo. ¡Lo veremos en la Lección 6!


🔸 Pregunta 2: ¿Hay valores nulos?

print(data.isnull().sum())

📌 Salida:

label      0
message    0
dtype: int64

→ ¡Perfecto! No hay valores nulos. En la vida real, esto es raro. Siempre revisa esto.


🔸 Pregunta 3: ¿Cómo es la longitud de los mensajes?

# Crear una nueva columna: longitud del mensaje
data['length'] = data['message'].apply(len)

# Estadísticas descriptivas
print(data['length'].describe())

📌 Salida:

count    5572.000000
mean       80.489052
std        59.942492
min         2.000000
25%        36.000000
50%        61.000000
75%       111.000000
max       910.000000
Name: length, dtype: float64

→ ¡Hay mensajes de hasta 910 caracteres! ¿Serán spam? ¿Serán normales?


🔸 Pregunta 4: Visualizar la distribución de longitudes

import matplotlib.pyplot as plt
import seaborn as sns

# Configurar estilo
sns.set_style("whitegrid")

# Histograma de longitudes, coloreado por label
plt.figure(figsize=(12, 6))
sns.histplot(data=data, x='length', hue='label', bins=50, kde=False)
plt.title("Distribución de longitud de mensajes por tipo (Spam vs Ham)", fontsize=16)
plt.xlabel("Longitud del mensaje (caracteres)", fontsize=12)
plt.ylabel("Frecuencia", fontsize=12)
plt.legend(title='Tipo', labels=['Spam', 'Ham'])
plt.show()

📌 ¿Qué ves?

  • Los mensajes de spam tienden a ser más largos (muchos alrededor de 150-200 caracteres).
  • Los mensajes de ham son más cortos (concentrados entre 20 y 100 caracteres).
  • ¡Esto es una pista valiosa! La longitud podría ser una característica útil para el modelo.

🔸 Pregunta 5: ¿Qué palabras aparecen en spam?

Vamos a hacer un análisis de texto muy básico.

# Filtrar solo spam
spam_messages = data[data['label'] == 'spam']['message']

# Convertir a minúsculas y dividir en palabras
words = ' '.join(spam_messages).lower().split()

# Contar frecuencia de palabras
from collections import Counter
word_freq = Counter(words)

# Mostrar las 20 palabras más comunes en spam
print("Palabras más frecuentes en SPAM:")
for word, freq in word_freq.most_common(20):
    print(f"{word}: {freq}")

📌 Salida típica:

free: 167
to: 137
you: 117
call: 90
txt: 89
now: 87
...

→ ¡Claro! Palabras como "free", "call", "now" son muy comunes en spam.
→ Esto confirma que el modelo podrá aprender de estas pistas.


🧹 Parte 3: Preparar los Datos — Limpieza y Transformación

Ahora, prepararemos los datos para el modelo. Recuerda: los modelos entienden números, no texto.


🔹 Paso 1: Codificar las etiquetas (label encoding)

Convertiremos 'ham' y 'spam' en 0 y 1.

# Crear un mapeo
label_map = {'ham': 0, 'spam': 1}

# Aplicar el mapeo
data['label_encoded'] = data['label'].map(label_map)

# Verificar
print(data[['label', 'label_encoded']].head())

📌 Salida:

  label  label_encoded
0   ham              0
1   ham              0
2  spam              1
3   ham              0
4   ham              0

→ ¡Listo! Ahora la etiqueta es numérica.


🔹 Paso 2: Dividir en Train y Test

¡Nunca entrenes y evalúes con los mismos datos!

from sklearn.model_selection import train_test_split

# Características (X) = mensajes
# Etiqueta (y) = label_encoded
X = data['message']
y = data['label_encoded']

# Dividir: 80% train, 20% test
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2,
    random_state=42,  # Para reproducibilidad
    stratify=y        # ¡Mantiene la proporción de spam/ham en train y test!
)

print(f"Tamaño de train: {len(X_train)} mensajes")
print(f"Tamaño de test: {len(X_test)} mensajes")
print(f"Proporción de spam en train: {y_train.mean():.2%}")
print(f"Proporción de spam en test: {y_test.mean():.2%}")

📌 Salida:

Tamaño de train: 4457 mensajes
Tamaño de test: 1115 mensajes
Proporción de spam en train: 13.42%
Proporción de spam en test: 13.41%

→ ¡Perfecto! La proporción se mantuvo gracias a stratify=y.


🔹 Paso 3: Vectorizar el texto — Convertir palabras en números

Usaremos CountVectorizer de Scikit-learn.

from sklearn.feature_extraction.text import CountVectorizer

# Crear el vectorizador
vectorizer = CountVectorizer()

# Aprender el vocabulario y transformar X_train
X_train_vec = vectorizer.fit_transform(X_train)

# Solo transformar X_test (¡no aprender de él!)
X_test_vec = vectorizer.transform(X_test)

# Ver el tamaño
print(f"Vocabulario: {len(vectorizer.vocabulary_)} palabras únicas")
print(f"X_train_vec shape: {X_train_vec.shape}")  # (4457, 7358)
print(f"X_test_vec shape: {X_test_vec.shape}")    # (1115, 7358)

📌 ¿Qué significa (4457, 7358)?

  • 4457 → número de mensajes en train.
  • 7358 → número de palabras únicas en el vocabulario (aprendido de train).

→ Cada mensaje es ahora un vector de 7358 números (la mayoría son 0, porque no todas las palabras aparecen en todos los mensajes).


🔹 Paso 4 (Opcional): Guardar el vocabulario

¿Quieres ver qué palabras aprendió el vectorizador?

# Obtener las primeras 20 palabras del vocabulario
vocab = vectorizer.get_feature_names_out()
print("Primeras 20 palabras del vocabulario:")
print(vocab[:20])

📌 Salida:

['00', '000', '0000', '00000', '000000', '00001', '0001', '00011', '00012', '00015', '0002', '0003', '0004', '0005', '0006', '0007', '00080', '0009', '001', '0010']

→ ¡Ups! Hay muchos números. ¿Por qué? Porque CountVectorizer por defecto toma todo como palabra, incluyendo números y signos de puntuación.

💡 Consejo profesional: Más adelante, podrías mejorar esto con:

  • stop_words='english' → eliminar palabras comunes ("the", "and", "is").
  • lowercase=True → convertir a minúsculas (ya lo hace por defecto).
  • token_pattern=r'\b[a-zA-Z]{2,}\b' → solo palabras de 2+ letras, sin números ni signos.

Pero por ahora, ¡está bien! Estamos aprendiendo.


🧪 Parte 4: Mini-Proyecto Exploratorio — ¡Hazlo Tuyo!

Ahora, es tu turno de explorar.

🔸 Ejercicio 1: ¿Cuál es el mensaje más largo? ¿Es spam o ham?

# Encuentra el índice del mensaje más largo
idx_max = data['length'].idxmax()
longest_message = data.loc[idx_max]

print(f"Longitud: {longest_message['length']} caracteres")
print(f"Tipo: {longest_message['label']}")
print(f"Mensaje: {longest_message['message']}")

📌 Salida típica:

Longitud: 910 caracteres
Tipo: spam
Mensaje: "I HAVE A DATE ON SUNDAY WITH WILL!!..." (¡un spam MUY largo!)

🔸 Ejercicio 2: ¿Cuántos mensajes tienen más de 200 caracteres? ¿Qué porcentaje son spam?

# Filtrar mensajes largos
long_messages = data[data['length'] > 200]
total_long = len(long_messages)
spam_long = long_messages[long_messages['label'] == 'spam'].shape[0]

print(f"Mensajes > 200 caracteres: {total_long}")
print(f"De ellos, spam: {spam_long} ({spam_long/total_long:.1%})")

📌 Salida típica:

Mensajes > 200 caracteres: 45
De ellos, spam: 43 (95.6%)

→ ¡Casi todos los mensajes largos son spam! Esto confirma nuestra hipótesis visual.


🔸 Ejercicio 3: ¿Qué tan limpio está el texto?

Mira algunos mensajes al azar. ¿Ves signos de puntuación, mayúsculas, números, errores ortográficos?

# Muestra 5 mensajes aleatorios
sample = data.sample(5, random_state=1)
for i, row in sample.iterrows():
    print(f"[{row['label']}] {row['message'][:100]}...")  # Solo primeros 100 caracteres

→ Verás cosas como:

  • "U dun say so early hor..." → lenguaje informal, abreviaciones.
  • "FreeMsg Hey there darling..." → mezcla de mayúsculas, signos, números.

💡 Reflexión: ¿Crees que esto afectará al modelo? ¿Cómo podrías mejorarlo? (Pista: limpieza de texto, lematización, etc. — lo veremos en cursos avanzados).


❌ Errores Comunes en esta Lección (¡Evítalos!)

  1. No usar stratify en train_test_split → Desbalancea train/test.
  2. Aplicar fit_transform en test → Fuga de datos (data leakage). ¡Solo transform!
  3. No explorar los datos antes de vectorizar → Te pierdes patrones y errores.
  4. Asustarse por la dimensión alta (7358 columnas) → ¡Es normal en texto! Se llama "espacio de alta dimensionalidad".
  5. No guardar y_train y y_test como variables separadas → Luego no puedes entrenar ni evaluar.

✅ Checklist de esta lección — ¿Qué debes saber hacer ahora?

☐ Cargar un dataset desde una URL con Pandas.
☐ Explorar su estructura, valores únicos, nulos y estadísticas.
☐ Crear visualizaciones para entender patrones (longitud, palabras frecuentes).
☐ Codificar etiquetas de texto a números.
☐ Dividir datos en train/test manteniendo proporciones (stratify).
☐ Vectorizar texto con CountVectorizer.
☐ Entender la forma de las matrices resultantes.
☐ Hacer preguntas exploratorias y responderlas con código.
☐ Sentirte cómodo/a con la manipulación básica de datos.


🎯 Frase para recordar:

"Antes de entrenar un modelo, entrena tus ojos. Aprende a ver lo que los datos te están diciendo."


← Anterior: Lección 2: El Mapa del Tesoro | Siguiente: Lección 4: ¡Entrena tu Primer Modelo! →

Course Info

Course: AI-course0

Language: ES

Lesson: 3 data exploration