Práctica: Analizar tendencias de ventas con agregaciones

Lectura
30 min~10 min lectura

Introducción: El Poder de las Agregaciones para el Análisis de Ventas

En el mundo del comercio moderno, los datos de ventas son un activo invaluable. Sin embargo, los números crudos en una tabla son difíciles de interpretar. ¿Cuál es nuestro producto más vendido este trimestre? ¿Qué región está experimentando el mayor crecimiento? ¿Existen patrones estacionales en nuestras ventas? Responder estas preguntas requiere la capacidad de resumir, segmentar y calcular métricas sobre grandes volúmenes de datos en tiempo real. Aquí es donde las agregaciones de Elasticsearch se convierten en la herramienta fundamental.

Esta lección práctica te guiará a través de la implementación de un pipeline completo de análisis de tendencias de ventas. Iremos más allá de las consultas simples para construir un dashboard analítico potente y flexible. Utilizaremos un conjunto de datos simulado de transacciones de ventas, que incluirá campos como producto, categoría, región, fecha, cantidad y monto. Nuestro objetivo será transformar estos datos brutos en información procesable mediante el uso estratégico de agregaciones anidadas, de pipeline y métricas avanzadas.

El enfoque será eminentemente práctico. Configuraremos un índice, indexaremos documentos de ejemplo y, paso a paso, construiremos consultas de agregación cada vez más complejas. Finalmente, visualizaremos estos resultados en Kibana para crear un panel de control ejecutivo. Al final de esta lección, tendrás un entendimiento sólido de cómo aplicar Elasticsearch para resolver problemas analíticos del mundo real en el dominio de ventas y más allá.

Concepto Clave: Agregaciones como Lentes Analíticos

Imagina que tienes una montaña de fichas de colores, formas y tamaños diferentes (tus documentos en Elasticsearch). Si quisieras saber cuántas fichas rojas hay, usarías un bucket aggregation (agregación de cubos) como un filtro que solo deja pasar el color rojo, agrupándolas. Si luego quisieras saber el tamaño promedio de esas fichas rojas, aplicarías una metric aggregation (agregación métrica), como una regla que mide cada ficha en el grupo. Las agregaciones en Elasticsearch funcionan exactamente así: son operaciones que procesan un conjunto de documentos para producir información resumida.

Una analogía más cercana al negocio sería pensar en un informe de ventas tradicional. Una agregación de términos por "región" es como el desglose por capítulos del informe. Dentro del capítulo "Europa", una sub-agregación de "suma" sobre el campo "ventas" es el total que aparece al final de ese capítulo. La potencia de Elasticsearch radica en que puede generar cientos de estos "informes" multidimensionales, con cálculos complejos, sobre terabytes de datos, en cuestión de milisegundos, y lo hace de manera programática y flexible.

Es crucial entender la distinción entre los dos tipos principales. Las agregaciones de cubos (Buckets) crean conjuntos de documentos (cubos), como agrupar por mes, por código postal o por rango de precios. Las agregaciones métricas (Metrics) calculan valores estadísticos sobre los documentos dentro de esos cubos, como la suma, el promedio, la desviación estándar o el percentil 95. La verdadera magia ocurre cuando las anidas: un cubo (por región) contiene una métrica (suma de ventas), y dentro de ese mismo cubo, otro cubo (por producto) con sus propias métricas.

Tip Profesional: Piensa siempre en tu agregación como un árbol. La raíz es el conjunto completo de datos. Cada agregación de cubo crea una rama que divide los datos. Las agregaciones métricas son las hojas que dan el fruto (el valor calculado) para cada rama. Planificar tu árbol antes de escribir el código te ahorrará mucho tiempo.

Cómo Funciona en la Práctica: De Datos Crudos a Tendencias

Vamos a simular un escenario común. Nuestra empresa vende productos electrónicos en línea. Cada transacción se indexa como un documento JSON en un índice llamado ventas-2024. Cada documento tiene una estructura similar a la del bloque de código a continuación. Nuestro primer objetivo analítico es simple: obtener el total de ventas (suma del campo monto) para el último trimestre.

El proceso comienza con una consulta de rango de fechas para aislar los documentos del último trimestre. Luego, aplicamos una agregación de tipo sum sobre el campo monto. Elasticsearch ejecuta esta operación en dos fases principales: la query phase (fase de consulta) que encuentra los documentos relevantes, y la aggregation phase (fase de agregación) que recorre esos documentos, extrae los valores del campo monto y los acumula en un total. El resultado se devuelve en la sección aggregations de la respuesta, separado de los documentos mismos.

Pero el análisis de ventas rara vez es unidimensional. Un ejecutivo querrá desglosar ese total por categoría de producto. Esto implica una agregación de cubos de tipo terms sobre el campo categoria.keyword. Dentro de cada cubo (por ejemplo, "Smartphones", "Laptops"), calcularemos la suma de ventas. Esto ya nos da un ranking de categorías por desempeño. Podemos llevar esto más lejos añadiendo una sub-agregación para calcular el número promedio de unidades vendidas por transacción (cantidad) dentro de cada categoría, combinando así métricas de valor y volumen.

Configuración del Índice y Datos de Ejemplo

Antes de ejecutar agregaciones, necesitamos un índice con mapeos apropiados. Para campos de texto que usaremos para agrupar (como categoria o region), es esencial tener sub-campos .keyword para agregaciones de términos eficientes. Los campos numéricos (monto, cantidad) y de fecha (fecha_venta) deben estar mapeados correctamente.


// PUT /ventas-2024
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "id_transaccion": { "type": "keyword" },
      "fecha_venta": { "type": "date" },
      "producto": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "categoria": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "region": {
        "type": "text",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "cliente_id": { "type": "keyword" },
      "cantidad": { "type": "integer" },
      "monto": { "type": "float" },
      "metodo_pago": { "type": "keyword" }
    }
  }
}

Ahora, indexamos algunos documentos de ejemplo para tener datos con los que trabajar. Estos documentos representan transacciones reales.


// POST /ventas-2024/_bulk
{"index":{}}
{"id_transaccion": "TXN001", "fecha_venta": "2024-03-15T10:30:00", "producto": "Smartphone Alpha X", "categoria": "Smartphones", "region": "Norte", "cliente_id": "CUST101", "cantidad": 1, "monto": 899.99, "metodo_pago": "tarjeta"}
{"index":{}}
{"id_transaccion": "TXN002", "fecha_venta": "2024-03-20T14:22:00", "producto": "Laptop Pro 16", "categoria": "Laptops", "region": "Sur", "cliente_id": "CUST102", "cantidad": 1, "monto": 1499.99, "metodo_pago": "transferencia"}
{"index":{}}
{"id_transaccion": "TXN003", "fecha_venta": "2024-04-05T09:15:00", "producto": "Smartphone Alpha X", "categoria": "Smartphones", "region": "Norte", "cliente_id": "CUST103", "cantidad": 2, "monto": 1799.98, "metodo_pago": "tarjeta"}
{"index":{}}
{"id_transaccion": "TXN004", "fecha_venta": "2024-04-10T16:45:00", "producto": "Tablet Lite", "categoria": "Tablets", "region": "Este", "cliente_id": "CUST101", "cantidad": 1, "monto": 329.50, "metodo_pago": "paypal"}
{"index":{}}
{"id_transaccion": "TXN005", "fecha_venta": "2024-04-18T11:30:00", "producto": "Laptop Pro 16", "categoria": "Laptops", "region": "Oeste", "cliente_id": "CUST104", "cantidad": 1, "monto": 1499.99, "metodo_pago": "tarjeta"}
{"index":{}}
{"id_transaccion": "TXN006", "fecha_venta": "2024-05-02T13:10:00", "producto": "Auriculares Premium", "categoria": "Accesorios", "region": "Sur", "cliente_id": "CUST102", "cantidad": 3, "monto": 450.00, "metodo_pago": "tarjeta"}
{"index":{}}
{"id_transaccion": "TXN007", "fecha_venta": "2024-05-22T10:00:00", "producto": "Smartphone Alpha X", "categoria": "Smartphones", "region": "Este", "cliente_id": "CUST105", "cantidad": 1, "monto": 899.99, "metodo_pago": "transferencia"}

Código en Acción: Construyendo el Análisis por Capas

Con los datos indexados, comenzamos nuestro análisis. Primero, una consulta base para obtener ventas del segundo trimestre (Abril-Junio, simplificado con nuestros datos de Abril y Mayo). Luego, aplicamos nuestra primera agregación compleja: un desglose por categoría, mostrando para cada una el total de ventas, el promedio del monto por transacción y el producto más vendido dentro de esa categoría.


// GET /ventas-2024/_search
{
  "size": 0, // No necesitamos devolver hits, solo agregaciones
  "query": {
    "range": {
      "fecha_venta": {
        "gte": "2024-04-01",
        "lt": "2024-07-01"
      }
    }
  },
  "aggs": {
    "ventas_por_categoria": {
      "terms": {
        "field": "categoria.keyword",
        "size": 10
      },
      "aggs": {
        "total_ventas": {
          "sum": { "field": "monto" }
        },
        "ticket_promedio": {
          "avg": { "field": "monto" }
        },
        "producto_top_por_ventas": {
          "terms": {
            "field": "producto.keyword",
            "size": 1,
            "order": { "_count": "desc" }
          },
          "aggs": {
            "ventas_totales_producto": {
              "sum": { "field": "monto" }
            }
          }
        }
      }
    },
    "ventas_totales_trimestre": {
      "sum_bucket": {
        "buckets_path": "ventas_por_categoria>total_ventas"
      }
    }
  }
}

Analicemos esta poderosa consulta. La agregación raíz ventas_por_categoria crea cubos para cada valor único en categoria.keyword. Dentro de cada cubo, calculamos tres sub-agregaciones: 1) total_ventas: la suma del monto, 2) ticket_promedio: el promedio del monto, y 3) producto_top_por_ventas: otra agregación de términos que, dentro del cubo de la categoría, encuentra el producto más frecuente (ordenado por _count) y además calcula sus ventas totales. Finalmente, fuera de los cubos, usamos una agregación de pipeline (sum_bucket) para sumar todos los valores de total_ventas de cada categoría, dándonos el total global del trimestre directamente en la respuesta.

La respuesta de Elasticsearch nos dará un JSON estructurado donde podremos ver claramente, por ejemplo, que la categoría "Smartphones" tiene un total_ventas de X, un ticket_promedio de Y, y que el producto_top_por_ventas es "Smartphone Alpha X" con sus propias ventas_totales_producto. Esta es la esencia del análisis multidimensional.

Errores Comunes y Cómo Evitarlos

Al trabajar con agregaciones avanzadas, es fácil caer en ciertas trampas que degradan el rendimiento o producen resultados incorrectos.

1. Agrupar por campos de texto sin subcampo .keyword: Si usas "field": "categoria" (sin .keyword) en una agregación de términos, Elasticsearch intentará agrupar por cada término tokenizado del análisis de texto (por ejemplo, "smart" y "phones" por separado). El resultado será un desastre. Solución: Siempre usa el subcampo .keyword para campos que quieras agrupar por su valor exacto.

2. Ignorar el parámetro "size" en agregaciones de términos: Por defecto, Elasticsearch devuelve solo los 10 cubos principales. Si tienes 50 regiones y quieres verlas todas, debes establecer "size": 50. Para obtener todos los cubos (con precaución, puede ser costoso), usa "size": 0 dentro del objeto de términos.

3. Malinterpretar el orden de las agregaciones de pipeline: Las agregaciones como sum_bucket, derivative o moving_avg operan sobre los resultados de otras agregaciones. Un error común es colocarlas en el mismo nivel que la agregación de la que dependen. Solución: Deben definirse después y referenciar la ruta de los cubos (buckets_path) correctamente, usando la sintaxis "aggs_padre>sub_aggs".

4. No considerar el impacto en el rendimiento de agregaciones anidadas profundas: Una agregación de términos dentro de otra agregación de términos (por ejemplo, región -> ciudad -> tienda) multiplica el número de cubos en memoria. Para conjuntos de datos grandes, esto puede causar errores de memoria (CircuitBreakingException). Solución: Utiliza el parámetro "shard_size" para controlar la precisión a nivel de fragmento, considera pre-calcular datos con rollups de Elasticsearch, y siempre prueba con un subconjunto de datos primero.

5. Olvidar que las agregaciones se ejecutan en el contexto de la consulta: Si tu consulta (query) filtra documentos por "region: Norte", todas tus agregaciones solo verán los documentos del Norte. Si quieres un total global y un desglose filtrado en la misma solicitud, necesitas usar agregaciones globales (global bucket) para escapar del contexto de la consulta.

Checklist de Dominio

Al completar esta lección, debes ser capaz de verificar las siguientes competencias:

  • Puedo diseñar y ejecutar una agregación anidada de tres niveles (ej: región -> categoría -> métricas de ventas).
  • Sé cuándo y cómo utilizar agregaciones de pipeline (como sum_bucket o bucket_sort) para realizar cálculos sobre los resultados de otros cubos.
  • Comprendo la diferencia crítica entre agrupar por un campo de texto y por su subcampo .keyword, y aplico siempre la opción correcta.
  • Puedo construir una consulta que combine filtros de fecha (range) con agregaciones para analizar tendencias temporales (por día, mes, trimestre usando date_histogram).
  • Soy capaz de extraer e interpretar los resultados de una respuesta de agregación compleja desde la API de Elasticsearch.
  • Puedo identificar y solucionar problemas comunes de rendimiento en agregaciones, como el ajuste de los parámetros size y shard_size.
  • Sé cómo usar una agregación significant_terms para encontrar correlaciones interesantes en los datos de ventas (ej: productos que se compran juntos).
  • Puedo llevar los resultados de mis agregaciones a Kibana y visualizarlos en un dashboard (usando un Data Table, un Pie Chart o un Bar Chart).
De lección a portfolio

Convertí esta lección en evidencia para Data Analyst.

Sumá un mini caso con datos, una conclusión de negocio y una captura del resultado. Eso pesa más que decir que viste la herramienta.

Paso 1

Publicá una consulta, dashboard o notebook con una conclusión clara.

Paso 2

Agregá contexto: problema, dato usado, decisión recomendada y limitación.

Paso 3

Guardá el enlace en tu CV, LinkedIn o portfolio antes de postular.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión