Indice / Practicas / EJ-6.10
EJ-6.10 · Sesionizar eventos por hueco de 30 minutos
600 eventos sueltos se convierten en 128 sesiones. La palabra «sesión» no existe en los datos: hay que inventarla a partir de los huecos, y decidir cuán grande tiene que ser un hueco para que corte.
Qué hay que hacer
A partir de eventos_usuarios.json, producir
sesiones_usuarios.csv: una fila por sesión, con cuándo empezó y terminó,
cuántos eventos tuvo, cuántas páginas distintas y cuánto se compró.
ELI5Explicado facil
Tenés el registro de cada vez que alguien tocó algo en la web. Nadie anotó «acá empieza una visita».
Pero si ves cuatro clics a las 8:07, 8:24, 8:35 y 8:45, y el siguiente recién a las 11:44, es evidente que fueron dos visitas. Lo que las separa es el hueco - y cuánto tiene que durar el hueco lo decidís vos, no los datos.
Archivos
- Entrada ·
eventos_usuarios.json - Objetivo ·
sesiones_usuarios.csv - Herramienta ·
comparar.py
El orden es parte del algoritmo
El archivo viene en el orden en que se registraron los eventos, no en el orden de
cada usuario. Y diff() compara con la fila anterior de la
tabla.
Sin ordenar por (usuario, fecha) salen 206 cortes en
vez de 88, y aparecen 292 diferencias negativas: el evento «anterior» resultó ser
posterior. Los dos programas corren, los dos devuelven un número plausible, y no hay
forma de saber cuál está bien mirando el resultado.
El assert que lo atrapa es una línea: ninguna duración puede ser
negativa.
La cuenta que tiene que cerrar
88 cortes + 40 usuarios = 128 sesiones. Cada usuario arranca con
la sesión 1 aunque nunca haya tenido un hueco; cada corte agrega una. Si tu archivo
tiene 88 filas, te olvidaste del + 1.
Cómo saber si te salió
python comparar.py mi_salida.csv sesiones_usuarios.csv
Antes de arrancar, mirá los datos
| { | |
|---|---|
| "origen": "web-analytics" | vacio |
| "eventos": [ | |
| { | |
| "usuario": "u10" | vacio |
| "fecha": "2026-08-24T15:11:00" | vacio |
El contrato de salida
sesiones_usuarios.csv · 128 filas
| Columna | Tipo | Que lleva |
|---|---|---|
usuario | texto | El identificador del usuario |
sesion | entero | Numero de sesion DENTRO de ese usuario, empezando en 1 |
inicio | texto | Primer evento de la sesion, AAAA-MM-DD HH:MM:SS |
fin | texto | Ultimo evento de la sesion, mismo formato |
eventos | entero | Cuantos eventos tiene la sesion |
minutos | decimal | Duracion: fin menos inicio, en minutos, con 1 decimal |
paginas | entero | Paginas distintas visitadas en la sesion |
compras | entero | Eventos de tipo compra |
importe | decimal | Suma de importe, redondeada a 2 decimales. 0.00 si no hubo compras |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| 3 eventos con la fecha invalida (vacia, 2026-08-32, 24/08/2026 10:00) | 3 de 600 | descartar descartar antes de sesionizar. Una fecha que no se puede ordenar rompe el calculo de huecos | fuera de sesiones_usuarios.csv |
| 1 evento con el usuario vacio | 1 de 600 | descartar: descartar: no se le puede atribuir a nadie | fuera de sesiones_usuarios.csv |
| 82 eventos sin pagina y 520 sin importe | son campos opcionales segun el tipo de evento | NO NO se descartan ni se imputan. nunique ignora los ausentes y sum los trata como 0 | sesiones_usuarios.csv |
| el archivo NO viene ordenado por usuario y fecha | las 600 filas | ordenar ordenar por (usuario, fecha) ANTES de calcular los huecos. Sin eso salen 206 cortes en vez de 88 | sesiones_usuarios.csv |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- descartar los eventos sin usuario
- descartar los eventos con fecha invalida
- ordenar por usuario y fecha, con kind='stable'
- calcular el hueco con groupby('usuario')['fecha'].diff()
- marcar los cortes y numerar con cumsum() + 1
- agrupar por (usuario, sesion)
El orden va antes del diff porque diff compara con la fila anterior de la TABLA, no con el evento anterior del usuario. Y los invalidos se descartan antes de ordenar: una fecha NaT no tiene lugar en ningun orden y contaminaria los huecos de su usuario.
Conteos clave
| eventos en el archivo | 600 |
|---|---|
| eventos validos | 596 |
| usuarios con eventos validos | 40 |
| cortes (huecos de mas de 30 minutos) | 88 |
| sesiones_usuarios.csv | 128 |
| compras en total | 81 |
| por motivo |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(sesiones) == 128
88 + 40 == 128
int(sesiones['eventos'].sum()) == 596
sesiones['usuario'].nunique() == 40
sesiones.duplicated(subset=['usuario', 'sesion']).sum() == 0
sesiones.groupby('usuario')['sesion'].min().eq(1).all()
(sesiones['minutos'] >= 0).all()
(sesiones['paginas'] <= sesiones['eventos']).all()
int(sesiones['compras'].sum()) == 81
PISTASSi te trabas, abri esto
- Las sesiones son cortes MAS usuarios: cada usuario arranca con la sesion 1 aunque nunca haya tenido un hueco. 88 + 40 = 128.
- Sin ordenar por (usuario, fecha) salen 206 cortes. Los dos programas corren y los dos dan un numero plausible.
- Un assert de que ninguna duracion es negativa atrapa el error de orden solo.
- cumsum() sobre la mascara de cortes es lo que convierte los cortes en numeros de sesion. Vale la pena mirarlo una vez con atencion.
Así tiene que quedar
| usuario | sesion | inicio | fin | eventos | minutos | paginas | compras | importe |
|---|---|---|---|---|---|---|---|---|
| u01 | 1 | 2026-08-24 10:22:00 | 2026-08-24 10:38:00 | 2 | 16.0 | 0 | 2 | 370.37 |
| u01 | 2 | 2026-08-24 11:48:00 | 2026-08-24 12:12:00 | 7 | 24.0 | 3 | 1 | 477.81 |
| u01 | 3 | 2026-08-24 13:39:00 | 2026-08-24 13:54:00 | 2 | 15.0 | 1 | 1 | 476.81 |
| u01 | 4 | 2026-08-24 15:36:00 | 2026-08-24 16:09:00 | 6 | 33.0 | 4 | 0 | 0.0 |
| u02 | 1 | 2026-08-24 09:03:00 | 2026-08-24 09:36:00 | 6 | 33.0 | 3 | 0 | 0.0 |
Tres tienen la fecha inválida - una vacía, un 2026-08-32 que no
existe, y una escrita 24/08/2026 10:00 - y uno no tiene usuario.
Van afuera antes de ordenar. Una fecha NaT no tiene
lugar en ningún orden, y si se cuela, contamina los huecos de su usuario: el evento
anterior y el siguiente quedan separados por una diferencia que no significa
nada.
30 minutos es la convención de la industria para analítica web, y por eso es el que usa el contrato. Con 15 minutos saldrían más sesiones y más cortas; con una hora, menos y más largas. Ninguno es «el correcto»: lo que importa es que esté escrito, para que dos informes del mismo mes se puedan comparar.
Y el borde también: más de 30 minutos corta; exactamente 30, no.