SELECTCOLUMNS https://interactivechaos.ovh/es es Cálculo de la frecuencia de compra de los clientes https://interactivechaos.ovh/es/dax/scenario/calculo-de-la-frecuencia-de-compra-de-los-clientes <span class="field field--name-title field--type-string field--label-hidden">Cálculo de la frecuencia de compra de los clientes</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Lun, 14/10/2019 - 12:50</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>En este escenario partimos de un listado de clientes:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0024.jpg"><img alt="Tabla de clientes" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-350e337f-140f-4fa2-bda4-0f03477cb3c2" data-insert-attach="{&quot;id&quot;: &quot;350e337f-140f-4fa2-bda4-0f03477cb3c2&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0024.jpg" /></a> <p>...y de una tabla de compras de cada uno de ellos:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0025.jpg"><img alt="Tabla de ventas" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-03329b88-76e4-4c9b-8765-af78475b80e1" data-insert-attach="{&quot;id&quot;: &quot;03329b88-76e4-4c9b-8765-af78475b80e1&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0025.jpg" /></a> <p>El objetivo es obtener un listado de clientes en el que se muestre el número de días (por ejemplo, podría ser otro período) entre compras. Para hacer el escenario sencillo los clientes reciben como nombre "Cliente x", siendo x el número de compras realizadas (de esta forma, el <em>Cliente 0</em> no ha realizado ninguna, el <em>Cliente 1</em> ha realizado una, etc.), y la frecuencia de compra en días para cada cliente coincide también con x. Así, el cliente 2 realizó una primera compra el 3 de enero, y una segunda dos días más tarde, el 5 de enero.</p> <p>Una vez hemos asegurado que el modelo de datos incluye las relaciones adecuadas (incluyendo una relación entre la tabla de ventas y el necesario calendario), podemos crear una tabla calculada que, usando la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>, extraiga el listado de clientes y agregue la información necesaria. Partimos, por lo tanto, de la siguiente expresión DAX que define esta primera versión de nuestra tabla de resultados:</p> <div class="codigo"> <p>Frecuencia de compra = <br />     SELECTCOLUMNS(<br />         Clientes;<br />         "Nombre"; Clientes[Nombre]<br />     )</p> </div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0026.jpg"><img alt="Listado de clientes" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-e53eaa7f-c9ca-4db7-8f55-7fbf3b792f47" data-insert-attach="{&quot;id&quot;: &quot;e53eaa7f-c9ca-4db7-8f55-7fbf3b792f47&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0026.jpg" /></a> <p>Aunque podríamos calcular directamente la frecuencia de compra para cada uno de ellos, hagámoslo paso por paso, incluyendo una columna adicional para cada uno de los datos que necesitamos.</p> <p>Si entendemos por frecuencia de compra el número de días transcurridos entre la primera compra y la última dividido entre el número de compras realizadas, en primer lugar sería preciso calcular las fechas de primera y de última compra por cliente, para lo que vamos a recurrir a las funciones <a href="/dax/function/firstdate">FIRSTDATE</a> y <a href="/dax/function/lastdate">LASTDATE</a>:</p> <div class="codigo"> <p>Frecuencia de compra = <br />     SELECTCOLUMNS(<br />         Clientes;<br />         "Nombre"; Clientes[Nombre];<br />         "Primera compra"; FIRSTDATE(Calendario[Fecha]);<br />         "Última compra"; LASTDATE(Calendario[Fecha])<br />     )</p> </div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0027.jpg"><img alt="Listado de clientes con primera y última fecha de compra" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-5c3969df-7095-40d4-b46e-af97df4d2797" data-insert-attach="{&quot;id&quot;: &quot;5c3969df-7095-40d4-b46e-af97df4d2797&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0027.jpg" /></a> <p>El cálculo del rango de días entre ambas fechas se reduce a una sencilla resta, que realizaremos usando la función <a href="/dax/function/datediff">DATEDIFF</a>, función que nos permite especificar el período a considerar -días, en nuestro ejemplo-:</p> <div class="codigo"> <p>Frecuencia de compra = <br />     SELECTCOLUMNS(<br />         Clientes;<br />         "Nombre"; Clientes[Nombre];<br />         "Primera compra"; FIRSTDATE(Calendario[Fecha]);<br />         "Última compra"; LASTDATE(Calendario[Fecha]);<br />         "Rango"; DATEDIFF(FIRSTDATE(Calendario[Fecha]); LASTDATE(Calendario[Fecha]); DAY)<br />     )</p> </div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0028.jpg"><img alt="Listado de clientes con el rango de días de compras" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-0c5b04ec-58bd-46b2-a673-efde628eabbb" data-insert-attach="{&quot;id&quot;: &quot;0c5b04ec-58bd-46b2-a673-efde628eabbb&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0028.jpg" /></a> <p>Debemos entender que estamos realizando un enfoque un tanto ingenuo del problema al presuponer que el cliente existe como tal desde su primera compra y solo hasta la última. Más realista sería considerar como comienzo del período el día en el que el cliente "se dio de alta como tal", hubiese realizado o no una compra, y/o considerar como fin del período el día actual, por ejemplo.</p> <p>Considérese también que, al crearse la tabla calculada en un entorno de contexto de fila, para cada cliente se extrae la información de la tabla remota relacionada con él. Así, cuando se está calculando la primera fecha de compra para el cliente X, la expresión</p> <div class="codigo"> <p>FIRSTDATE(Calendario[Fecha])</p> </div> <p>...filtra el campo <em>Fecha</em> de la tabla <em>Calendario</em> de forma que solo incluya las fechas para las que existen compras asociadas al cliente que corresponda.</p> <p>El siguiente paso es contar el número de compras por cliente, para lo que podemos extraer la tabla <em>Ventas</em> (una vez filtrada por el contexto, para lo que usaremos la función <a href="/dax/function/relatedtable">RELATEDTABLE</a>) y contar el número de filas que tiene con <a href="/dax/function/countrows">COUNTROWS</a>:</p> <div class="codigo"> <p>Frecuencia de compra = <br />     SELECTCOLUMNS(<br />         Clientes;<br />         "Nombre"; Clientes[Nombre];<br />         "Primera compra"; FIRSTDATE(Calendario[Fecha]);<br />         "Última compra"; LASTDATE(Calendario[Fecha]);<br />         "Rango"; DATEDIFF(FIRSTDATE(Calendario[Fecha]); LASTDATE(Calendario[Fecha]); DAY);<br />         "Nº de compras"; COUNTROWS(RELATEDTABLE(Ventas))<br />     )</p> </div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0029.jpg"><img alt="Listado de clientes con el rango de compras y número de éstas" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-b68efa25-a5d8-4ce5-8aa3-96568aef9119" data-insert-attach="{&quot;id&quot;: &quot;b68efa25-a5d8-4ce5-8aa3-96568aef9119&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0029.jpg" /></a> <p>Por último queda lo más sencillo, que es dividir el rango de fechas entre el número de compras, para lo que recurriremos a la función <a href="/dax/function/divide">DIVIDE</a>:</p> <div class="codigo"> <p>Frecuencia de compra = <br />     SELECTCOLUMNS(<br />         Clientes;<br />         "Nombre"; Clientes[Nombre];<br />         "Primera compra"; FIRSTDATE(Calendario[Fecha]);<br />         "Última compra"; LASTDATE(Calendario[Fecha]);<br />         "Rango"; DATEDIFF(FIRSTDATE(Calendario[Fecha]); LASTDATE(Calendario[Fecha]); DAY);<br />         "Nº de compras"; COUNTROWS(RELATEDTABLE(Ventas));<br />         "Frecuencia de compra"; DIVIDE(<br />             DATEDIFF(FIRSTDATE(Calendario[Fecha]); LASTDATE(Calendario[Fecha]); DAY);<br />             COUNTROWS(RELATEDTABLE(Ventas))<br />         )<br />     )</p> </div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2019-10/dax-0030.jpg"><img alt="Listado de clientes con su frecuencia de compras" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-a1dd98da-72fb-45f2-a860-f2a054bc67d1" data-insert-attach="{&quot;id&quot;: &quot;a1dd98da-72fb-45f2-a860-f2a054bc67d1&quot;, &quot;attributes&quot;: {&quot;alt&quot;: [&quot;alt&quot;, &quot;description&quot;], &quot;title&quot;: [&quot;title&quot;]}}" data-insert-class="" data-insert-type="image" src="/sites/default/files/styles/max_800_px/public/2019-10/dax-0030.jpg" /></a> <p>Comprobamos que, en esta primera versión de la expresión, si un cliente no ha realizado ninguna compra se muestra un BLANK, y si ha realizado una única compra de devuelve una frecuencia de 1. Podríamos completar nuestro DIVIDE con alguna función lógica que realizase un cálculo u otro en función del número de compras y, de esta forma, gestionar estas excepciones.</p> <p>Puede descargarse el fichero Excel y el informe Power BI resultante <a href="https://www.interactivechaos.com/sites/default/files/data/calculo_de_la_frecuencia_de_compra_de_los_clientes.zip">aquí</a>.</p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/36" hreflang="es">FIRSTDATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/35" hreflang="es">LASTDATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/122" hreflang="es">DATEDIFF</a></div> <div class="field__item"><a href="/es/taxonomy/term/71" hreflang="es">RELATEDTABLE</a></div> <div class="field__item"><a href="/es/taxonomy/term/67" hreflang="es">COUNTROWS</a></div> <div class="field__item"><a href="/es/taxonomy/term/45" hreflang="es">DIVIDE</a></div> </div> </div> <div class="field field--name-field-dax-esc-dificultad field--type-list-string field--label-above"> <div class="field__label">Dificultad</div> <div class="field__item">Intermedia</div> </div> Mon, 14 Oct 2019 10:50:28 +0000 admin 1482 at https://interactivechaos.ovh Generación de una tabla con una columna de valores aleatorios https://interactivechaos.ovh/es/dax/scenario/generacion-de-una-tabla-con-una-columna-de-valores-aleatorios <span class="field field--name-title field--type-string field--label-hidden">Generación de una tabla con una columna de valores aleatorios</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Dom, 14/07/2019 - 12:09</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>En ocasiones queremos generar valores aleatorios para plantear diferentes escenarios de análisis. Generar un único valor es sencillo usando las funciones <a href="/dax/function/rand">RAND</a> o <a href="/dax/function/randbetween">RANDBETWEEN</a>, pero la generación de un listado de valores aleatorios es un poco más complicado. En este escenario queremos crear una tabla con una columna de valores enteros aleatorios.</p> <p>La generación de una tabla con una columna de n filas es sencillo si utilizamos la función <a href="/dax/function/generateseries">GENERATESERIES</a>. Por ejemplo, para generar 10 valores (consecutivos) podemos recurrir al siguiente código:</p> <div class="codigo"> <p>Tabla = GENERATESERIES(1; 10)</p> <img alt="Tabla con una columna conteniendo valores consecutivos" data-entity-type="file" data-entity-uuid="5291569c-1034-4483-b7cd-24d26d8e1128" src="/sites/default/files/inline-images/escenario-dax-0096.png" class="align-center" /></div> <p>Una vez generada la tabla con el número de filas que nos interesa, añadir columnas con contenido personalizado es muy sencillo con la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>. Podemos, por ejemplo, incluir una nueva columna en la que se generen los números aleatorios que estamos buscando, ignorando la columna <em>Value</em> que ya existe:</p> <div class="codigo"> <p>Tabla = <br />     SELECTCOLUMNS(<br />         GENERATESERIES(1; 10);<br />         "Value"; RANDBETWEEN(1; 5)<br />     )</p> </div> <img alt="Tabla conteniendo la columna de números aleatorios buscada" data-entity-type="file" data-entity-uuid="c3e38ccf-b683-4a5c-8dc8-654f0a7d0f51" src="/sites/default/files/inline-images/escenario-dax-0097.png" class="align-center" width="343" height="344" loading="lazy" /><p>Si quisiéramos generar valores reales, podemos usar la función RAND:</p> <div class="codigo"> <p>Tabla FLOAT = <br />     SELECTCOLUMNS(<br />         GENERATESERIES(1; 10);<br />         "Value"; RAND()<br />     )</p> </div> <img alt="Tabla conteniendo la columna de números aleatorios reales buscada" data-entity-type="file" data-entity-uuid="2f2a6ce5-7508-4d2e-a177-8e1bfbdc52e4" src="/sites/default/files/inline-images/escenario-dax-0098.png" class="align-center" width="320" height="343" loading="lazy" /><p> </p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/81" hreflang="es">GENERATESERIES</a></div> <div class="field__item"><a href="/es/taxonomy/term/27" hreflang="es">RANDBETWEEN</a></div> <div class="field__item"><a href="/es/taxonomy/term/96" hreflang="es">RAND</a></div> </div> </div> Sun, 14 Jul 2019 10:09:52 +0000 admin 1163 at https://interactivechaos.ovh Cálculo del número de días entre compras https://interactivechaos.ovh/es/dax/scenario/calculo-del-numero-de-dias-entre-compras <span class="field field--name-title field--type-string field--label-hidden">Cálculo del número de días entre compras</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mar, 02/07/2019 - 16:23</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Partimos de un listado de ventas (<span class="code">Sales</span>) en el que se indica -por ejemplo- el identificador de cliente y las fechas de las compras:</p> <img alt="Datos de partida" data-entity-type="file" data-entity-uuid="6e7ea336-e513-4349-a3ec-f7cc14d9cd1b" src="/sites/default/files/inline-images/escenario-dax-0042.png" class="align-center" width="238" height="198" loading="lazy" /><p>El objetivo es añadir a esta tabla una columna que indique el número de días transcurridos desde la compra anterior (anterior en el tiempo, pues nada nos asegura que el orden de los datos en la tabla original sea cronológico, y ya sabemos que Power BI tampoco asegura el orden de las filas).</p> <p>La resolución de este escenario no es sencilla. Aun cuando podría resolverse en una única expresión, se plantea a continuación su resolución paso a paso. Las fases involucradas son las siguientes:</p> <ol><li>Es necesario obtener, para cada fecha de compra del cliente X, la fecha de la anterior compra.</li> <li>La anterior fecha de compra es la mayor fecha de todas las compras del cliente X (exceptuando la compra que estemos analizando, por supuesto).</li> <li>Podemos obtener todas las compras anteriores del cliente X realizando una combinación cruzada de la tabla consigo misma tomando como campo común el correspondiente al identificador del cliente y considerando en la tabla de la derecha (véase la documentación de la función <a href="/es/dax/function/generate">GENERATE</a>) solo las fechas anteriores a la de la fecha de compra siendo considerada en la tabla izquierda. Así, por ejemplo, para la compra que aparece en la tercera línea de la imagen anterior (correspondiente al cliente 1, con fecha de 21 de enero de 2019), una combinación cruzada con la misma tabla en las condiciones mencionadas devolvería una fila para la combinación de la fila 3 y la fila 1 (mismo cliente y fecha anterior) y otra para la combinación de la fila 3 y la fila 2 (de nuevo, mismo cliente y fecha anterior).</li> </ol><p>Por último, una vez obtenida la tabla mencionada en el punto 1, bastaría calcular la diferencia entre cada fecha de compra y la fecha de la última compra.</p> <p>Vamos punto por punto.</p> <p>En primer lugar queremos obtener, para cada fila (es decir, para cada compra), el conjunto de compras anteriores del mismo cliente. Esto, como se ha comentado, puede lograrse con una combinación cruzada que nos permita especificar las condiciones de la combinación, es decir, con la función <a href="/es/dax/function/generate">GENERATE</a>. Como esta función requiere que las tablas involucradas en la combinación tengan campos con nombres diferentes, vamos modificar los nombres de los campos de la segunda copia con la función <a href="/es/dax/function/selectcolumns">SELECTCOLUMNS</a>, añadiendo un símbolo "_" al final de los nombres:</p> <div class="codigo"> <p>SelfJoin = <br />     GENERATE(<br />         Sales;<br />         FILTER(<br />             SELECTCOLUMNS(<br />                 Sales;<br />                 "Customer_"; Sales[Customer];<br />                 "Purchase Date_"; Sales[Purchase Date]<br />             );<br />             [Purchase Date_] &lt; Sales[Purchase Date] &amp;&amp; Sales[Customer] = [Customer_]<br />         )<br />     )</p> </div> <img alt="Self-join utilizando el contexto de fila creado por la función GENERATE" data-entity-type="file" data-entity-uuid="61138555-e1c0-45a5-9bbb-dcefc4489cf9" src="/sites/default/files/inline-images/escenario-dax-0044.png" class="align-center" width="670" height="514" loading="lazy" /><p>Tal y como se ve, estamos obteniendo la combinación cruzada imponiendo la condición de que el identificador de cliente sea el mismo, y que las fechas de la columna de la derecha sean inferiores a la fecha considerada en cada una de las filas de la izquierda (para simplificar, estamos suponiendo que un mismo cliente no va a realizar dos compras el mismo día).</p> <p>Obsérvese que no aparecen los registros de la primera tabla para los que no hay registros en la segunda que cumplan las condiciones impuestas (es decir, en la tabla que acabamos de crear, solo se incluyen fechas de compra para las que hay alguna compra anterior).</p> <p>Obtener, a partir de aquí, la fecha anterior a cada compra es fácil: basta con agrupar (con la función <a href="/es/dax/function/groupby">GROUPBY</a>) por cliente y fecha de compra (campos <em>Customer</em> y <em>Purchase Date</em>) y seleccionar el valor máximo (de cada grupo) del campo <em>Purchase Date_</em>:</p> <div class="codigo"> <p>LastPurchases = <br />     GROUPBY(<br />         SelfJoin;<br />         SelfJoin[Customer];<br />         SelfJoin[Purchase Date];<br />         "Last Purchase"; MAXX(<br />                             CURRENTGROUP();<br />                             SelfJoin[Purchase Date_]<br />                         )<br />     )</p> </div> <img alt="Tabla con las fechas de la última compra" data-entity-type="file" data-entity-uuid="c7517dfc-1136-438b-b5c4-736188acc9ea" src="/sites/default/files/inline-images/escenario-dax-0045.png" class="align-center" width="459" height="348" loading="lazy" /><p>Ahora viene un proceso un tanto complicado: como en la tabla solo se incluyen fechas de compra para las que existe una compra anterior, para que en el resultado final aparezcan todas las fechas originales tendríamos que realizar una combinación externa izquierda entre la tabla de fechas original y la nueva. Para esto tendríamos que usar la función <a href="/es/dax/function/naturalleftouterjoin">NATURALLEFTOUTERJOIN</a>, pero esta función exige que las tablas a combinar tengan la misma ascendencia (procedan de la misma fuente), lo que no se cumple en nuestro caso. Tal y como se explica en la <a href="/es/dax/function/naturalleftouterjoin">documentación de esta función</a>, es posible resolver este problema añadiendo una cadena de texto vacía al final de los nombres:</p> <div class="codigo"> <p>Purchase and Last Purchase = <br />     NATURALLEFTOUTERJOIN(<br />         SELECTCOLUMNS(<br />             Sales;<br />             "Customer"; Sales[Customer] &amp; "";<br />             "Purchase Date"; Sales[Purchase Date] &amp; ""<br />         );<br />         SELECTCOLUMNS(<br />             LastPurchases;<br />             "Customer"; LastPurchases[SelfJoin_Customer] &amp; "";<br />             "Purchase Date"; LastPurchases[SelfJoin_Purchase Date] &amp; "";<br />             "Last Purchase"; LastPurchases[Last Purchase]<br />         )<br />     )</p> </div> <img alt="Tabla inicial incluyendo la fecha de la última compra (si existe)" data-entity-type="file" data-entity-uuid="bd8c8912-3173-4504-886e-057331c10f2a" src="/sites/default/files/inline-images/escenario-dax-0046.png" class="align-center" width="590" height="461" loading="lazy" /><p>Por último, para calcular el número de días desde la última compra basta con añadir una nueva columna que devuelva la diferencia (solo si existe una última compra):</p> <div class="codigo"> <p>Days since last purchase = <br />     ADDCOLUMNS(<br />         'Purchase and Last Purchase';<br />         "# days";<br />             VAR<br />                 NumberOfDays =<br />                     'Purchase and Last Purchase'[Purchase Date] - <br />                     'Purchase and Last Purchase'[Last Purchase]<br />             RETURN<br />                 IF(<br />                     ISBLANK('Purchase and Last Purchase'[Last Purchase]);<br />                     BLANK();<br />                     NumberOfDays<br />                 )<br />     )</p> </div> <img alt="Tabla final mostrando el número de días desde la última compra" data-entity-type="file" data-entity-uuid="98f6ede6-aae6-4e3b-89fa-9826e897310c" src="/sites/default/files/inline-images/escenario-dax-0047.png" class="align-center" width="595" height="479" loading="lazy" /><p>Para devolver la diferencia de días comentada utilizamos la función <a href="/es/dax/function/if">IF</a> para confirmar si el valor del campo correspondiente a la última compra es un BLANK o no, para lo que utilizamos la función <a href="/es/dax/function/isblank">ISBLANK</a>.</p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/79" hreflang="es">GENERATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/48" hreflang="es">FILTER</a></div> <div class="field__item"><a href="/es/taxonomy/term/60" hreflang="es">GROUPBY</a></div> <div class="field__item"><a href="/es/taxonomy/term/39" hreflang="es">MAXX</a></div> <div class="field__item"><a href="/es/taxonomy/term/82" hreflang="es">CURRENTGROUP</a></div> <div class="field__item"><a href="/es/taxonomy/term/83" hreflang="es">NATURALLEFTOUTERJOIN</a></div> <div class="field__item"><a href="/es/taxonomy/term/68" hreflang="es">VAR</a></div> <div class="field__item"><a href="/es/taxonomy/term/29" hreflang="es">IF</a></div> <div class="field__item"><a href="/es/taxonomy/term/56" hreflang="es">ISBLANK</a></div> <div class="field__item"><a href="/es/taxonomy/term/31" hreflang="es">BLANK</a></div> <div class="field__item"><a href="/es/taxonomy/term/84" hreflang="es">ADDCOLUMNS</a></div> </div> </div> <div class="field field--name-field-dax-esc-dificultad field--type-list-string field--label-above"> <div class="field__label">Dificultad</div> <div class="field__item">Alta</div> </div> Tue, 02 Jul 2019 14:23:33 +0000 admin 1147 at https://interactivechaos.ovh Replicación de cada fila de una tabla según una tabla de valores https://interactivechaos.ovh/es/dax/scenario/replicacion-de-cada-fila-de-una-tabla-segun-una-tabla-de-valores <span class="field field--name-title field--type-string field--label-hidden">Replicación de cada fila de una tabla según una tabla de valores</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mar, 02/07/2019 - 11:25</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Otra aplicación interesante de la función <a href="/dax/function/generate">GENERATE</a> es la de replicar las filas de una tabla según ciertos valores. Por ejemplo, supongamos que tenemos una tabla de ventas ("<em>Ventas</em>") en las que se muestra el producto vendido y el número de unidades implicadas en cada venta:</p> <img alt="Tabla de ventas" data-entity-type="file" data-entity-uuid="e6a6260c-2e6b-40a6-b84b-3d6b073c2095" src="/sites/default/files/inline-images/escenario-dax-0039.png" class="align-center" width="271" height="128" loading="lazy" /><p>En múltiples circunstancias preferiríamos tener, no este tipo de listado, sino aquel en el que el item C, por ejemplo, aparezca tantas veces (en tantas filas) como unidades se han vendido (es decir, en el que aparezca tres veces en nuestro ejemplo).</p> <p>Este escenario podría ser resuelto si consiguiésemos realizar una combinación cruzada entre cada una de las filas de la tabla anterior y una tabla de soporte que tuviese tantas filas como se indicase en el campo <em>Units</em> de Ventas (con independencia del contenido exacto que tuviese esta tabla de soporte). Es posible crear una tabla con n filas mediante la función <a href="/dax/function/generateseries">GENERATESERIES</a>, función que crea una tabla con una única columna de nombre <em>Value</em> con los valores de una progresión aritmética. Es decir, si el número de unidades del producto C -por ejemplo- es 3, la combinación cruzada de dicha fila con el resultado de GENERATESERIES(1; 3) estaría replicando la fila del producto C tres veces.</p> <p>Afortunadamente ya sabemos que la función GENERATE genera un contexto de filtro para cada una de las filas de la primera tabla, lo que significa que los datos de dicha fila están a disposición de la segunda (y que, de hecho, esta segunda tabla se va a contextualizar según dicho contexto). En otras palabras, el campo <em>Units</em> está disponible para la segunda tabla de la función GENERATE.</p> <p>Todo esto hace muy fácil conseguir el resultado buscado, pues basta con ejecutar el siguiente código para generar la tabla que queremos:</p> <div class="codigo"> <p>Tabla = <br />     GENERATE(<br />         Ventas;<br />         GENERATESERIES(1; Ventas[Units])<br />     )</p> </div> <img alt="Tabla resultante" data-entity-type="file" data-entity-uuid="66ff029e-5fa5-4a16-80e5-48bcb9f6e07e" src="/sites/default/files/inline-images/escenario-dax-0040.png" class="align-center" width="381" height="344" loading="lazy" /><p>La función GENERATE va a recorrer cada fila de la tabla <em>Ventas</em>, creando el contexto de fila asociado a cada una y a realizar una combinación cruzada con todas las filas de la segunda tabla tras contextualizar esta última. La segunda tabla es el resultado de generar una progresión aritmética entre 1 y el valor del campo <em>Units</em> (disponible vía contexto de fila, de forma que basta con hacer referencia al nombre del campo para acceder a su contenido).</p> <p>Si quisiéramos quedarnos ahora con apenas los campos <em>Id</em> y <em>Product</em> (por ejemplo), bastaría utilizar la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>:</p> <div class="codigo"> <p>Tabla = <br />     SELECTCOLUMNS(<br />         GENERATE(<br />             Ventas;<br />             GENERATESERIES(1; Ventas[Units])<br />         );<br />         "Id"; Ventas[Id];<br />         "Product"; Ventas[Product]<br />     )</p> </div> <img alt="Tabla resultante con apenas los campos de interés" data-entity-type="file" data-entity-uuid="ad575bcb-ec72-480c-b71c-9b3c967533e9" src="/sites/default/files/inline-images/escenario-dax-0041.png" class="align-center" width="403" height="413" loading="lazy" /><p> </p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/79" hreflang="es">GENERATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/81" hreflang="es">GENERATESERIES</a></div> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> </div> </div> Tue, 02 Jul 2019 09:25:51 +0000 admin 1146 at https://interactivechaos.ovh Visualización de los días de ocupación de un recurso https://interactivechaos.ovh/es/dax/scenario/visualizacion-de-los-dias-de-ocupacion-de-un-recurso <span class="field field--name-title field--type-string field--label-hidden">Visualización de los días de ocupación de un recurso</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Lun, 01/07/2019 - 18:38</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Un interesante escenario es aquel que implica una tabla de datos que incluye la "ocupación" de unos ciertos recursos indicando la fecha de comienzo y la de finalización de la ocupación, teniendo como objetivo la visualización de una gráfica en la que se muestre el calendario y los días en los que cada recurso está ocupado o disponible.</p> <p>Para resolver este escenario partimos de una tabla ("<em>Allocations</em>") en la que se muestra el recurso en cuestión (1, 2 o 3 en nuestro ejemplo), y las fechas de inicio y fin de la ocupación:</p> <img alt="Datos de ocupación de los recursos" data-entity-type="file" data-entity-uuid="f6c39c65-644d-4cef-8dc5-6a96e5ea6721" src="/sites/default/files/inline-images/dax_function_0030.PNG" class="align-center" width="462" height="170" loading="lazy" /><p>Partimos también de un calendario ("<em>Calendar</em>") que, en este ejemplo, está limitado a la quincena dentro de la cual se producen las ocupaciones de cada recurso:</p> <img alt="Calendario" data-entity-type="file" data-entity-uuid="2baa69eb-5b8b-4255-a6f2-de8576f59025" src="/sites/default/files/inline-images/dax_function_0031.PNG" class="align-center" width="177" height="323" loading="lazy" /><p>Si generamos un gráfico a mano -usando Excel, por ejemplo-, la visualización de las ocupaciones de cada recurso tendría el siguiente aspecto:</p> <img alt="Gráfico de ocupación" data-entity-type="file" data-entity-uuid="e9a0d18c-4220-4395-972b-4e8c163c297f" src="/sites/default/files/inline-images/dax_function_0032.PNG" class="align-center" width="570" height="94" loading="lazy" /><p>El objetivo es llegar a una visualización semejante que se genere de forma automática.</p> <p>Obsérvese que si cruzamos la tabla "Allocations" (que incluye los períodos en los que cada recurso está reservado) y el calendario mediante la función <a href="/dax/function/generate">GENERATE</a>, por ejemplo (también valdría la función <a href="/dax/function/crossjoin">CROSSJOIN</a> pues, en este caso, no estamos haciendo uso del contexto de fila generado por las filas de la primera tabla), tendremos todas las posibles combinaciones entre las filas de ambas:</p> <div class="codigo"> <p>Table = <br />     GENERATE(<br />         Allocations;<br />         'Calendar'<br />     )</p> </div> <img alt="Cruce de la tabla de reserva de recursos y del calendario" data-entity-type="file" data-entity-uuid="8f7e8fc7-4298-448b-8f94-01932657e909" src="/sites/default/files/inline-images/escenario-dax-0034.png" class="align-center" width="487" height="537" loading="lazy" /><p>Y de esta tabla, es sencillo quedarnos solo con los días en los que el recurso está reservado si seleccionamos apenas aquellas filas en las que el campo <em>Date </em>sea mayor o igual a <em>Start </em>y menor o igual a <em>End</em>, para lo que recurrimos a la función <a href="/dax/function/filter">FILTER</a>:</p> <div class="codigo"> <p>Table = <br />     FILTER(<br />         GENERATE(<br />             Allocations;<br />             'Calendar'<br />         );<br />         'Calendar'[Date] &gt;= Allocations[Start] &amp;&amp; 'Calendar'[Date] &lt;= Allocations[End]<br />     )</p> </div> <img alt="Tabla de reserva de recursos por día" data-entity-type="file" data-entity-uuid="bcf230cb-01e3-4d6f-a5b1-ef311afd3147" src="/sites/default/files/inline-images/escenario-dax-0035.png" class="align-center" width="488" height="416" loading="lazy" /><p>En este punto podríamos obviar los campos <em>Start </em>y <em>End</em>, que ya no aportan nada, para lo que podemos recurrir a la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a> que nos permite escoger qué campos queremos seleccionar:</p> <div class="codigo"> <p>Table = <br />     SELECTCOLUMNS(<br />         FILTER(<br />             GENERATE(<br />                 Allocations;<br />                 'Calendar'<br />             );<br />             'Calendar'[Date] &gt;= Allocations[Start] &amp;&amp; 'Calendar'[Date] &lt;= Allocations[End]<br />         );<br />         "Id"; Allocations[Id];<br />         "Resource"; Allocations[Resource];<br />         "Date"; 'Calendar'[Date]<br />     )</p> </div> <img alt="Tabla de reserva de recursos por día" data-entity-type="file" data-entity-uuid="69d1ad45-c287-4783-8119-e0e293da2e21" src="/sites/default/files/inline-images/escenario-dax-0036.png" class="align-center" width="711" height="512" loading="lazy" /><p>Para visualizar el resultado vamos a recurrir a la visualización "<em>as Timeline</em>" descargable desde el marketplace. Una vez llevada al lienzo su configuración es la siguiente:</p> <img alt="Configuración de la visualización &quot;as Timeline&quot;" data-entity-type="file" data-entity-uuid="2edb3b99-714c-48b8-b589-d9be27b4a8c1" src="/sites/default/files/inline-images/escenario-dax-0037.png" class="align-center" width="178" height="411" loading="lazy" /><p>Y la visualización queda de la siguiente forma:</p> <img alt="Visualización mostrando las reservas por día" data-entity-type="file" data-entity-uuid="27c857a1-7798-4b5a-9041-8e28cd68b77e" src="/sites/default/files/inline-images/escenario-dax-0038.png" class="align-center" width="558" height="158" loading="lazy" /><p>Podemos comparar el resultado con el esquema realizado a mano:</p> <img alt="Gráfico de ocupación" data-entity-type="file" data-entity-uuid="e9a0d18c-4220-4395-972b-4e8c163c297f" src="/sites/default/files/inline-images/dax_function_0032.PNG" class="align-center" width="570" height="94" loading="lazy" /><p>También habríamos podido aprovechar el contexto de fila creado por la función GENERATE para incluir el filtro dentro de la propia función, de la siguiente manera:</p> <div class="codigo"> <p>Table = <br />     SELECTCOLUMNS(<br />         GENERATE(<br />             Allocations;<br />             FILTER(<br />                 'Calendar';<br />                 'Calendar'[Date] &gt;= Allocations[Start] &amp;&amp; 'Calendar'[Date] &lt;= Allocations[End]<br />             )<br />         );<br />         "Id"; Allocations[Id];<br />         "Resource"; Allocations[Resource];<br />         "Date"; 'Calendar'[Date]<br />     )</p> </div></div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/48" hreflang="es">FILTER</a></div> <div class="field__item"><a href="/es/taxonomy/term/79" hreflang="es">GENERATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/80" hreflang="es">CROSSJOIN</a></div> </div> </div> Mon, 01 Jul 2019 16:38:30 +0000 admin 1145 at https://interactivechaos.ovh Renombrado de los campos de una tabla https://interactivechaos.ovh/es/dax/scenario/renombrado-de-los-campos-de-una-tabla <span class="field field--name-title field--type-string field--label-hidden">Renombrado de los campos de una tabla</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Lun, 01/07/2019 - 18:12</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Hay múltiples ocasiones en las que resulta conveniente -o necesario- renombrar temporalmente los campos de una tabla antes de poder utilizarla en una expresión DAX. Esto puede lograrse fácilmente utilizando la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>. Veámos un sencillo ejemplo: Partimos de la siguiente tabla:</p> <img alt="Tabla de partida" data-entity-type="file" data-entity-uuid="176758ed-ea41-4f60-9964-e00bb2b9003e" src="/sites/default/files/inline-images/dax_function_0028.PNG" class="align-center" width="239" height="117" loading="lazy" /><p>Ahora deseamos crear una versión de esta tabla en la que los nombres de los campos aparezcan... en inglés, por ejemplo, con el fin de realizar una combinación con otra tabla o por el motivo que sea. En este ejemplo vamos a crear una nueva tabla, y lo hacemos con la función SELECTCOLUMNS, e incluyendo todos los campos con los nombres que nos convengan:</p> <div class="codigo"> <p>Tabla2 = <br />     SELECTCOLUMNS(<br />         Tabla;<br />         "Office"; Tabla[Oficina];<br />         "Year"; Tabla[Año];<br />         "Sales"; Tabla[Ventas]<br />     )</p> </div> <img alt="Tabla con los campos renombrados" data-entity-type="file" data-entity-uuid="cdc9b3a7-6c35-41a3-a4a3-07a729231621" src="/sites/default/files/inline-images/dax_function_0029.PNG" class="align-center" width="346" height="257" loading="lazy" /><p> </p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> </div> </div> Mon, 01 Jul 2019 16:12:31 +0000 admin 1144 at https://interactivechaos.ovh Agregación de dos tablas con la misma estructura https://interactivechaos.ovh/es/dax/scenario/agregacion-de-dos-tablas-con-la-misma-estructura <span class="field field--name-title field--type-string field--label-hidden">Agregación de dos tablas con la misma estructura</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mié, 10/04/2019 - 10:58</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Un escenario frecuente y sencillo de resolver es aquel en el que nos encontramos con dos tablas de idéntica estructura que deseamos agregar según algún campo. Veamos cómo sería. Suponemos que ya hemos obtenido los datos y tenemos cargadas en Power BI las dos tablas que, en este caso, contienen información de ventas:</p> <img alt="Tabla 1" data-entity-type="file" data-entity-uuid="46d7b1ed-8bab-4fc1-8789-cbe6657b8ca5" src="/sites/default/files/inline-images/escenario-dax-0010.PNG" class="align-center" width="690" height="158" loading="lazy" /><img alt="Tabla 2" data-entity-type="file" data-entity-uuid="5c8295a5-d833-4a35-bf57-35e69a3f3cff" src="/sites/default/files/inline-images/escenario-dax-0011.PNG" class="align-center" width="698" height="157" loading="lazy" /><p>En este escenario queremos obtener el total de ventas por vendedor. Vamos a hacerlo en dos fases:</p> <ol><li>Unión de las tablas anteriores</li> <li>Agregación de la tabla completa para mostrar las ventas (campo "<em>Precio final</em>") según el campo que identifica cada vendedor</li> </ol><p>Para el primer paso vamos a recurrir a la función <a href="/es/dax/function/union">UNION</a>. Sin embargo, no vamos a unir las tablas directamente pues, como vemos en las imágenes anteriores, a pesar de tener la misma estructura (y, en el editor de consultas, aparecer con las columnas en el mismo orden) cuando se importan a Power BI las columnas no se muestran en el mismo orden (probablemente por un <em>bug</em> en el software que existe desde, al menos, finales de 2017). Es por ello que vamos a aplicar la función UNION a las tablas especificando a mano las columnas que nos interesan y el orden que nos interesa, para lo que recurriremos a la función <a href="/es/dax/function/selectcolumns">SELECTCOLUMNS</a>:</p> <div class="codigo"> <p>Ventas totales = <br />     VAR<br />         Tabla1 = SELECTCOLUMNS(<br />             Ventas1;<br />             "Vendedor"; Ventas1[Id Vendedor];<br />             "PVP"; Ventas1[Precio final]<br />         )<br />     VAR<br />         Tabla2 = SELECTCOLUMNS(<br />             Ventas2;<br />             "Vendedor"; Ventas2[Id Vendedor];<br />             "PVP"; Ventas2[Precio final]<br />         )<br />     RETURN<br />         UNION(Tabla1; Tabla2)</p> </div> <img alt="Ventas totales" data-entity-type="file" data-entity-uuid="2e9813ee-d18b-47e7-bc9f-46a4586d92a6" src="/sites/default/files/inline-images/escenario-dax-0012.PNG" class="align-center" width="164" height="282" loading="lazy" /><p>Hemos seleccionado apenas las columnas "Vendedor" y "Precio final" (con el nombre de "PVP") pero podríamos haber seleccionado otras columnas también.</p> <p>Ahora, para el segundo paso, utilizamos la función <a href="/es/dax/function/summarize">SUMMARIZE</a> para agregar la tabla anterior:</p> <div class="codigo"> <p>Ventas totales por vendedor = <br />     SUMMARIZE(<br />         'Ventas totales';<br />         'Ventas totales'[Vendedor];<br />         "Ventas"; SUM('Ventas totales'[PVP]<br />         )<br />     )</p> </div> <img alt="Tabla de ventas agregada por vendedor" data-entity-type="file" data-entity-uuid="44dad59e-f058-470f-9c22-ceb2790c4512" src="/sites/default/files/inline-images/escenario-dax-0013.PNG" class="align-center" width="163" height="94" loading="lazy" /></div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/75" hreflang="es">UNION</a></div> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/51" hreflang="es">SUMMARIZE</a></div> <div class="field__item"><a href="/es/taxonomy/term/28" hreflang="es">SUM</a></div> </div> </div> <div class="field field--name-field-dax-esc-dificultad field--type-list-string field--label-above"> <div class="field__label">Dificultad</div> <div class="field__item">Intermedia</div> </div> Wed, 10 Apr 2019 08:58:12 +0000 admin 1046 at https://interactivechaos.ovh Recuento del número de valores distintos de una columna https://interactivechaos.ovh/es/dax/scenario/recuento-del-numero-de-valores-distintos-de-una-columna <span class="field field--name-title field--type-string field--label-hidden">Recuento del número de valores distintos de una columna</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mar, 09/04/2019 - 11:29</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>En este sencillo escenario partimos de dos tablas:</p> <ul><li><strong>Productos</strong>. Tabla de dimensiones conteniendo dos campos, con un identificador de cada producto y el nombre del producto. Vemos que hay 6 productos cuyos identificadores pertenecen al rango [1, 6]:</li> </ul><img alt="Tabla de productos" data-entity-type="file" data-entity-uuid="8fbe927b-2c77-4084-900c-787a5e576e3f" src="/sites/default/files/inline-images/escenario-dax-0006.PNG" class="align-center" width="239" height="157" loading="lazy" /><ul><li><strong>Ventas</strong>: Tabla de hechos con una única columna conteniendo los identificadores de los productos vendidos. Podemos apreciar la existencia de una fila con el identificador 7 que no existe en la tabla de dimensiones anterior, lo que provoca una violación de integridad referencial:</li> </ul><img alt="Tabla de ventas" data-entity-type="file" data-entity-uuid="c6ceeaa5-58a5-4c5a-82d1-e309c90894e3" src="/sites/default/files/inline-images/escenario-dax-0007.PNG" class="align-center" width="99" height="157" loading="lazy" /><p>Si creamos una tabla calculada con los identificadores de los productos vendidos y su nombre (extraído de la tabla producto), el resultado es el siguiente:</p> <div class="codigo"> <p>Productos vendidos = SELECTCOLUMNS(<br />     Ventas;<br />     "Id. Producto"; Ventas[Id Producto];<br />     "Nombre"; RELATED(Productos[Nombre de producto])<br /> )</p> </div> <img alt="Tabla de productos vendidos" data-entity-type="file" data-entity-uuid="41d6673d-adbb-4c58-a3d4-ea90b772e56a" src="/sites/default/files/inline-images/escenario-dax-0008.PNG" class="align-center" width="199" height="135" loading="lazy" /><p>Para acceder a la columna "<em>Nombre de producto</em>" de la tabla "<em>Productos</em>" utilizamos la función <a href="/dax/function/related">RELATED</a> que nos da acceso a una columna remota.</p> <p>Vemos que el producto con identificador 7 no tiene un nombre asociado. Si quisiéramos contar el número de productos vendidos distintos y para ello utilizásemos la segunda columna de la tabla anterior (la que ha recibido el nombre de "Nombre"), podríamos hacerlo con dos funciones distintas: <a href="/dax/function/distinctcount">DISTINCTCOUNT</a> y <a href="/dax/function/distinctcountnoblank">DISTINCTCOUNTNOBLANK</a>. Si definimos dos medidas con estas funciones, tenemos lo siguiente (damos a las medidas el mismo nombre de la función usada en cada caso):</p> <div class="codigo"> <p>DISTINCTCOUNTNOBLANK = DISTINCTCOUNTNOBLANK('Productos vendidos'[Nombre])</p> <p>DISTINCTCOUNT = DISTINCTCOUNT('Productos vendidos'[Nombre])</p> </div> <p>Si llevamos estas medidas a dos visualizaciones tipo tarjeta, tenemos:</p> <img alt="Medidas con el recuento de los valores distintos" data-entity-type="file" data-entity-uuid="5fe5c846-b47b-4403-bd32-16e027c8b8df" src="/sites/default/files/inline-images/escenario-dax-0009.PNG" class="align-center" width="534" height="123" loading="lazy" /><p>Comprobamos que la función DISTINCTCOUNT cuenta el número de valores distintos considerando los valores vacíos (BLANK) como un valor adicional, mientras que DISTINCTCOUNTNOBLANK excluye estos valores vacíos.</p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/57" hreflang="es">RELATED</a></div> <div class="field__item"><a href="/es/taxonomy/term/73" hreflang="es">DISTINCTCOUNT</a></div> <div class="field__item"><a href="/es/taxonomy/term/74" hreflang="es">DISTINCTCOUNTNOBLANK</a></div> </div> </div> Tue, 09 Apr 2019 09:29:53 +0000 admin 1044 at https://interactivechaos.ovh Obtención de la fecha de la primera y última venta de cada vendedor https://interactivechaos.ovh/es/dax/scenario/obtencion-de-la-fecha-de-la-primera-y-ultima-venta-de-cada-vendedor <span class="field field--name-title field--type-string field--label-hidden">Obtención de la fecha de la primera y última venta de cada vendedor</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Lun, 08/04/2019 - 11:50</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>En este sencillo escenario partimos de los siguientes datos:</p> <ul><li>Tabla de vendedores:</li> </ul><img alt="Tabla de vendedores" data-entity-type="file" data-entity-uuid="a31aa195-9f87-4a2e-b537-eb3fea9b2fad" src="/sites/default/files/inline-images/dax_function_0001.PNG" class="align-center" width="226" height="94" loading="lazy" /><ul><li>Tabla de ventas:</li> </ul><img alt="Tabla de ventas" data-entity-type="file" data-entity-uuid="76624da2-fe68-42d3-a4ee-7fd62a27825e" src="/sites/default/files/inline-images/dax_function_0002.PNG" class="align-center" width="606" height="156" loading="lazy" /><p>El objetivo es crear una tabla calculada en la que se incluya el nombre del vendedor y las fechas de su primera y última venta. Comencemos creando la tabla con los nombres, para lo que vamos a usar la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>:</p> <div class="codigo"> <p>Listado vendedores = SELECTCOLUMNS(<br />     Vendedores;<br />     "Nombre"; Vendedores[Nombre Vendedor]<br /> )</p> </div> <img alt="Listado de vendedores" data-entity-type="file" data-entity-uuid="bc6e090c-08b7-4c0f-b2f7-d1ed0731d599" src="/sites/default/files/inline-images/escenario-dax-0003.PNG" class="align-center" width="236" height="153" loading="lazy" /><p>Añadamos ahora la columna con la primera fecha de venta. Podríamos pensar en la necesidad de extraer mediante la función <a href="/dax/function/relatedtable">RELATEDTABLE</a> el subconjunto de la tabla de ventas asociada a cada fila de nuestra tabla (tal y como hacemos en el escenario de <a href="/dax/scenario/creacion-de-una-tabla-con-las-ventas-medias-por-vendedor">Creación de una tabla con las ventas medias por vendedor</a>) pero la función <a href="/dax/function/firstdate">FIRSTDATE</a>, tal y como podemos leer en la documentación, devuelve la primera fecha <strong>en el contexto actual</strong>, lo que supone el contexto de fila definido por cada vendedor, en nuestro caso, lo que hace innecesario el uso de la mencionada función RELATEDTABLE. Basta con usar FIRSTDATE de la siguiente forma:</p> <div class="codigo"> <p>Listado vendedores = SELECTCOLUMNS(<br />     Vendedores;<br />     "Nombre"; Vendedores[Nombre Vendedor];<br />     "Primera venta"; FIRSTDATE(Ventas[Fecha])<br /> )</p> </div> <img alt="Listado de vendedores y primera fecha de venta" data-entity-type="file" data-entity-uuid="87a43dbc-6379-4017-9d5b-b943975a3190" src="/sites/default/files/inline-images/escenario-dax-0004.PNG" class="align-center" width="387" height="157" loading="lazy" /><p>Vemos que las fechas son distintas, lo que ya nos indica que el cálculo de FIRSTDATE se realiza en contextos diferentes para cada fila. En todo caso comprobamos mediante una inspección ocular de la tabla de ventas que las fechas mostradas son las correctas.</p> <p>La adición de la última fecha de venta es análoga, utilizando la función <a href="/dax/function/lastdate">LASTDATE</a>:</p> <div class="codigo"> <p>Listado vendedores = SELECTCOLUMNS(<br />     Vendedores;<br />     "Nombre"; Vendedores[Nombre Vendedor];<br />     "Primera venta"; FIRSTDATE(Ventas[Fecha]);<br />     "Última venta"; LASTDATE(Ventas[Fecha])<br /> )</p> </div> <img alt="Listado de vendedores y primera y última fecha de venta" data-entity-type="file" data-entity-uuid="958fe5a6-6cdb-4cf8-bff3-cf406ae1651c" src="/sites/default/files/inline-images/escenario-dax-0005.PNG" class="align-center" width="330" height="141" loading="lazy" /><p> </p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/36" hreflang="es">FIRSTDATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/35" hreflang="es">LASTDATE</a></div> </div> </div> Mon, 08 Apr 2019 09:50:20 +0000 admin 1041 at https://interactivechaos.ovh Creación de una tabla con las ventas medias por vendedor https://interactivechaos.ovh/es/dax/scenario/creacion-de-una-tabla-con-las-ventas-medias-por-vendedor <span class="field field--name-title field--type-string field--label-hidden">Creación de una tabla con las ventas medias por vendedor</span> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/es/user/1" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Dom, 07/04/2019 - 14:50</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>En este escenario partimos de dos tablas:</p> <ul><li><em>DimEmployee</em>, conteniendo información sobre los empleados. Esta tabla contiene el campo <em>Name</em> con el nombre completo de cada vendedor, y el campo <em>SalesTerritoryKey</em>, con el territorio de ventas asignado a cada empleado. Hay 10 territorios de ventas (de 1 a 10). Los empleados que no trabajan en ventas reciben un <em>SalesTerritoryKey</em> de 11.</li> <li> <p><em>FactResellerSales</em>, conteniendo información sobre las ventas realizadas. Esta tabla contiene el campo <em>SalesAmount</em> con el precio de venta (de cada una de las ventas) y está enlazada con la anterior a través de un campo <em>EmployeeKey</em>.</p> </li> </ul><p>El objetivo es la creación de una tabla calculada en la que se muestren tres campos: el nombre de cada vendedor, su territorio de ventas y la cifra media de ventas de cada uno.</p> <p>Para la creación de la tabla vamos a utilizar la función <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a> a partir de la tabla de empleados, extrayendo el nombre de los empleados que nos interesan, su territorio, y añadiendo su cifra media de ventas. En pseudo-código sería:</p> <div class="respuesta"> <p>Average sales amount per seller = SELECTCOLUMNS(<br />   DimEmployee;<br />   "Name"; &lt;nombre de cada empleado de ventas&gt;;<br />   "Territory"; &lt;territorio de cada empleado&gt;;<br />   "Average sales"; Cálculo-de-ventas-medias<br /> )</p> </div> <p>Como primer intento, vamos a calculas las ventas medias usando la función <a href="/dax/function/average">AVERAGE</a> para calcular el valor medio de la columna <em>SalesAmount</em>. El código sería el siguiente:</p> <div class="codigo"> <p>Average sales amount per seller = <br />     SELECTCOLUMNS(<br />         DimEmployee;<br />         "Name"; DimEmployee[Name];<br />         "Territory"; DimEmployee[SalesTerritoryKey];<br />         "Average sales"; AVERAGE(FactResellerSales[SalesAmount])<br />     )</p> </div> <p>Y el resultado:</p> <img alt="Creación de una tabla con las ventas medias por vendedor" data-entity-type="file" data-entity-uuid="cac39ca2-cd31-4fbc-a121-a05534aee563" src="/sites/default/files/inline-images/escenario-dax-0000.PNG" class="align-center" width="304" height="290" loading="lazy" /><p>Vemos dos errores: se están mostrando empleados que no pertenecen a ventas (cuyo territorio es el 11) y todas las cifras medias de ventas son iguales, señal de que no estamos personalizando el cálculo para cada vendedor. Arreglemos en primer lugar el primer problema: debemos considerar solo los empleados cuyo territorio de ventas no sea 11. En el código usado vemos que estamos partiendo de la tabla <em>DimEmployee</em> para extraer el nombre, etc. Podemos partir, no de esta tabla completa, sino de la tabla filtrada usando la función <a href="/dax/function/filter">FILTER</a>, de forma que solo se incluyan los vendedores:</p> <div class="codigo"> <p>Average sales amount per seller = <br />     SELECTCOLUMNS(<br />         FILTER(<br />             DimEmployee;<br />             DimEmployee[SalesTerritoryKey] &lt;&gt; 11<br />         );<br />         "Name"; DimEmployee[Name];<br />         "Territory"; DimEmployee[SalesTerritoryKey];<br />         "Average sales"; AVERAGE(FactResellerSales[SalesAmount])<br />     )</p> </div> <p>El resultado en este caso es el siguiente:</p> <img alt="Creación de una tabla con las ventas medias por vendedor" data-entity-type="file" data-entity-uuid="18597f8a-968f-484c-9efe-2ad9ebca71b5" src="/sites/default/files/inline-images/escenario-dax-0001.PNG" class="align-center" width="311" height="323" loading="lazy" /><p>...lo que tiene mucho mejor aspecto. Al respecto del segundo problema -que las cifras devueltas por la función AVERAGE sean las mismas, debemos entender que esta función está considerando toda la tabla de ventas para hacer el cálculo. Tenemos que filtrar esta tabla para que solo considere las ventas de cada empleado. Para ello podemos usar la función <a href="/dax/function/relatedtable">RELATEDTABLE</a> que cambia el contexto en el que se va a realizar el cálculo filtrando la tabla de ventas de forma que solo se considere el subconjunto de esta tabla que esté relacionado con la fila (con el vendedor) siendo considerada. Es decir, para el primer vendedor que vemos en la imagen anterior, Michael Blythe, la función RELATEDTABLE filtraría la tabla de ventas extrayendo solo las ventas en las que Michael aparezca como vendedor asociado a la venta. Pero la función AVERAGE no admite una tabla como argumento... Afortunadamente podemos recurrir a la función <a href="/dax/function/averagex">AVERAGEX</a> que acepta una tabla como primer argumento y una expresión como segundo (que puede ser simplemente el campo <em>SalesAmount</em> que nos interesa):</p> <div class="codigo"> <p>Average sales amount per seller = <br />     SELECTCOLUMNS(<br />         FILTER(<br />             DimEmployee;<br />             DimEmployee[SalesTerritoryKey] &lt;&gt; 11<br />         );<br />         "Name"; DimEmployee[Name];<br />         "Territory"; DimEmployee[SalesTerritoryKey];<br />         "Average sales"; AVERAGEX(<br />             RELATEDTABLE(FactResellerSales);<br />             FactResellerSales[SalesAmount])<br />     )</p> </div> <p>Ahora el resultado es el correcto:</p> <img alt="Creación de una tabla con las ventas medias por vendedor" data-entity-type="file" data-entity-uuid="51dc6991-39ca-4703-a758-ab4a7b3641df" src="/sites/default/files/inline-images/escenario-dax-0002.PNG" class="align-center" width="316" height="324" loading="lazy" /><p> </p> </div> <div class="field field--name-field-funciones-dax-involucradas field--type-entity-reference field--label-above"> <div class="field__label">Funciones DAX involucradas</div> <div class="field__items"> <div class="field__item"><a href="/es/taxonomy/term/69" hreflang="es">SELECTCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/70" hreflang="es">AVERAGEX</a></div> <div class="field__item"><a href="/es/taxonomy/term/48" hreflang="es">FILTER</a></div> <div class="field__item"><a href="/es/taxonomy/term/71" hreflang="es">RELATEDTABLE</a></div> <div class="field__item"><a href="/es/taxonomy/term/72" hreflang="es">AVERAGE</a></div> </div> </div> Sun, 07 Apr 2019 12:50:59 +0000 admin 1040 at https://interactivechaos.ovh