lección 4
Día 2 (tarde) — El embudo entero: de la visita al contrato
Con los datos ya limpios, pasas a SQL para montar el embudo visita → contacto → contrato y sus tasas. Confirmarás el titular de Gonzalo (el destacado dobla visitas y contactos) y verás que la tasa global a contrato parece perfectamente sana. Nadie sospecha nada mirando el total.
⏱ 55 min
### Ya sabes agrupar y cruzar. Ahora sobre datos limpios
Ya sabes agrupar filas con GROUP BY, contar con COUNT y cruzar dos tablas con JOIN: lo aprendiste cuando viste SQL, y lo has usado para responder preguntas de una tabla sola. Hasta ahora, en este encargo, has trabajado en Pandas porque tocaba abrir ficheros crudos y limpiarlos, y Pandas es la navaja para eso. Pero la exploración se acabó: los datos ya están limpios, y a partir de aquí el análisis —los números que van a ir a la demo del viernes— se produce en SQL, que es como trabaja el resto del equipo de datos de NidoHogar. Las tablas limpias viven en la base de datos con los tipos correctos: precios que son números, fechas que son fechas, zonas canónicas, el tipo de anuncio en dos categorías y los duplicados fuera. Tú te sientas encima y preguntas.
Y la primera pregunta es el titular de Gonzalo, tal cual: ¿de verdad el destacado dobla las visitas y los contactos? Vamos a confirmarlo, porque un buen análisis empieza dándole la razón a quien la tiene antes de buscar dónde no la tiene. Si le negaras de entrada lo que es cierto —que el destacado trae más tráfico—, perderías toda credibilidad para el momento en que le digas lo que no ve. Así que hoy construyes el embudo entero y confirmas la superficie. La grieta, si la hay, aparecerá cuando el total se parta en trozos, y eso es mañana.
### Por qué el análisis vive en SQL y la exploración en Pandas
Puede chocarte cambiar de herramienta a mitad de encargo: dos días en Pandas y ahora SQL. No es capricho ni moda, responde a para qué es buena cada una. Pandas brilla explorando y limpiando datos crudos y sucios: lee un CSV desordenado, te deja mirarlo, transformarlo columna a columna, probar cosas. SQL brilla consultando datos ya ordenados en tablas: agrupar, cruzar, agregar, calcular tasas sobre millones de filas con una claridad que en Pandas se vuelve farragosa. La regla práctica que usan los equipos: se limpia donde es cómodo limpiar (Pandas) y se analiza donde es cómodo analizar (SQL). En NidoHogar, además, el equipo de datos trabaja en SQL sobre las tablas limpias, así que producir el análisis ahí significa que otros pueden leer, revisar y reutilizar tus consultas. Un número que va a una decisión conviene que esté en el idioma común del equipo.
Hay también una razón de fiabilidad. La limpieza que hiciste en Pandas quedó plasmada en unas tablas limpias y estables; el análisis en SQL parte de ahí, de un terreno firme y compartido, no de tu cuaderno personal donde cada uno limpia a su manera. Esta separación —limpieza reproducible primero, análisis sobre lo limpio después— es la que hace que dos analistas distintos, partiendo de las mismas tablas, lleguen al mismo número. Es la diferencia entre un análisis que es una obra de arte irrepetible en la cabeza de una persona y uno que es un proceso que cualquiera del equipo puede seguir y verificar. Y para los números que van a una demo donde se decide frenar un producto, esa verificabilidad no es un lujo: es lo que los hace defendibles.
### De dónde viene la idea de embudo (y por qué está en todas partes)
El embudo no lo inventó la tecnología; es una idea de vendedores de hace más de un siglo. En 1898, un publicista llamado Elias St. Elmo Lewis describió cómo un comprador pasa por etapas antes de comprar: primero se entera de que algo existe, luego le interesa, luego lo desea, y por fin actúa y compra. Con el tiempo esa secuencia se dibujó como un embudo, ancho arriba (mucha gente se entera) y estrecho abajo (pocos compran), porque en cada etapa se cae parte de la gente. La idea es tan buena que se ha reencarnado en cada negocio nuevo: en una tienda online es visita → carrito → compra; en un juego es instalación → jugador activo → pagador; y en NidoHogar es visita → contacto → contrato. Son el mismo esqueleto con otros nombres, y por eso, si entiendes un embudo, los entiendes todos.
Lo valioso de mirar un negocio como un embudo es que te dice dónde buscar el problema: no en el total, sino en el paso donde más gente se cae. Un embudo que pierde poca gente en cada escalón está sano; uno con un escalón donde se desploma la mitad tiene ahí su cuello de botella, y arreglar ese escalón vale más que mejorar todos los demás. Esta semana vas a mirar cada escalón del embudo de NidoHogar, y la pregunta de fondo siempre será la misma: ¿dónde se está cayendo la gente que debería acabar firmando, y por qué justo ahí? El embudo no es un adorno de presentación: es un mapa que te dice dónde cavar.
### Contar el embudo: una tabla por escalón
El embudo son tres tablas de hechos —visitas, contactos, contratos— que apuntan todas al anuncio. Contar cada escalón es un COUNT sobre su tabla. Pero la pregunta de Gonzalo no es cuántas visitas hay en total, sino cuántas recibe de media un anuncio destacado frente a uno normal. Eso obliga a dos pasos: primero contar las visitas de cada anuncio (agrupando visitas por anuncio_id), y luego promediar esos recuentos por tipo de anuncio, que es un atributo que vive en la tabla anuncios, no en visitas. De ahí el JOIN: pegamos el tipo de cada anuncio a su recuento de visitas para poder promediar por tipo.
Este patrón —agregar en dos niveles— es la clave y conviene fijarlo. No preguntas «cuántas visitas hay por tipo» directamente, porque eso mezcla anuncios con muchas y con pocas visitas en un solo saco. Preguntas «cuántas visitas tiene cada anuncio» y luego «cuál es la media de esos por tipo». La diferencia parece sutil pero es la que responde de verdad a «¿cuántas visitas recibe un anuncio destacado?». Es el mismo razonamiento del tique del supermercado de ayer: primero cuentas por la unidad correcta (el anuncio), luego promedias.
1WITH v AS (2 SELECT anuncio_id, COUNT(*) AS n3 FROM visitas4 GROUP BY anuncio_id5)6SELECT a.tipo_anuncio, ROUND(AVG(v.n), 2) AS visitas_por_anuncio7FROM anuncios a8JOIN v USING (anuncio_id)9GROUP BY a.tipo_anuncio10ORDER BY visitas_por_anuncio DESC;
Visitas por anuncio según el tipo: primero cuenta por anuncio (CTE), luego promedia por tipo.
### Leer una consulta como una frase, y las CTE como pasos
La consulta de las visitas por anuncio usa una CTE —eso del WITH v AS (...)—, y conviene entender qué es porque las vas a usar todo el tiempo esta semana. Una CTE (siglas en inglés de expresión de tabla común) es una tabla temporal con nombre que existe solo durante la consulta: le pones un nombre, la defines una vez, y la usas después como si fuera una tabla más. Sirve para partir un cálculo en pasos legibles en vez de amontonar subconsultas unas dentro de otras. Aquí el primer paso (v) cuenta las visitas de cada anuncio, y el segundo paso promedia. Leído como una frase: «primero, para cada anuncio cuenta sus visitas; luego, agrupa esos recuentos por tipo y promédialos». Cada CTE es una oración de esa frase.
Esta forma de escribir —en pasos con nombre— no es solo por elegancia: es por poder depurar. Cuando una consulta de tres pasos te da un número raro, puedes ejecutar solo la primera CTE y mirar si ya salía mal ahí, luego la segunda, y así aislar dónde se tuerce. Una consulta monolítica de veinte líneas anidadas, en cambio, o funciona o no, y cuando no funciona no sabes por dónde empezar. El mismo principio que en Pandas, donde exploras paso a paso: partir el problema en trozos que puedes mirar por separado. Un analista que escribe SQL en pasos con nombre es un analista que puede encontrar sus propios errores; uno que lo amontona todo en una consulta ilegible se queda mirándola sin saber qué falla.
Consejo de senior: cuando montes una consulta con varias CTE y el resultado no cuadre, no la mires entera buscando el fallo. Ejecuta solo la primera CTE (un SELECT * FROM esa_cte) y comprueba que da lo que esperas; luego la segunda. Aislar el paso que falla te lleva segundos; releer veinte líneas anidadas, media hora. Es la misma disciplina que aislar una variable en un experimento: cambia una cosa, mira el efecto, avanza. La legibilidad de tu SQL no es estética, es tu capacidad de depurarte a ti mismo.
### El embudo global y su tasa reina
Con las tres tablas puedes montar el embudo entero: cuántas visitas, cuántos contactos, cuántos contratos, y las tasas que los enlazan. La tasa que de verdad manda es la de contacto→contrato, porque el contrato es lo que paga la comisión: de todos los interesados que escribieron, ¿qué porcentaje acabó firmando? Se calcula dividiendo contratos entre contactos. Y hay otra, la de visita→contacto, que mide qué parte de las miradas se convierte en mensaje. Fíjate en un detalle de SQL: para dividir dos enteros y que no te devuelva otro entero (redondeando a cero), hay que multiplicar por 100.0 con el punto decimal, que fuerza el cálculo en decimales.
Cuando calcules la tasa global de contacto→contrato te va a salir alrededor del 6,5%. Y aquí viene lo importante, la razón de que este caso exista: ese 6,5% tiene una pinta perfectamente sana para un marketplace de alquiler. Ni alarma ni euforia; un número normal, del montón, de esos que ves en un panel y pasas de largo. Precisamente por eso nadie ha sospechado nada del destacado: mirando el total, NidoHogar parece un portal que funciona con normalidad. La grieta no está en la foto global. Está escondida en el promedio, esperando a que alguien lo desagregue.
Un inciso sobre eso de que un número «parece sano», porque es una trampa sutil. ¿Sano comparado con qué? Un 6,5% no es bueno ni malo en el vacío; solo lo es en relación con algo: con lo que hacía este mismo portal el trimestre pasado, con lo que hace la competencia, con lo que la dirección espera. A eso se le llama un punto de comparación o benchmark, y es lo primero que te va a pedir Irene con su «¿y eso comparado con qué?». El problema de NidoHogar es que nadie tiene un benchmark de la tasa a contrato, así que el 6,5% se acepta como «normal» por pura falta de referencia: nadie sabe si debería ser el 4% o el 10%. Una cifra sin comparación es una cifra muda, y aceptar un total como «sano» solo porque no chirría es justo lo que ha dejado la grieta escondida todo este tiempo.
Consejo de senior: nunca presentes una cifra sola; preséntala siempre contra algo. Contra el periodo anterior («ha subido del 6% al 6,5%»), contra otro grupo («el normal convierte al 8%, el destacado al 5%»), contra un objetivo («queríamos el 8%»). Una cifra sola no se puede juzgar y por eso no decide nada; una cifra comparada cuenta una historia. Si un stakeholder te da un número a secas —«tenemos un 6,5% de conversión»—, tu primera pregunta, la de Irene, es siempre «¿comparado con qué?». La mitad de los análisis inútiles lo son por presentar cifras que no se comparan con nada.
No te fíes de una tasa global sana. Un promedio junta cosas que pueden comportarse al revés entre sí, y el resultado del conjunto puede parecer normal mientras cada trozo esconde una historia. Un 6,5% de conversión global puede ser la mezcla de un grupo que convierte al 8% y otro al 5%, o de contactos atendidos en minutos que convierten al 14% y contactos ignorados que convierten al 0,5%. La media te tranquiliza justo cuando deberías desconfiar. La primera pregunta ante cualquier total que parece bueno es siempre: ¿y si lo parto en trozos?
### Ordenar el resultado también es comunicar
Un detalle pequeño que marca la diferencia entre una tabla que se lee y una que hay que descifrar: ordena siempre el resultado por lo que quieres que se vea. Si sacas la tasa por tipo de anuncio, un ORDER BY tasa deja al más bajo arriba (o abajo) y el ojo va directo a la comparación; si sacas las zonas por contratos, un ORDER BY contratos DESC pone las importantes primero. Una tabla sin ordenar sale en el orden en que la base la tenía, que no significa nada, y obliga a quien la lee a buscar. El orden de las filas es una forma silenciosa de decir «mira aquí primero», y usarlo bien es parte de contar la historia ya desde la propia consulta, antes de llegar al gráfico.
Lo mismo con el LIMIT cuando hay muchas filas: sacar el top 10 de zonas por contratos es más legible que volcar las 40, y para una demo casi siempre quieres los pocos que importan, no el listado completo. Pero cuidado, porque un LIMIT sin un ORDER BY delante es una trampa: te da «diez filas cualesquiera», no «las diez primeras de algo», y esas diez cualesquiera parecen un ranking sin serlo. La regla es que LIMIT siempre va detrás de un ORDER BY que dice según qué son las primeras. Ordenar y limitar no son adornos: son las dos herramientas con las que conviertes un volcado de datos en una respuesta legible a una pregunta concreta.
### Comprobar que un número tiene sentido antes de fiarte de él
Cada vez que una consulta te devuelva un número, antes de anotarlo o de mandárselo a nadie, hazle dos comprobaciones de sentido común que cuestan segundos y salvan reuniones enteras. La primera: ¿está en el rango que esperabas? Si calculas la tasa a contrato y te sale un 60%, algo está mal, porque sabes que en un portal de alquiler eso es imposible; probablemente el denominador está recortado por un JOIN interno. Si te sale 0%, otro error. Una tasa a contrato razonable está entre el 2% y el 15%, y salirte mucho de ahí es una alarma, no un descubrimiento. La segunda: ¿suman las partes al total? Si el destacado tiene X contratos y el normal Y, X más Y debería dar los 1.169 contratos totales; si no, se te ha caído o duplicado algo por el camino.
Esta costumbre —desconfiar de tus propios resultados hasta que pasan el olfato— es lo que separa a un analista que se puede soltar a producción de uno al que hay que revisar todo. No es inseguridad: es método. Los números salen de consultas que escribes deprisa, con JOIN que pueden multiplicar filas, filtros que pueden recortar de más, y tipos que pueden traicionarte. Un resultado que no has sometido a un par de comprobaciones de sentido es una hipótesis, no un hecho. Y el peor momento para descubrir que tu número estaba mal es en la demo del viernes, cuando Álvaro haga una cuenta rápida de cabeza y vea que no cuadra. Mejor que el error lo caces tú, en tu cuaderno, con una comprobación de treinta segundos.
Consejo de senior: cuando saques una cifra clave, calcúlala de dos formas distintas y comprueba que coinciden. La tasa a contrato global la puedes sacar dividiendo totales, o sumando las tasas por tipo ponderadas, o contando de otra manera; si las dos rutas dan lo mismo, te fías; si no, tienes un error que encontrar. Los analistas con experiencia tienen cicatrices de números que parecían bien y estaban mal, y por eso triangulan las cifras importantes antes de firmarlas. La confianza en un número no viene de que la consulta corriera sin error, viene de haberlo comprobado por dos caminos.
### Una tasa es una fracción, y la fracción tiene trampa
Casi todo tu análisis de esta semana son tasas: visita→contacto, contacto→contrato, porcentaje sin responder. Una tasa no es más que una fracción —un numerador dividido por un denominador— pero elegir bien cada uno es donde se juega la corrección del número, y donde Irene te va a apretar con su «¿y eso comparado con qué?». El numerador es lo que cuentas (los contratos); el denominador es sobre qué base lo cuentas (los contactos). El mismo numerador con distinto denominador cuenta historias distintas: 1.169 contratos sobre 17.880 contactos es un 6,5%, pero 1.169 contratos sobre 55.275 visitas es un 2,1%, y 1.169 sobre 4.700 anuncios es «una cuarta parte de los pisos se alquila». Los tres son ciertos y responden a preguntas distintas. Un porcentaje sin decir su denominador al lado no es un dato: es una adivinanza.
Y hay una segunda trampa en las tasas del embudo, técnica pero importante: el tipo de JOIN. Para calcular la tasa contacto→contrato necesitas todos los contactos en el denominador, tengan contrato o no. Si cruzas contactos con contratos con un JOIN normal (interno), te quedas solo con los contactos que convirtieron, porque el interno descarta lo que no casa: el denominador se encoge a los que ya tienen contrato y la tasa te sale del 100%, un disparate. Por eso se usa un LEFT JOIN: mantiene todos los contactos y les pega el contrato si lo hay, o un hueco si no. El LEFT JOIN es el que conserva el denominador entero, y confundirlo con el interno es uno de los errores más habituales al calcular tasas de conversión.
Consejo de senior: ante cualquier tasa de conversión, pregúntate primero «¿quién tiene que estar en el denominador?» y asegúrate de que tu JOIN no lo recorta. La regla práctica: si mides «de todos los X, cuántos llegaron a Y», empiezas por la tabla de X y le pegas Y con un LEFT JOIN, nunca al revés ni con JOIN interno. El interno silenciosamente tira a los X que no llegaron a Y, que son justo los que necesitas contar. Una tasa que te sale sospechosamente alta —del 40%, del 80%— casi siempre es un denominador recortado por un JOIN mal elegido.
La diferencia entre los dos tipos de JOIN es de las que, mal entendida, produce errores que parecen bien. Un JOIN interno solo conserva las filas que casan en las dos tablas; un LEFT JOIN conserva todas las de la tabla izquierda y les pega las de la derecha cuando hay coincidencia, o un hueco cuando no. Para una tasa de conversión esto es decisivo: los que no convirtieron —los contactos sin contrato— son justo el grueso del denominador, y son los que el JOIN interno tira a la basura. Es el mismo principio que el nulo con significado del martes: lo que «no pasó» (no hubo contrato) es información, y hay que conservarlo, no descartarlo. Un JOIN interno donde debía ir un LEFT es, en el fondo, la misma trampa que un dropna() sobre los contactos sin respuesta: borrar en silencio a los que no llegaron, que son los que dan sentido a la tasa.
### Contexto: por zona, por ciudad y por precio
Antes de partir la tasa por tipo de anuncio —que es mañana—, conviene dar contexto: dónde se concentran los contratos y a qué precio se alquilan los pisos. Esto no es el hallazgo, es el enmarcado que ayuda a contar la historia y a responder las preguntas de contexto que Álvaro suele lanzar («¿y en las zonas caras qué pasa?»). Aquí es donde el JOIN con zonas cobra sentido: gracias a que ayer normalizaste la zona al canónico, ahora anuncios.zona casa con zonas.zona y puedes traer el precio de mercado de cada barrio. Si no hubieras normalizado, este cruce dejaría fuera a media base sin decir nada.
Un cruce que merece la pena es comparar el precio medio de los pisos que se alquilan con el de los que no. Se hace con un LEFT JOIN entre anuncios y la lista de anuncios que aparecen en contratos: los que casan están alquilados, los que no, no. Es un contexto útil —¿se alquilan más los baratos?— pero, ojo, es contexto: ninguna de estas cifras por zona o por precio cambia por sí sola la decisión sobre el Destacado Premium. Es color para la historia, no el titular.
### La corazonada de Cristina: las fotos
A media tarde, Cristina te reabre el alcance por Slack: «Oye, ya que estás con el embudo, ¿le echas un ojo también a las fotos? Tengo la corazonada de que los anuncios con más fotos vuelan. Sería un puntazo para el comité.» Es la realidad de trabajar con una PM que recibe presión de arriba: te llega una petición nueva a mitad de camino. Irene te ayuda a acotar sin quedar mal: sí, puedes mirar si más fotos van con más visitas —el dato está y es una consulta rápida—, pero la pregunta que importa sigue siendo si eso llega a contrato. Lo metes como una observación rápida, no como el eje del análisis.
Absorber un cambio de alcance sin perder el hilo del encargo principal es una habilidad de oficio. Cuando un stakeholder te añade «una cosita más» a mitad de semana, la respuesta no es «no» (queda mal y a veces la cosita es buena) ni «claro» a todo (se te va la semana en peticiones sueltas). Es acotar: «lo miro como observación rápida, pero el encargo sigue siendo el destacado». Las fotos, ya te adelanto, correlacionan con las visitas pero no mueven la tasa a contrato: es una observación para Producto, no una recomendación. Manténla como color, nunca como segundo titular que compita con el hallazgo de verdad.
### Un vistazo a las funciones de ventana, que usarás el jueves
Hay una familia de operaciones de SQL que te va a resultar muy útil y que conviene presentar antes de necesitarla: las funciones de ventana. La idea es sencilla de intuir con un ejemplo. Cuando quieres el porcentaje que representa cada grupo sobre el total —«los contactos sin respuesta son el 41% de todos»—, necesitas dos cosas a la vez: el recuento de cada grupo y el recuento total. Un GROUP BY normal te da el primero pero pierde el segundo, porque colapsa las filas. Una función de ventana, escrita como algo OVER (), calcula sobre el conjunto entero sin colapsarlo, así que puedes tener el recuento del grupo y el total en la misma fila y dividirlos. Es la forma limpia de calcular «cuánto pesa cada parte sobre el todo» sin hacer dos consultas y cruzarlas.
No hace falta que las domines esta semana; solo que reconozcas el patrón cuando aparezca, porque lo usarás para repartir los contactos por tramo y ver qué porcentaje del total cae en cada uno. La expresión SUM(COUNT(*)) OVER () suena rara la primera vez —una suma de un recuento— pero se lee así: cuenta por grupo, y luego suma todos esos recuentos por encima de la ventana entera para tener el total. Las funciones de ventana son de las herramientas que separan un SQL de aficionado de uno de oficio, porque resuelven en una consulta lo que sin ellas obliga a subconsultas o a cálculos a mano; pero como todo, se aprenden usándolas, y el jueves tendrás tu primera ocasión.
### Contar filas o contar cosas: COUNT(*) frente a COUNT(DISTINCT)
Hay una distinción en SQL que parece de detalle y que, en un embudo, es la diferencia entre una cifra correcta y una inflada: COUNT(*) cuenta filas, y COUNT(DISTINCT columna) cuenta valores distintos de esa columna. Vuelve a la granularidad de ayer: si cuentas COUNT(*) sobre contactos agrupando por anuncio, cuentas mensajes; si lo que quieres es «cuántos pisos recibieron algún contacto», necesitas COUNT(DISTINCT anuncio_id), porque un piso con quince mensajes tiene que contar como un piso, no como quince. La regla práctica: cuando cuentes algo que puede repetirse dentro del grupo —un anuncio con muchos contactos, un inquilino con muchas visitas—, pregúntate si quieres contar las apariciones (COUNT(*)) o las cosas distintas (COUNT(DISTINCT)). Elegir mal aquí no da error; da un número que parece bien y está mal.
Lo verás con fuerza mañana al calcular la tasa a contrato por tipo. Un contacto puede, en teoría, aparecer más de una vez en un cruce si no tienes cuidado con los JOIN, y un contrato apunta a un contacto concreto. Contar COUNT(DISTINCT contacto_id) y COUNT(DISTINCT contrato_id) te asegura que cuentas contactos y contratos de verdad, no filas duplicadas por el cruce. Es una red de seguridad barata: en una tasa, poner DISTINCT en el numerador y el denominador te protege de que un JOIN mal montado multiplique filas y te dé una tasa disparatada. Cuando una tasa te salga por encima del 100% —que es imposible—, el primer sospechoso es siempre un COUNT(*) donde debía ir un COUNT(DISTINCT).
Consejo de senior: en cualquier tasa de conversión, usa COUNT(DISTINCT) en el numerador y en el denominador por defecto, aunque creas que no hay duplicados. Cuesta lo mismo de escribir y te blinda contra el error más difícil de detectar: un cruce que multiplica filas en silencio. Si luego compruebas que no había duplicados, no has perdido nada; si los había, te has salvado de una cifra falsa que habría llegado a una reunión. La paranoia con el denominador es una virtud en este oficio: el numerador se ve, el denominador se olvida, y casi todos los errores de tasa viven en el denominador.
Al final del día tendrás el embudo confirmado: el destacado gana en visitas y en contactos, la tasa global a contrato parece sana, y tienes el contexto por zona y por precio. Todo apunta a que Gonzalo tiene razón y el Destacado Premium es buena idea. Y sin embargo, hay una pregunta que aún no has hecho y que lo cambia todo: esa tasa global tan tranquilizadora, ¿es igual para los destacados que para los normales? Mañana la partes en dos y aparece la primera grieta.
Regístrate para guardar tu progreso.