Volver a la lista

Análisis de datos de expresión génica mediante aprendizaje automático.

Cómo agrupar datos de expresión génica con scikit-learn, visualizarlos con PCA y clasificar tumores y tejidos normales.

Intermedio
|
120min
|
Verificado (2026-06)
AgrupaciónPCAPCACategoríascikit-learnK-means
Progreso0/12 (0%)

Análisis de datos de expresión génica mediante aprendizaje automático

Al finalizar este tema

Con scikit-learn, podrá realizar el agrupamiento (K-means) de datos de expresión génica, reducir la dimensionalidad mediante PCA y clasificar muestras tumorales frente a normales.


¿Qué es el aprendizaje automático?

A medida que acumula experiencia en el laboratorio, podrá determinar que "este patrón de bandas es una mutación" simplemente observando las fotografías del gel. El aprendizaje automático consiste en enseñar a las computadoras esta capacidad de "reconocimiento de patrones" mediante datos.

ML común en bioinformática:

  • Aprendizaje no supervisado: encuentra patrones en los datos sin etiquetas (agrupamiento, PCA)
  • Aprendizaje supervisado: entrena modelos de clasificación con datos etiquetados (tumor vs. normal)

Preparación de datos: Datos de expresión génica simulados

Se generan datos sintéticos que imitan datos reales de RNA-seq. En esta situación, se mide la expresión de 20 genes en 100 muestras (50 normales + 50 tumorales).

python
import numpy as np
import pandas as pd
np.random.seed(42)
n_samples = 100
n_genes = 20
gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]
sample_labels = ["Normal"] * 50 + ["Tumor"] * 50
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0 # Genes_01~05 hacia arriba
tumor_expr[:, 15:] -= 2.0 # Genes_16~20 hacia abajo
expression = np.vstack([normal_expr, tumor_expr])
df = pd.DataFrame(expression, columns=gene_names)
df["Label"] = sample_labels
print(f"Tamaño de datos: {df.shape}")
print(f"Muestras: Normal {sum(df['Label']=='Normal')}, Tumor {sum(df['Label']=='Tumor')}")
print(f"\nPrimeras 5 filas:")
print(df.head().to_string())
assert df.shape == (100, 21)
assert sum(df["Label"] == "Normal") == 50
assert sum(df["Label"] == "Tumor") == 50

PCA: Reducción de la dimensionalidad de datos a 2D

Se reduce la dimensionalidad de los datos de expresión de 20 genes a una representación bidimensional para facilitar su visualización. El análisis de componentes principales (PCA) identifica las direcciones con la mayor varianza en los datos.

python
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
np.random.seed(42)
n_genes = 20
gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0
tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])
labels = ["Normal"] * 50 + ["Tumor"] * 50
# 1. Estandarización (media 0, varianza 1)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(expression)
# 2. Aplicar PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print(f"Dimensiones originales: {expression.shape[1]} genes")
print(f"Dimensiones reducidas: {X_pca.shape[1]} componentes principales")
print(f"Varianza explicada: PC1={pca.explained_variance_ratio_[0]:.1%}, PC2={pca.explained_variance_ratio_[1]:.1%}")
print(f"Total: {sum(pca.explained_variance_ratio_):.1%}")
assert X_pca.shape == (100, 2)
assert pca.explained_variance_ratio_[0] > pca.explained_variance_ratio_[1]
python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
np.random.seed(42)
n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0
tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])
labels = np.array(["Normal"] * 50 + ["Tumor"] * 50)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(expression)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
fig, ax = plt.subplots(figsize=(8, 6))
for label, color in [("Normal", "#2E86AB"), ("Tumor", "#E74C3C")]:
mask = labels == label
ax.scatter(X_pca[mask, 0], X_pca[mask, 1], c=color, s=40, alpha=0.7, label=label, edgecolors="white", linewidth=0.5)
ax.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.1%})", fontsize=12)
ax.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.1%})", fontsize=12)
ax.set_title("PCA — Normal vs Tumor", fontsize=14, fontweight="bold")
ax.legend(fontsize=11)
ax.grid(True, alpha=0.2)
fig.tight_layout()
fig.savefig("pca_plot.png", dpi=150)
plt.close(fig)
print("Guardado completo de pca_plot.png")
assert X_pca.shape == (100, 2)

Clustering K-medias: Aprendizaje no supervisado

Identifica grupos de datos sin necesidad de etiquetas. K-medias es uno de los algoritmos de clustering más intuitivos.

python
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
np.random.seed(42)
n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0
tumor_expr[:, 15:] -= 2.0
expression = np.vstack([normal_expr, tumor_expr])
true_labels = [0] * 50 + [1] * 50
scaler = StandardScaler()
X_scaled = scaler.fit_transform(expression)
# K-means: clasificación en 2 clústeres
kmeans = KMeans(n_clusters=2, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X_scaled)
# Verificar qué tan bien coinciden los clústeres con las etiquetas reales
from sklearn.metrics import adjusted_rand_score
ari = adjusted_rand_score(true_labels, clusters)
print(f"Clúster 0: {sum(clusters == 0)} muestras")
print(f"Clúster 1: {sum(clusters == 1)} muestras")
print(f"Índice de Rand ajustado: {ari:.3f} (1.0 = coincidencia perfecta)")
assert len(set(clusters)) == 2
assert ari > 0.5
print("El agrupamiento K-means tuvo éxito en la distinción normal/tumoral")

Clasificación: Aprendizaje supervisado

En esta ocasión, se entrena el modelo con un conjunto de datos en el que se conoce la etiqueta correcta (Normal/Tumor) y se utilizan los datos para clasificar nuevas muestras.

python
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
np.random.seed(42)
n_genes = 20
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0
tumor_expr[:, 15:] -= 2.0
X = np.vstack([normal_expr, tumor_expr])
y = np.array([0] * 50 + [1] * 50) # 0=Normal, 1=Tumor
# 1. División de datos (80% entrenamiento, 20% prueba)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 2. Estandarización
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 3. Entrenamiento de Random Forest
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train_scaled, y_train)
# 4. Predicción y Evaluación
y_pred = clf.predict(X_test_scaled)
accuracy = accuracy_score(y_test, y_pred)
print(f"Precisión: {accuracy:.1%}")
print(f"\nDatos de entrenamiento: {len(X_train)} muestras, datos de prueba: {len(X_test)} muestras")
print(f"\nInforme de clasificación:")
print(classification_report(y_test, y_pred, target_names=["Normal", "Tumor"]))
assert accuracy > 0.8
assert len(X_train) == 80
assert len(X_test) == 20

Importancia de las características: ¿Qué genes son importantes?

El modelo de Bosque Aleatorio identifica los genes (características) que más contribuyen a la clasificación.

python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
np.random.seed(42)
n_genes = 20
gene_names = [f"Gene_{i+1:02d}" for i in range(n_genes)]
normal_expr = np.random.randn(50, n_genes) * 1.0 + 5.0
tumor_expr = np.random.randn(50, n_genes) * 1.5 + 5.0
tumor_expr[:, :5] += 3.0
tumor_expr[:, 15:] -= 2.0
X = np.vstack([normal_expr, tumor_expr])
y = np.array([0] * 50 + [1] * 50)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_scaled, y)
importances = clf.feature_importances_
sorted_idx = np.argsort(importances)[-10:] # Top 10
fig, ax = plt.subplots(figsize=(8, 5))
ax.barh(range(len(sorted_idx)), importances[sorted_idx], color="#2E86AB")
ax.set_yticks(range(len(sorted_idx)))
ax.set_yticklabels([gene_names[i] for i in sorted_idx], fontsize=10)
ax.set_xlabel("Feature Importance", fontsize=12)
ax.set_title("Top 10 Important Genes", fontsize=14, fontweight="bold")
fig.tight_layout()
fig.savefig("feature_importance.png", dpi=150)
plt.close(fig)
top_gene = gene_names[sorted_idx[-1]]
print(f"Gen más importante: {top_gene}")
print(f"Top 5 genes: {[gene_names[i] for i in sorted_idx[-5:]]}")
assert len(importances) == 20
assert sum(importances) > 0.99

Prueba tú mismo (Ejemplo desvanecido)

Completa los espacios en blanco para completar el patrón de ajuste y predicción de scikit-learn.

Completar espaciospython
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(data)
kmeans = KMeans(n_clusters=)
clusters = kmeans.(X_scaled)

Errores comunes y soluciones

P: ValueError: could not convert string to float

Hay columnas de texto (por ejemplo, nombres de genes, etiquetas) en los datos. Selecciona solo las columnas numéricas con df.select_dtypes(include=[np.number]).

P: La precisión es solo del 50%

Esto significa que los datos no son diferentes de los datos aleatorios. Verifica si se realizó la estandarización (StandardScaler) y si las características (genes) tienen patrones suficientemente distintos.

P: ConvergenceWarning: Number of distinct clusters found

El algoritmo K-means no ha convergido. Aumenta los valores de n_init=10 y max_iter=300, o verifica el escalado de los datos.


¡Felicidades! Has completado los 5 temas del MVP de la ruta común de DevBench y la ruta A-1.

💬 Preguntas y comentarios

0 comentarios

Puedes publicar sin iniciar sesión. Los comentarios de invitados no pueden editarse ni eliminarse después.

0/2000

Cargando...