Guía: secadores eléctricos con DCA y app en GitHub Codespaces

Diseño Experimental 2026-2 y Procesos 3 · Oct 7, 2026 · @Justo

Qué vamos a hacer

Cada grupo va a comparar 4 secadores eléctricos con un Diseño Completamente al Azar (DCA) y a construir, en GitHub Codespaces, una app de Streamlit que aleatoriza el experimento, recibe los datos, dibuja las curvas de secado y hace el ANOVA. Es una actividad conjunta con Procesos 3 (profesor Alfredo Fernández).

Todo se hace sin laboratorio: solo se necesita una balanza, un termómetro o termocupla y un cronómetro. Todo el software es gratuito.

Al final, cada grupo entrega:

  • Un repositorio en GitHub con la app funcionando y los datos reales en data/.
  • El análisis del DCA: tabla ANOVA, supuestos, comparaciones de medias e interpretación.
  • La bitácora de IA: prompts usados, qué se verificó a mano y qué se corrigió.

El experimento: un DCA con 4 secadores

La pregunta es: ¿los 4 secadores secan igual de rápido el mismo producto? Como los equipos son eléctricos y tienen temperatura controlada, las condiciones entre corridas son bastante homogéneas, y por eso un DCA es razonable. Con secadores solares, el clima de cada día obligaría a bloquear.

Elemento Definición en este experimento
Factor (tratamientos) Secador, con 4 niveles: A, B, C y D
Unidad experimental Una corrida: un lote de producto secado una vez en un secador
Réplicas r = 4 corridas por secador (mínimo 3), en total N = 16 corridas
Aleatorización El orden de las 16 corridas se sortea con la app
Respuesta principal Pérdida de masa (%) a un tiempo fijo, por ejemplo 120 min
Respuesta secundaria Constante de secado k del modelo exponencial
Variable de control Temperatura media en el hogar durante la corrida

Con r = 4, el error tiene 4 × (4 − 1) = 12 grados de libertad; con r = 3 tendría solo 8.

El modelo del DCA es:

\[y_{ij} = \mu + \tau_i + \varepsilon_{ij}, \qquad i = 1,\dots,4 \;\text{(secadores)}, \quad j = 1,\dots,r \;\text{(corridas)}\]

Respuestas que se pueden medir sin laboratorio

Sin estufa no se puede conocer la humedad exacta del producto, pero sí su pérdida de masa, que en el secado es casi toda agua:

\[PM_t(\%) = 100 \cdot \frac{m_0 - m_t}{m_0}\]

La curva de secado se describe con un modelo exponencial sobre la masa, donde mₑ es la masa a la que tiende el producto (masa de equilibrio) y k la constante de secado:

\[m_t = m_e + (m_0 - m_e)\, e^{-k t}\]

Un k mayor indica un secador que seca más rápido. La app estima mₑ y k para cada corrida, y después compara los k con el ANOVA.

Condiciones que deben ser iguales en todas las corridas

  • El mismo producto y variedad, cortado con el mismo espesor (lo define el profesor Fernández).
  • La misma masa inicial, por ejemplo 200 g ± 5 g, distribuida en una sola capa.
  • La misma temperatura de consigna en los 4 secadores y la misma posición de la bandeja.
  • El mismo tiempo total de secado y la misma forma de pesar.

Advertencia: si solo hay un equipo de cada tipo, las conclusiones son sobre esos 4 equipos concretos. Varias bandejas dentro de la misma corrida no son réplicas: la réplica es una corrida nueva.

Protocolo de medición

En cada corrida se mide la masa de la bandeja y la temperatura en el hogar (donde están las muestras), siguiendo el calendario del profesor Fernández: el intervalo se duplica en cada tramo, porque al principio la masa cambia muy rápido.

Tramo Intervalo Tiempos de lectura (min)
Inicio — 0
Hasta 10 min 1 min 1, 2, 3, …, 10
Hasta 20 min 2 min 12, 14, 16, 18, 20
Hasta 40 min 4 min 24, 28, 32, 36, 40
Hasta 80 min 8 min 48, 56, 64, 72, 80
Hasta 160 min 16 min 96, 112, 128, 144, 160
Hasta 320 min 32 min 192, 224, 256, 288, 320

La duración total se acuerda con el profesor Fernández antes del primer ensayo y es igual para todas las corridas. Si se fija en 240 min, la última lectura es 240.

Cómo medir:

  1. Antes de empezar: pesar la bandeja vacía (tara), cargar el producto y anotar la masa inicial. Anotar la temperatura y la humedad relativa del ambiente.
  2. Cada lectura: sacar la bandeja, pesarla y devolverla en menos de 15 segundos, siempre igual. Una persona pesa, otra anota y otra controla el cronómetro.
  3. Temperatura: la sonda queda fija a la altura de las muestras y no se mueve en toda la corrida.
  4. Observaciones: anotar cualquier anomalía (puerta abierta más tiempo, corte de luz, producto quemado).

Formato de registro

Todas las corridas se guardan en un solo archivo CSV, en formato largo: una fila por lectura. La masa se anota sin la tara.

corrida,orden,secador,fecha,tiempo_min,masa_g,temp_hogar_C,temp_amb_C,observaciones
1,1,C,2026-10-14,0,200.4,24.8,29.5,
1,1,C,2026-10-14,1,199.1,38.2,29.5,
1,1,C,2026-10-14,2,197.6,45.0,29.6,puerta abierta 20 s

Almacenamiento de 15 días (sin laboratorio)

Al terminar, se empaca cada corrida en bolsas separadas, una por fecha: días 0, 4, 8, 12 y 15. En cada fecha se abre solo la bolsa que corresponde y se registra:

  • Masa del producto, para ver si gana humedad del ambiente.
  • Foto del color con el celular, dentro de una caja con la misma luz y una hoja blanca de referencia. El color se analiza después con Python.

Textura, sólidos solubles, acidez y pH quedan pendientes mientras no haya laboratorio.

Parte 1. Crear el repositorio del grupo y abrir el Codespace

Un Codespace es un computador en la nube con VS Code, Python y Git ya instalados, que se abre en el navegador. Sirve aunque tu computador sea lento o no permita instalar programas.

1.1 El líder del grupo crea el repositorio

  1. En github.com, clic en + (arriba a la derecha) → New repository.
  2. Repository name: secadores-dca-grupoN (N = número del grupo). Marcar Public.
  3. Marcar Add a README file y, en Add .gitignore, elegir la plantilla Python.
  4. Clic en Create repository.

1.2 Invitar a los compañeros

  1. En el repositorio: Settings → Collaborators → Add people.
  2. Escribir el usuario de GitHub de cada integrante y enviar la invitación.
  3. Cada integrante acepta la invitación desde su correo o desde github.com/notifications.

1.3 Cada integrante abre su propio Codespace

  1. En la página del repositorio, clic en el botón verde Code → pestaña Codespaces → Create codespace on main.
  2. Esperar 1 o 2 minutos. Se abre VS Code en el navegador, con el proyecto a la izquierda y una terminal abajo.
  3. Para volver otro día: Code → Codespaces → clic en el nombre del Codespace existente. No crear uno nuevo cada vez.

Cada integrante trabaja en su propio Codespace, y las horas se descuentan de su propia cuenta. Si prefieres VS Code instalado, en el menú ☰ del Codespace elige Open in VS Code Desktop.

Parte 2. Preparar el entorno y ejecutar Streamlit

Esta parte la hace solo el líder, una vez. Los demás la reciben con git pull (Parte 4).

2.1 Instalar las librerías

En el explorador de la izquierda, crear el archivo requirements.txt con este contenido:

streamlit
pandas
numpy
scipy
statsmodels
matplotlib

En la terminal del Codespace (si no está abierta: Ctrl+ñ o menú ☰ → Terminal → New Terminal):

pip install -r requirements.txt

En el Codespace no hace falta entorno virtual: cada Codespace ya es un entorno aislado.

2.2 Que las librerías se instalen solas en cada Codespace nuevo

Crear la carpeta .devcontainer y dentro el archivo devcontainer.json:

{
  "image": "mcr.microsoft.com/devcontainers/python:3.12",
  "postCreateCommand": "pip install -r requirements.txt",
  "customizations": {
    "vscode": {
      "extensions": ["ms-python.python", "GitHub.copilot-chat"]
    }
  }
}

Así, cuando un compañero cree su Codespace, ya tendrá Python, las librerías y Copilot listos.

2.3 Probar Streamlit

Crear app.py:

import streamlit as st

st.title("Secadores eléctricos – DCA")
st.write("Grupo N – Diseño Experimental y Procesos 3")

Ejecutar en la terminal:

streamlit run app.py --server.enableXsrfProtection false
  1. Abajo a la derecha aparece el aviso Your application running on port 8501 is available. Clic en Open in Browser.
  2. Si no aparece, abrir la pestaña Puertos (Ports) junto a la terminal y hacer clic en el globo del puerto 8501.
  3. Para detener la app: Ctrl+C en la terminal.

La opción --server.enableXsrfProtection false evita un error frecuente en Codespaces al subir archivos CSV a la app.

Parte 3. Construir la app con Copilot

La app tiene 4 páginas, y cada una se construye con un prompt. Abre el chat de Copilot (Ctrl+Alt+I), elige el modo Agent, pega el prompt y revisa cada cambio antes de aceptarlo. Si se agota la cuota, pega el mismo prompt en un chat gratuito (Claude, ChatGPT o Gemini) y copia el código a mano.

Regla del curso: después de cada prompt, ejecuta la app, verifica un resultado a mano y anota en la bitácora qué pediste, qué salió y qué corregiste.

Prompt 0. Contexto (al iniciar cada sesión de chat)

Actúa como tutor experto en Python, Streamlit y Diseño Experimental. Responde en español.
Estamos construyendo una app multipágina de Streamlit (app.py + carpeta pages/) para un
experimento con Diseño Completamente al Azar (DCA): 4 secadores eléctricos (A, B, C, D),
r = 4 corridas por secador, N = 16 corridas. En cada corrida se pesa la bandeja y se mide la
temperatura en el hogar en los tiempos 0, 1..10 (cada 1 min), 12..20 (cada 2), 24..40 (cada 4),
48..80 (cada 8), 96..160 (cada 16) y 192..320 (cada 32) minutos.
Los datos están en formato largo con columnas: corrida, orden, secador, fecha, tiempo_min,
masa_g, temp_hogar_C, temp_amb_C, observaciones.
Usa pandas, numpy, scipy, statsmodels y matplotlib. Código comentado, funciones reutilizables
en utils/ y nada de librerías que no estén en requirements.txt.

Prompt 1. Aleatorización y plantilla de registro

Crea pages/1_Aleatorizacion.py. Debe:
1. Pedir el número de réplicas r (por defecto 4) y una semilla (por defecto 2026).
2. Generar las 4*r corridas (secadores A, B, C, D, cada uno r veces) en orden aleatorio
   usando numpy.random.default_rng(semilla), y mostrar la tabla corrida-orden-secador.
3. Permitir descargar esa tabla como CSV.
4. Generar y permitir descargar la plantilla de registro vacía en formato largo:
   una fila por corrida y por cada tiempo del calendario, con masa_g y temperaturas vacías.
5. Explicar en un st.info por qué se aleatoriza el orden de las corridas en un DCA.

Prompt 2. Carga y validación de datos

Crea utils/datos.py y pages/2_Datos.py. Debe:
1. Permitir subir el CSV de registro con st.file_uploader.
2. Si no se sube nada, usar datos simulados realistas generados con la función
   simular_datos() de utils/datos.py, con el modelo m_t = m_e + (m0 - m_e)*exp(-k*t),
   m0 cerca de 200 g, m_e cerca de 40 g, k distinto por secador y ruido de ±0.5 g.
3. Validar: columnas obligatorias, masa numérica, 4 secadores, sin datos faltantes en masa.
   Mostrar mensajes de error claros y pedagógicos.
4. Calcular la pérdida de masa PM_t(%) = 100*(m0 - m_t)/m0 para cada corrida.
5. Mostrar la tabla y un resumen por corrida: masa inicial, masa final, PM final y temperatura media.
6. Guardar los datos en st.session_state para las demás páginas.

Prompt 3. Curvas de secado y constante k

Crea utils/modelos.py y pages/3_Curvas.py. Debe:
1. Graficar la masa (g) contra el tiempo (min) para cada corrida, con un color por secador.
2. Graficar también la temperatura en el hogar contra el tiempo.
3. Para cada corrida, ajustar m_t = m_e + (m0 - m_e)*exp(-k*t) con scipy.optimize.curve_fit,
   con valores iniciales razonables y límites positivos para m_e y k.
4. Mostrar una tabla por corrida con: secador, m0, m_e, k (1/min), R2 y la pérdida de masa
   a un tiempo t* elegido por el usuario (por defecto 120 min).
5. Mostrar la curva ajustada sobre los puntos para la corrida que el usuario elija.
6. Permitir descargar la tabla resumen como CSV y guardarla en st.session_state.

Prompt 4. ANOVA del DCA

Crea utils/anova.py y pages/4_ANOVA_DCA.py. Usando la tabla resumen (una fila por corrida):
1. Permitir elegir la respuesta: k o pérdida de masa a t*.
2. Ajustar el DCA con statsmodels: ols('respuesta ~ C(secador)') y anova_lm(typ=2).
3. Mostrar la tabla ANOVA con fuente, gl, SC, CM, F y valor p, más el CV (%).
4. Mostrar medias por secador con su error estándar y un diagrama de cajas.
5. Supuestos: Shapiro-Wilk de los residuos, Levene entre secadores, gráfico de residuos
   contra ajustados y QQ-plot, con interpretación en lenguaje sencillo.
6. Si p < 0.05, prueba de Tukey HSD entre secadores.
7. Una interpretación automática prudente, que recuerde que las conclusiones son sobre estos
   4 equipos.
8. Un st.toggle que muestre el código equivalente en R: aov(respuesta ~ secador) y
   agricolae::HSD.test.

Prompt 5. README

Escribe el README.md del repositorio: objetivo del experimento, diseño (DCA, factor,
unidad experimental, réplicas, respuestas), protocolo de medición, cómo abrir el proyecto en
Codespaces y ejecutar la app, estructura de carpetas, integrantes del grupo y limitaciones.

Verificación con lápiz (obligatoria)

  • Con 3 corridas, calcula a mano la pérdida de masa a t* y compara con la app.
  • Con la tabla resumen, calcula a mano SC de tratamientos, SC del error, CM y F, y compara con la tabla ANOVA.
  • Corre el mismo ANOVA en R con el código del Prompt 4 y comprueba que los valores coinciden.

Parte 4. Trabajo en grupo con Git

La regla de oro para no pisarse el trabajo es que cada integrante trabaja en archivos distintos.

Integrante Responsable de Archivos
1 (líder) Estructura, entorno y aleatorización requirements.txt, .devcontainer/, app.py, pages/1_Aleatorizacion.py
2 Datos y registro real utils/datos.py, pages/2_Datos.py, data/registro_secadores.csv
3 Curvas y modelo de secado utils/modelos.py, pages/3_Curvas.py
Todos ANOVA, README y bitácora pages/4_ANOVA_DCA.py, README.md, bitacora_IA.md (por turnos)

Cada vez que trabajes, sigue este ciclo en la terminal del Codespace:

git pull                       # 1. traer lo que subieron los compañeros
# ... trabajar, ejecutar y verificar ...
git add .                      # 2. preparar los cambios
git commit -m "Agrega ajuste del modelo exponencial por corrida"
git push                       # 3. subir a GitHub

También puedes hacerlo con clics en el panel Control de código fuente (Ctrl+Shift+G): + para preparar, Confirmar para el commit y Sincronizar cambios para subir y bajar.

Si git push es rechazado porque alguien subió cambios antes, ejecuta git pull y luego git push otra vez. Si aparece un conflicto en un archivo, no borres nada: pídele a Copilot en modo Ask que te explique el conflicto, o avísale al docente.

Los datos reales se suben a data/registro_secadores.csv. En el Codespace basta con arrastrar el archivo desde tu computador a la carpeta data/ del explorador y luego hacer commit.

Cuidar las horas gratuitas y problemas frecuentes

Las horas de Codespaces se cuentan por núcleo: una máquina de 2 núcleos gasta 2 horas de cuota por cada hora encendida (GitHub Docs).

Cuenta Cuota mensual Horas reales en máquina de 2 núcleos Almacenamiento
GitHub Free 120 horas-núcleo 60 horas 15 GB
GitHub Pro (Student Developer Pack) 180 horas-núcleo 90 horas 20 GB

Para que alcance todo el semestre:

  • Usa siempre la máquina de 2 núcleos (la que viene por defecto).
  • Al terminar, detén el Codespace: en github.com/codespaces, clic en … → Stop codespace. Cerrar la pestaña no lo detiene de inmediato.
  • Ten un solo Codespace por repositorio y borra los que no uses (… → Delete), después de hacer git push.
  • Revisa tu consumo en Settings → Billing and licensing.
Problema Solución
Al subir el CSV a la app aparece un error 403 Ejecutar Streamlit con --server.enableXsrfProtection false (Parte 2.3)
ModuleNotFoundError: No module named 'streamlit' Ejecutar pip install -r requirements.txt
La app no abre en el navegador Pestaña Puertos → globo del puerto 8501
ModuleNotFoundError: No module named 'utils' Ejecutar streamlit run app.py desde la raíz del proyecto y crear utils/__init__.py vacío
El Codespace se detuvo solo Es normal tras un rato sin uso; se reabre desde Code → Codespaces sin perder archivos
curve_fit no converge en una corrida Revisar que la masa esté sin tara y sin datos mal digitados, y pedir a Copilot mejores valores iniciales

Entregables y lista de verificación

Cada grupo envía al docente el enlace a su repositorio. El repositorio debe contener todo lo siguiente.