Organizar datos experimentales con Pandas
Al finalizar este tema
Podrás leer un archivo CSV en un DataFrame de Pandas, realizar filtrados condicionales, obtener estadísticas por grupo y combinar dos tablas.
¿Qué es Pandas?
En el tema anterior, aprendiste a leer archivos con el módulo csv y a procesarlos con bucles. Esto es suficiente si tienes 10 muestras, pero si tienes 5000 muestras y 50 columnas, los bucles se vuelven ineficientes.
Pandas es una biblioteca de Python para trabajar con datos tabulares. Permite almacenar datos en una estructura llamada DataFrame, similar a una hoja de cálculo de Excel, y realizar filtrados, ordenamientos y agregaciones con una sola línea de código.
Si lo comparamos con un cuaderno de laboratorio, el módulo csv sería como leer el cuaderno línea por línea, mientras que Pandas sería como tener toda la tabla a la vista y poder extraer las columnas o filas que necesitas de inmediato.
Crear un DataFrame
import pandas as pd
df = pd.read_csv("expression_data.csv")
print(df)print(f"\nFilas: {len(df)}, columnas: {len(df.columns)}")print(f"Columnas: {list(df.columns)}")pd.read_csv() Un archivo CSV se convierte en un DataFrame con una sola línea. Todos los datos se cargan sin necesidad de csv.DictReader ni bucles.
También puede crear los datos directamente:
import pandas as pd
data = { "sample_id": ["S001", "S002", "S003", "S004", "S005"], "gene": ["EGFR", "TP53", "EGFR", "BRCA1", "TP53"], "expression": [12.5, 3.2, 18.7, 7.1, 2.8], "status": ["high", "low", "high", "medium", "low"],}
df = pd.DataFrame(data)print(df)Salida:
sample_id gene expression status
0 S001 EGFR 12.5 high
1 S002 TP53 3.2 low
2 S003 EGFR 18.7 high
3 S004 BRCA1 7.1 medium
4 S005 TP53 2.8 lowExploración de los datos
Cuando se recibe un DataFrame por primera vez, es importante obtener una visión general rápida de su estructura:
df.head() # Primeras 5 filasdf.tail(3) # Últimas 3 filasdf.shape # (número de filas, número de columnas)df.dtypes # Tipo de datos de cada columnadf.describe() # Estadísticas básicas de las columnas numéricas (media, desviación estándar, mínimo y máximo)df.describe() es útil para tener una idea general del rango de los resultados experimentales cuando se reciben por primera vez.
Selección de columnas/filas
# Seleccionar una columna — devuelve una Seriesgenes = df["gene"]
# Seleccionar varias columnas — devuelve un DataFramesubset = df[["sample_id", "expression"]]
# Seleccionar filas — por índicefirst_row = df.iloc[0] # Primera filafirst_three = df.iloc[0:3] # Filas 0 a 2Filtrado condicional
Es una de las funciones principales de Pandas. Permite realizar en una sola línea lo que antes se hacía en dos líneas en el módulo csv: for y if.
# Solo el gen EGFRegfr = df[df["gene"] == "EGFR"]print(egfr)
# Muestras con expression mayor o igual que 5high_expr = df[df["expression"] >= 5.0]print(high_expr)
# Condición compuesta: EGFR y expression mayor o igual que 15egfr_high = df[(df["gene"] == "EGFR") & (df["expression"] >= 15.0)]print(egfr_high)
assert len(egfr) == 2assert len(egfr_high) == 1& es AND, | es OR. Cada condición debe estar entre paréntesis.
Ordenar
# expression en orden descendentesorted_df = df.sort_values("expression", ascending=False)print(sorted_df)Agrupación por grupos: groupby
"¿Cuál es la expresión media por gen?" — En Excel, esto se hacía con tablas dinámicas; ahora se hace con groupby:
gene_stats = df.groupby("gene")["expression"].agg(["mean", "std", "count"])print(gene_stats)Salida:
mean std count
gene
BRCA1 7.10 NaN 1
EGFR 15.60 4.384062 2
TP53 3.00 0.282843 2groupby("gene") — Agrupar por gen, ["expression"] — para la columna de expresión, agg(["mean", "std", "count"]) — calcular la media, la desviación estándar y el recuento de una sola vez.
Agregar una nueva columna
# Añadir una columna con expression transformada a log2import numpy as np
df["log2_expression"] = np.log2(df["expression"])print(df[["sample_id", "expression", "log2_expression"]])Combinar dos tablas: merge
Cuando los datos del experimento y la información de la muestra se encuentran en archivos separados:
import pandas as pd
# Metadatos de las muestrasmetadata = pd.DataFrame({ "sample_id": ["S001", "S002", "S003", "S004", "S005"], "tissue": ["lung", "breast", "lung", "breast", "colon"], "age": [45, 62, 38, 55, 71],})
# Combinar con los datos de expressionmerged = pd.merge(df, metadata, on="sample_id")print(merged)on="sample_id" — Combina filas que tienen el mismo valor en dos tablas. Es un concepto similar a JOIN en SQL. Lo que aprendiste en el tema database-basics también se aplica aquí.
Guardar los resultados
# Guardar como CSVegfr.to_csv("egfr_samples.csv", index=False)
# Guardar como TSVegfr.to_csv("egfr_samples.tsv", sep="\t", index=False)index=False — No se incluirán los números de fila (0, 1, 2...) en el archivo.
¡Pruébalo tú mismo! (Ejemplo simplificado)
Completa los espacios en blanco para realizar un filtrado condicional y una agregación de grupos en el DataFrame.
import pandas as pddf = pd.read_csv("samples.csv")# Filtrar las muestras con un valor de OD mayor o igual a 1.0passed = df[df["od"] 1.0]# OD medio por estadostats = df.("status")["od"].mean()print(stats)
Errores comunes y soluciones
P: Me sale el error KeyError: 'gene'
Comprueba que el nombre de la columna sea correcto. Usa df.columns para imprimir los nombres de las columnas reales. Puede que contenga espacios (" gene" vs "gene").
P: He filtrado los datos y he obtenido un DataFrame vacío
Es posible que la condición sea demasiado estricta o que el tipo de datos no sea el correcto. Usa df["expression"].dtype para comprobar el tipo. Si comparas una cadena con un número, es posible que no obtengas resultados.
P: Me sale la advertencia SettingWithCopyWarning
Esta advertencia aparece cuando asignas un valor al resultado filtrado. Crea una copia explícita con df_filtered = df[condición].copy() y modifícala para que la advertencia desaparezca.
P: He hecho merge y el número de filas ha aumentado
Si la columna especificada en on tiene valores duplicados, se crearán todas las combinaciones posibles, lo que aumentará el número de filas. Antes de merge, comprueba si hay duplicados en ambas tablas con df["sample_id"].duplicated().sum().