Demostración: Python, Google Colab y datos experimentales¶

Curso: Herramientas Estadísticas y Computacionales para el Análisis de Datos en Investigación Bioquímica
Sesión 1: Introducción a Python y manejo de datos experimentales
Docente: Andrés García Medina
Contacto: andgarm.n@gmail.com

Propósito¶

Al terminar esta demostración podrás:

  1. ejecutar celdas de texto y código;
  2. reconocer objetos básicos de Python;
  3. importar datos CSV y Excel con pandas;
  4. interpretar filas, columnas, tipos y valores faltantes;
  5. producir una primera tabla y una gráfica reproducibles.

Los datos son sintéticos y se diseñaron exclusivamente con fines docentes.

Antes de ejecutar en Google Colab¶

  1. Descarga del paquete los archivos datos_actividad_enzimatica_limpios.csv y datos_actividad_enzimatica_limpios.xlsx.
  2. Abre este notebook en Google Colab.
  3. En el panel izquierdo, selecciona Archivos > Subir y carga ambos archivos.
  4. Ejecuta las celdas en orden con Shift + Enter.
In [36]:
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

print("Python:", sys.version.split()[0])
print("pandas:", pd.__version__)
Python: 3.12.13
pandas: 2.2.2

1. Objetos básicos de Python¶

Una variable en Python es un nombre que apunta a un objeto. En esta sesión usaremos números, texto, listas y diccionarios antes de trabajar con tablas.

In [37]:
ph = 7
temperatura_c = 37
tratamiento = "pH 7 a 37 °C"
replicas = [1, 2, 3]
condicion = {"ph": ph, "temperatura_c": temperatura_c}

print(tratamiento)
print("Número de réplicas biológicas:", len(replicas))
print("Condición:", condicion)
pH 7 a 37 °C
Número de réplicas biológicas: 3
Condición: {'ph': 7, 'temperatura_c': 37}

2. Importar CSV y Excel¶

pandas representa una tabla como un DataFrame. Cada fila es una observación y cada columna es una variable.

In [38]:
datos_csv = pd.read_csv("datos_actividad_enzimatica_limpios.csv")
datos_excel = pd.read_excel("datos_actividad_enzimatica_limpios.xlsx",
    sheet_name="actividad_enzimatica")
In [39]:
print("CSV:", datos_csv.shape)
print("Excel:", datos_excel.shape)

datos_excel.shape
CSV: (90, 13)
Excel: (90, 13)
Out[39]:
(90, 13)
In [40]:
print("¿Coinciden las dimensiones?", datos_csv.shape == datos_excel.shape)
¿Coinciden las dimensiones? True
In [41]:
datos_csv.columns
Out[41]:
Index(['muestra_id', 'fecha', 'lote_enzima', 'operador', 'ph', 'temperatura_c',
       'replica_biologica', 'replica_tecnica', 'tiempo_reaccion_min',
       'absorbancia_inicial_405', 'absorbancia_final_405', 'actividad_u_ml',
       'observacion'],
      dtype='object')

actividad_u_ml (U/mL): actividad enzimática estimada por mililitro de muestra. En este ejercicio se calcula a partir del cambio de absorbancia por minuto y un factor de conversión definido por el método experimental.

3. Inspeccionar los datos antes de avanzar con el análisis¶

Las cuatro preguntas iniciales son:

  • ¿Cuántas observaciones y variables hay?
  • ¿Cómo se llaman las variables?
  • ¿Qué tipo asignó Python a cada columna?
  • ¿Existen valores faltantes o identificadores repetidos?
In [42]:
print("Dimensiones:", datos_csv.shape)
print("\nColumnas:")
print(datos_csv.columns.tolist())
print("\nTipos:")
print(datos_csv.dtypes)
print("\nValores faltantes:")
print(datos_csv.isna().sum())
print("\nIdentificadores duplicados:", datos_csv["muestra_id"].duplicated().sum())
Dimensiones: (90, 13)

Columnas:
['muestra_id', 'fecha', 'lote_enzima', 'operador', 'ph', 'temperatura_c', 'replica_biologica', 'replica_tecnica', 'tiempo_reaccion_min', 'absorbancia_inicial_405', 'absorbancia_final_405', 'actividad_u_ml', 'observacion']

Tipos:
muestra_id                  object
fecha                       object
lote_enzima                 object
operador                    object
ph                           int64
temperatura_c                int64
replica_biologica            int64
replica_tecnica              int64
tiempo_reaccion_min        float64
absorbancia_inicial_405    float64
absorbancia_final_405      float64
actividad_u_ml             float64
observacion                float64
dtype: object

Valores faltantes:
muestra_id                  0
fecha                       0
lote_enzima                 0
operador                    0
ph                          0
temperatura_c               0
replica_biologica           0
replica_tecnica             0
tiempo_reaccion_min         0
absorbancia_inicial_405     0
absorbancia_final_405       0
actividad_u_ml              0
observacion                90
dtype: int64

Identificadores duplicados: 0

4. Seleccionar, filtrar, verificar y resumir¶

En esta sección transformaremos la tabla completa en un resumen útil para responder una pregunta experimental:

A 37 °C, ¿cómo cambia la actividad enzimática media entre los distintos valores de pH?

El procedimiento se divide en cuatro acciones:

  1. Seleccionar: conservar las variables necesarias.
  2. Filtrar: mantener únicamente las observaciones realizadas a 37 °C.
  3. Verificar: comprobar que los tratamientos y las réplicas estén correctamente representados.
  4. Resumir: calcular el número de observaciones y la actividad media para cada pH.
In [43]:
columnas_clave = [
    "muestra_id",
    "ph",
    "temperatura_c",
    "replica_biologica",
    "replica_tecnica",
    "actividad_u_ml"
]

vista = datos_csv[columnas_clave]

vista.head()
Out[43]:
muestra_id ph temperatura_c replica_biologica replica_tecnica actividad_u_ml
0 AE-PH5-T25-B1-R1 5 25 1 1 0.4025
1 AE-PH5-T25-B1-R2 5 25 1 2 0.4243
2 AE-PH5-T25-B2-R1 5 25 2 1 0.4232
3 AE-PH5-T25-B2-R2 5 25 2 2 0.4222
4 AE-PH5-T25-B3-R1 5 25 3 1 0.4824
In [44]:
condicion_37 = vista[vista["temperatura_c"] == 37]

condicion_37.head()
Out[44]:
muestra_id ph temperatura_c replica_biologica replica_tecnica actividad_u_ml
6 AE-PH5-T37-B1-R1 5 37 1 1 0.4731
7 AE-PH5-T37-B1-R2 5 37 1 2 0.4955
8 AE-PH5-T37-B2-R1 5 37 2 1 0.4508
9 AE-PH5-T37-B2-R2 5 37 2 2 0.4233
10 AE-PH5-T37-B3-R1 5 37 3 1 0.5618

El filtro mantiene únicamente las muestras analizadas a 37 °C. Así, la temperatura permanece constante y podemos concentrarnos en comparar el efecto del pH.

Primero contamos las observaciones disponibles para cada pH:

In [45]:
condicion_37["ph"].value_counts().sort_index()
Out[45]:
count
ph
5 6
6 6
7 6
8 6
9 6

Después revisamos cuántas réplicas técnicas existen para cada combinación de pH y réplica biológica:

In [46]:
verificacion_replicas = (
    condicion_37
    .groupby(["ph", "replica_biologica"], as_index=False)
    .agg(
        replicas_tecnicas=("replica_tecnica", "nunique"),
        observaciones=("muestra_id", "size")
    )
)

verificacion_replicas
Out[46]:
ph replica_biologica replicas_tecnicas observaciones
0 5 1 2 2
1 5 2 2 2
2 5 3 2 2
3 6 1 2 2
4 6 2 2 2
5 6 3 2 2
6 7 1 2 2
7 7 2 2 2
8 7 3 2 2
9 8 1 2 2
10 8 2 2 2
11 8 3 2 2
12 9 1 2 2
13 9 2 2 2
14 9 3 2 2

También comprobamos si existen datos faltantes o identificadores duplicados:

In [47]:
print("Datos faltantes:")
print(condicion_37.isna().sum())

print("\nIdentificadores duplicados:")
print(condicion_37["muestra_id"].duplicated().sum())
Datos faltantes:
muestra_id           0
ph                   0
temperatura_c        0
replica_biologica    0
replica_tecnica      0
actividad_u_ml       0
dtype: int64

Identificadores duplicados:
0

Resumir la actividad por tratamiento

In [48]:
resumen_control = (
    condicion_37
    .groupby("ph", as_index=False)
    .agg(
        n=("actividad_u_ml", "size"),
        actividad_media=("actividad_u_ml", "mean")
    )
)

resumen_control
Out[48]:
ph n actividad_media
0 5 6 0.493700
1 6 6 1.178717
2 7 6 1.584667
3 8 6 1.143617
4 9 6 0.500167

Al seleccionar, filtrar y verificar los datos, confirmamos que cada tratamiento conserva sus muestras y réplicas. Posteriormente, resumimos la actividad enzimática por pH bajo una temperatura constante de 37 °C.

5. Primera visualización exploratoria¶

La gráfica sirve como control de calidad y como panorama del patrón, nunca debe pensarse como un reemplazo del análisis estadístico formal (que se desarrollará en sesiones posteriores.)

In [49]:
sns.set_theme(style="whitegrid")
fig, ax = plt.subplots(figsize=(8, 4.5))
sns.boxplot(
    data=datos_csv,
    x="ph",
    y="actividad_u_ml",
    hue="temperatura_c",
    ax=ax,
)
ax.set(
    title="Actividad enzimática sintética por pH y temperatura",
    xlabel="pH",
    ylabel="Actividad (U/mL)",
)
ax.legend(title="Temperatura (°C)", bbox_to_anchor=(1.02, 1), loc="upper left")
plt.tight_layout()
plt.show()
In [51]:
datos_csv
Out[51]:
muestra_id fecha lote_enzima operador ph temperatura_c replica_biologica replica_tecnica tiempo_reaccion_min absorbancia_inicial_405 absorbancia_final_405 actividad_u_ml observacion
0 AE-PH5-T25-B1-R1 2026-07-20 L01 Ana 5 25 1 1 2.0 0.0496 0.1301 0.4025 NaN
1 AE-PH5-T25-B1-R2 2026-07-20 L01 Ana 5 25 1 2 2.0 0.0482 0.1331 0.4243 NaN
2 AE-PH5-T25-B2-R1 2026-07-21 L02 Luis 5 25 2 1 2.0 0.0398 0.1244 0.4232 NaN
3 AE-PH5-T25-B2-R2 2026-07-21 L02 Luis 5 25 2 2 2.0 0.0361 0.1205 0.4222 NaN
4 AE-PH5-T25-B3-R1 2026-07-22 L03 Ana 5 25 3 1 2.0 0.0427 0.1392 0.4824 NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ...
85 AE-PH9-T45-B1-R2 2026-07-20 L01 Ana 9 45 1 2 2.0 0.0522 0.1235 0.3566 NaN
86 AE-PH9-T45-B2-R1 2026-07-21 L02 Luis 9 45 2 1 2.0 0.0434 0.1293 0.4295 NaN
87 AE-PH9-T45-B2-R2 2026-07-21 L02 Luis 9 45 2 2 2.0 0.0495 0.1398 0.4517 NaN
88 AE-PH9-T45-B3-R1 2026-07-22 L03 Ana 9 45 3 1 2.0 0.0483 0.1405 0.4608 NaN
89 AE-PH9-T45-B3-R2 2026-07-22 L03 Ana 9 45 3 2 2.0 0.0477 0.1388 0.4556 NaN

90 rows × 13 columns

In [54]:
import matplotlib.pyplot as plt
import seaborn as sns

# Gráfico de barras de la actividad enzimática media por pH
sns.set_theme(style="whitegrid")
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

sns.barplot(
    data=resumen_control,
    x="ph",
    y="actividad_media",
    ax=ax1,
    palette="viridis"
)
ax1.set(title="Actividad Enzimática",
        xlabel="pH",
        ylabel="Actividad Media (U/mL)")

# Histograma de la actividad enzimática
sns.histplot(
    data=datos_csv,
    x="actividad_u_ml",
    ax=ax2,
    color="red"
)
ax2.set(title="Distribución de la Actividad Enzimática",
        xlabel="Actividad (U/mL)",
        ylabel="Frecuencia")

plt.tight_layout()
plt.show()
/tmp/ipykernel_2579/3646558244.py:8: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.

  sns.barplot(
In [56]:
import matplotlib.pyplot as plt

plt.figure(figsize=(8, 8))
plt.pie(resumen_control['actividad_media'], labels=resumen_control['ph'], autopct='%1.1f%%', startangle=90, colors=plt.cm.viridis.colors)
plt.title('Proporción de Actividad Enzimática Media por pH')
plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.show()

6. Exportar una tabla reproducible¶

En Colab el archivo aparecerá en el panel Archivos. Cada exportación debe tener un nombre claro y no sobrescribir los datos crudos.

In [50]:
resumen_control.to_csv("resumen_actividad_37C.csv")

Conclusión¶

  • El notebook conserva código, decisiones y resultados en una secuencia verificable.
  • Una tabla bien organizada distingue tratamientos, unidades y tipos de réplica.
  • Antes de calcular estadísticas debemos inspeccionar estructura, tipos, faltantes y duplicados.

¿Qué error de organización en tus propios datos podría cambiar una conclusión científica?