En ocasiones es tan importante saber qué productos no se venden como aquellos que sí, y saber por qué. O saber a qué clientes o zonas no se les ha vendido durante un periodo. El problema es que si algo no se vende no genera registro y ¿cómo consultar datos que no existen?

Vamos a ver como podríamos resolver este caso con Power BI. De paso nos plantearemos la importancia de cuestionarse la validez de un modelo de datos ante determinadas cuestiones, como la que sigue.

Datos utilizados

Para el ejemplo vamos a partir de una tabla que contiene un diario de ventas de varios artículos en dos zonas (Hoja 1):

Tras importar los datos y relacionar con una tabla de fechas (puedes consultar este post que explica cómo crearlas rápidamente) el modelo queda como puede verse:

“Hoja 1” contiene un diario de ventas por producto y zona

En la siguiente imagen podemos ver cómo se distribuye la venta de los productos mensualmente. Esto nos permite apreciar que hay meses en los que determinados productos no se han vendido:

Productos A1 y A11 no se vendieron en marzo y abril del 19

El caso

Evidentemente, mostrar lo vendido en un periodo de tiempo es trivial. Para el periodo completo:

En otro periodo más reducido, los que no se hayan vendido no se ven:

Productos A3 y A12 no se vendieron en el periodo seleccionado.

Podríamos pensar que activar la opción “Show items with no data” para la fila “Producto” en el visual, solucionaría el problema. Pero observaremos que no cambia nada.

Asignar un valor con una fórmula DAX

El problema es que “no podemos” sacar ni filtrar valores que no existen. Así que vamos a crear una medida que asigne un valor a esos cruces de datos para los cuales no hay importes. Esta comprobará si el valor calculado es BLANK y, de ser así, le asignará un cero.

Importe Con Blanks = IF( ISBLANK( SUM(Hoja1[Importe]) ); 
0;
SUM(Hoja1[Importe])
)
Los valores blank se han reemplazado con ceros

Al agregar la nueva medida ya empezamos a ver diferencias.

Ahora, si lo que queremos es mostrar los no vendidos podremos aplicar un filtro sobre esa medida, aquellos que sean=0:

Pero no iba a ser tan fácil

Nuestro ejemplo permite analizar también ventas por zonas. Si vemos la medida “importe” por producto y zona:

Si hacemos lo mismo con la nueva medida creada, vemos que algo no está del todo bien… En la zona 2 sólo aparece con venta 0 un producto, cuando hay otros que tampoco se han vendido:

Si aplicamos el filtro vemos que ha ignorado artículos que no han vendido y deberían salir:

¿Por qué? Replanteamiento del modelo

La respuesta rápida y correcta es por que no tenemos un maestro de artículos.

Y tal y como hemos comenzado el artículo: ¿Cómo consultar un dato que no existe?

Estamos generando datos a partir de un diario de ventas. Lo primero que hemos de preguntarnos es si ese diario contiene ventas de todos los artículos en catálogo, independientemente de la zona.

Si la respuesta es Sí podríamos buscar una solución tipo crear un maestro a partir de este diario. También podríamos complicarnos creando fórmulas complejas con ALLSELECTED, etc… Pero mejor si conseguimos un maestro con los productos.

Vamos a incluirlo en el modelo y observaremos que sin añadir complejidad resolvemos mucho.

Añadimos el maestro de Productos a nuestro modelo y lo relacionamos

El contenido del maestro de Productos es el siguiente. Observamos que incluye más productos de los que aparecían en nuestro diario de ventas, lo cual es un caso de lo más habitual en la realidad, y nos confirma que los datos originales del diario no eran suficientes:

Con unos pequeños cambios en las tablas anteriores obtendremos el resultado esperado. Primero, los productos deben venir de la tabla Productos, no del diario.

Esto ya nos permite, por lo pronto, que la propiedad “Show items with no data” que hemos visto antes, funcione correctamente:

Y podríamos seguir utilizando la medida “Importe con blanks” aplicando el filtro de =0 de antes, o prescindir de la misma y utilizar la original, “Importe”, con el filtro “is blank”, que para el caso, es lo mismo que estamos evaluando en la fórmula DAX.

Pero no tengo tabla “maestra”

El caso me parece poco probable, pero puede darse. Además, este blog es para aprender ¿no?. Exploremos una nueva posibilidad.

En este caso podemos construir un maestro a partir de los movimientos del diario.

Crearemos una nueva tabla, que llamaré “ProductosSel”, y su contenido va a ser una fórmula DAX que va a “consultar” la columna productos del diario.

Nos interesan lo valores únicos, por eso el parámetro DISTINCT
ProductosSel = distinct(SELECTCOLUMNS(Hoja1;"SelProductos"; Hoja1[Producto])) 

La función SELECTCOLUMNS nos permite especificar de qué tabla (primer parámetro) que columna queremos extraer (tercer parámetro) y el 2º parámetro sirve para indicar el nombre del campo que queramos usar.

El valor devuelto es de tipo table y por defecto todas las filas de la tabla consultada. Por eso añadimos la función DISTINCT, que nos deja sólo valores únicos de esa columna.

Y esta tabla será nuestro maestro de artículos y sólo hemos de proceder como hemos hecho en el apartado anterior, como si dispusiésemos de un maestro real, relacionándolo:

Hemos construido una tabla “maestra” dinámicamente y la podemos relacionar en nuestro modelo

Conclusión

En ocasiones recibo consultas que con un simple rediseño se resolverían mucho más fácilmente. Como este caso que, con un pequeño (y justificado) cambio, hemos podido obtener la respuesta buscada si tener que recurrir a funcionalidades “personalizadas”.

Es vital cuestionarse los modelos de datos, replantearlos si es necesario… Un simple cambio de punto de vista puede darnos proporcionarnos soluciones mucho más sencillas de desarrollar, fáciles de mantener y respuestas más completas y eficientes.

  1. Genial aporte! me sirvió muchisimo para un trabajo que estoy realizando, ahora mi duda es la siguiente :¿ hay alguna forma de contabilizar las veces que el valor de productos vendidos es igual a 0? en mi caso particular estoy realizando vistas por semanas del mes y me interesa determinar la cantidad de días de la semana en que la venta de un producto es 0…alguna idea?

    • Hola Andrés, me alegra que haya sido de utilidad.
      Respecto a tu consulta, es compleja… Quizá lo abordaría del siguiente modo: A nivel básico (granularidad producto-dia) dax que use countx y filter para sacar recuento de productos que cumplan una condicion que actuaría de filtro:
      COUNTX(FILTER(Productos,(condicion)<>0), Producto[referencia]) .
      Como precaución, ten en cuenta que estás funciones countx countax, cuentan líneas que tengan valor, con lo que seguramente tendrás que sacar el total de productos y por diferencia con esta medida el número de los que no tienen venta. Espero haberte dado alguna idea. Saludos

  2. Hola Santiago:
    Tengo una duda, tengo que buscar un código, y que la tabla que arroje tenga una columna con el nombre de las empresas que no compraron dicho artículo y las que sí; y la otra columna con las cantidades(filtrado por vendedor).
    Con el ejemplo que explicas(es muy bueno), logro el resultado pero no me filtra por vendedor.
    Espero puedas ayudarme, muchas gracias.

    • Hola Pablo, si lo he entendido bien y es opción, mostraría los resultados que dices en dos tablas:
      – una que muestre los clientes que NO han comprado el producto, con una medida que un “ALL” de vendedores (si no deseas que el filtro de vendedores actue en esta medida-ignora la sugerencia si debe filtrar por vendedor también)
      – otra tabla que sacase los clientes que sí lo han comprado, que utilizaría una medida normal (sin particularidades)

  3. José Manuel Ortega

    Hola Santiago,

    Hay una opción que me gusta tambie´n para este ejemplo. Si quiero saber los Productos No Vendidos, puedo Crear una Tabla de la siguiente manera:

    Productos No vendidos = FILTER(DimProduct;ISEMPTY(RELATEDTABLE(FactSales)))

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *