ADDCOLUMNS https://interactivechaos.ovh/es es Completado de una tabla sustituyendo valores inexistentes por ceros https://interactivechaos.ovh/es/dax/scenario/completado-de-una-tabla-sustituyendo-valores-inexistentes-por-ceros <span class="field field--name-title field--type-string field--label-hidden">Completado de una tabla sustituyendo valores inexistentes por ceros</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é, 30/10/2019 - 10:06</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Un caso con el que nos encontramos con cierta frecuencia es aquel en el que tenemos datos tabulares pero no disponemos de valores para todas las combinaciones de filas y columnas, y querríamos tener al menos ceros que nos permitan operar con la tabla completa.</p> <p>Para ver cómo podemos resolver este problema vamos a partir de la siguiente tabla de datos:</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-0031.jpg"><img alt="Tabla de datos" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-9016ada0-c23b-4f3a-9f90-baed27bfbede" data-insert-attach="{&quot;id&quot;: &quot;9016ada0-c23b-4f3a-9f90-baed27bfbede&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-0031.jpg" /></a> <p>Como vemos, se trata de una tabla en la que se muestran las ventas de tres posibles productos a lo largo de los cuatro trimestres del año, tabla en la que muchas de las combinaciones de trimestres y productos no existen.</p> <p>Si, tras leer esta tabla con Power BI, llevamos los datos a una matriz, el resultado es el siguiente:</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-0032.jpg"><img alt="Matriz de datos en Power BI" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-da07c2cd-781c-4498-99c5-ae1268dfedc7" data-insert-attach="{&quot;id&quot;: &quot;da07c2cd-781c-4498-99c5-ae1268dfedc7&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-0032.jpg" /></a> <p>...con blanks en todas aquellas intersecciones de filas y columnas para las que no hay datos en nuestra tabla original.</p> <p>Pues bien, para crear una tabla a partir de la leída que contenga valores en todas las combinaciones de filas y columnas vamos a comenzar identificando los posibles valores en filas y en columnas -lo que podemos conseguir usando la función <a href="https://www.interactivechaos.com/dax/function/values">VALUES</a>-, y calculando el producto cartesiano de ambos conjuntos con <a href="https://www.interactivechaos.com/dax/function/crossjoin">CROSSJOIN</a>:</p> <div class="codigo"> <p>Tabla = <br />     CROSSJOIN(<br />         VALUES(Data[Trimestre]);<br />         VALUES(Data[Producto])<br />     )</p> </div> <p>El resultado se muestra en la siguiente imagen:</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-0033.jpg"><img alt="Producto cartesiano de los elementos de filas y columnas" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-5bd7eda1-4b9a-4b3d-a94c-2aac29e3ba9d" data-insert-attach="{&quot;id&quot;: &quot;5bd7eda1-4b9a-4b3d-a94c-2aac29e3ba9d&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-0033.jpg" /></a> <p>A esta tabla le faltaría la columna de "Ventas" para incluir todos los datos que necesitamos. La añadimos con la función <a href="https://www.interactivechaos.com/dax/function/addcolumns">ADDCOLUMNS</a>:</p> <div class="codigo"> <p>Tabla = <br />     ADDCOLUMNS(<br />         CROSSJOIN(<br />             VALUES(Data[Trimestre]);<br />             VALUES(Data[Producto])<br />         );<br />         "Ventas"; 0<br />     )</p> </div> <p>Ahora ya tenemos la tabla con valores nulos:</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-0034.jpg"><img alt="Producto cartesiano de los elementos de filas y columnas con ventas" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-d7ba40cb-dc01-44b3-b5fe-c30e201170cf" data-insert-attach="{&quot;id&quot;: &quot;d7ba40cb-dc01-44b3-b5fe-c30e201170cf&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-0034.jpg" /></a> <p>Si llevamos esta tabla a una matriz confirmamos que el aspecto es el deseado:</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-0035.jpg"><img alt="Matriz con los datos de la nueva tabla" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-7fc1cafc-e2c1-450c-8262-aabd0fc9bc07" data-insert-attach="{&quot;id&quot;: &quot;7fc1cafc-e2c1-450c-8262-aabd0fc9bc07&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-0035.jpg" /></a> <p>Ahora necesitamos "sumar" los valores de ambas tablas, la original y la que acabamos de crear. Para ello vamos a recurrir a unirlas (verticalmente) con la función <a href="https://www.interactivechaos.com/dax/function/union">UNION</a>:</p> <div class="codigo"> <p>Tabla = <br />     VAR <br />         tabla1 = ADDCOLUMNS(<br />                 CROSSJOIN(<br />                     VALUES(Data[Trimestre]);<br />                     VALUES(Data[Producto])<br />                 );<br />                 "Ventas"; 0<br />             )<br />     VAR<br />         tabla2 = Data<br />     RETURN<br />         UNION(<br />             tabla1;<br />             tabla2<br />         )</p> </div> <p>El resultado es el siguiente:</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-0036.jpg"><img alt="Unión de las dos tablas" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-3b4d3fbb-bba0-4819-b6f9-e5dd0ffb343f" data-insert-attach="{&quot;id&quot;: &quot;3b4d3fbb-bba0-4819-b6f9-e5dd0ffb343f&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-0036.jpg" /></a> <p>Como nuestro objetivo es tener una tabla con una única entrada para cada combinación de filas y columnas, la agregamos según estos criterios. El problema con el que nos encontramos aquí es que la función <a href="https://www.interactivechaos.com/dax/function/summarize">SUMMARIZE</a>, por ejemplo, exige una tabla de base, no una tabla calculada. De forma que vamos a crear una nueva tabla calculada a partir de la anterior:</p> <div class="codigo"> <p>Tabla completa = <br />     SUMMARIZE(<br />         Tabla;<br />         Tabla[Trimestre];<br />         Tabla[Producto];<br />         "Ventas"; SUM(Tabla[Ventas])<br />     )</p> </div> <p>El resultado es el esperado:</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-0037.jpg"><img alt="Tabla agregada" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-6dd2737f-9cb0-49d2-8d60-d0246e5d4689" data-insert-attach="{&quot;id&quot;: &quot;6dd2737f-9cb0-49d2-8d60-d0246e5d4689&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-0037.jpg" /></a> <p>Ahora ya podemos llevar esta tabla a una matriz y comprobar que incluye todos los valores:</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-0038.jpg"><img alt="Matriz con los datos agregados" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-d6e78bb0-18ba-410b-81e2-b14e9c8533d9" data-insert-attach="{&quot;id&quot;: &quot;d6e78bb0-18ba-410b-81e2-b14e9c8533d9&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-0038.jpg" /></a></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/80" hreflang="es">CROSSJOIN</a></div> <div class="field__item"><a href="/es/taxonomy/term/30" hreflang="es">VALUES</a></div> <div class="field__item"><a href="/es/taxonomy/term/84" hreflang="es">ADDCOLUMNS</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/75" hreflang="es">UNION</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> Wed, 30 Oct 2019 09:06:37 +0000 admin 1662 at https://interactivechaos.ovh Replicación de cada fila de una tabla un número aleatorio de veces https://interactivechaos.ovh/es/dax/scenario/replicacion-de-cada-fila-de-una-tabla-un-numero-aleatorio-de-veces <span class="field field--name-title field--type-string field--label-hidden">Replicación de cada fila de una tabla un número aleatorio de veces</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 - 10:56</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 una tabla ("<em>Products</em>") de productos identificados por números enteros consecutivos:</p> <img alt="Tabla con los registros a replicar un número aleatorio de veces" data-entity-type="file" data-entity-uuid="4c23a5e6-f061-473b-9691-a6dc77468d59" src="/sites/default/files/inline-images/escenario-dax-0099.png" class="align-center" width="161" height="114" loading="lazy" /><p>El objetivo es generar otra tabla en la que cada uno de estos registros se haya replicado un número aleatorio de veces. Como puede apreciarse, este escenario es semejante al de <a href="/dax/scenario/replicacion-de-cada-fila-de-una-tabla-segun-una-tabla-de-valores">Replicación de cada fila de una tabla según una tabla de valores</a> con la salvedad de que ahora no tenemos una tabla que nos indique el número de veces que deberemos replicar cada fila. Pero la idea es semejante: en este caso generaremos la columna conteniendo esos números aleatorios manteniendo el esquema de trabajo que vimos en el escenario mencionado.</p> <p>Esta columna de números aleatorios la vamos a añadir a las columnas existentes de la tabla <em>Products</em> utilizando el siguiente código:</p> <div class="codigo"> <p>Products_Extended = <br />     ADDCOLUMNS(<br />         Products;<br />         "RandomValue"; RANDBETWEEN(2; 3)<br />     )</p> </div> <img alt="Tabla de productos conteniendo la columna adicional con los valores aleatorios" data-entity-type="file" data-entity-uuid="22e4e727-c1c6-4048-8d84-37f2b5971f24" src="/sites/default/files/inline-images/escenario-dax-0100.png" class="align-center" width="379" height="214" loading="lazy" /><p>Queremos ahora realizar una combinación cruzada entre la tabla anterior y una segunda tabla con tantas filas como indique el valor de <em>RandomValue</em> que vemos en la imagen anterior (el contenido exacto de dichas filas es irrelevante: lo importante es que contenga el número correcto de filas para que la combinación cruzada "replique" cada fila de la primera tabla el número de veces indicado por el valor generado aleatoriamente). La función a utilizar para realizar la combinación cruzada es <a href="/dax/function/generate">GENERATE</a>, y la función para generar la tabla con el número que nos interesa de filas es <a href="/dax/function/generateseries">GENERATESERIES</a>. El código completo es el siguiente:</p> <div class="codigo"> <p>Products_Extended = <br />     VAR<br />         Random = <br />         ADDCOLUMNS(<br />             Products;<br />             "RandomValue"; RANDBETWEEN(2; 3)<br />         )<br />     RETURN<br />         GENERATE(<br />             Random;<br />             GENERATESERIES(1; [RandomValue])<br />         )</p> </div> <p>Hemos generado la tabla <em>Random</em> conteniendo los mismos campos que la tabla <em>Products</em> añadiéndole la columna con los valores aleatorios, y se devuelve la combinación cruzada de dicha tabla con la progresión aritmética generada por GENERATESERIES, indicando que deberán generarse <em>[RandomValue]</em> valores (cifra accesible vía contexto de fila para cada fila de la tabla <em>Random</em> para la que la función GENERATE se plantea la combinación cruzada). El resultado es el siguiente:</p> <img alt="Tabla generada" data-entity-type="file" data-entity-uuid="d40d5b76-e361-4fb6-9847-764032fed403" src="/sites/default/files/inline-images/dax-0001.png" class="align-center" width="409" height="445" loading="lazy" /><p>Obsérvese que la máquina DAX no reconoce el campo RandomValue en el editor de fórmulas, pese a lo cual lo interpreta correctamente. Esto significa que hemos de escribir dicha etiqueta a mano en el código, sin esperar que el sistema "Intellisense" del editor nos lo muestre en la lista de opciones disponibles.</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/68" hreflang="es">VAR</a></div> <div class="field__item"><a href="/es/taxonomy/term/84" hreflang="es">ADDCOLUMNS</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/79" hreflang="es">GENERATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/81" hreflang="es">GENERATESERIES</a></div> </div> </div> Sun, 14 Jul 2019 08:56:30 +0000 admin 1164 at https://interactivechaos.ovh Creación de un calendario https://interactivechaos.ovh/es/dax/scenario/creacion-de-un-calendario <span class="field field--name-title field--type-string field--label-hidden">Creación de un calendario</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/07/2019 - 18:20</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Una de las preguntas más frecuentes relativas al calendario necesario para utilizar funciones de inteligencia de tiempo es ¿Es mejor crearlo externamente a Power BI? ¿O en el editor de consultas? ¿O tal vez con DAX?</p> <p>En este escenario vamos a crear un calendario completo en DAX, incluyendo el número y nombre del mes, trimestre, día de la semana, etc.</p> <p>Para comenzar, creamos la tabla con el calendario básico usando la función <a href="/es/dax/function/calendarauto">CALENDARAUTO</a>. Esta función crea una tabla con una única columna con una secuencia de fechas cubriendo todas las fechas que aparezcan en nuestro conjunto de datos, por defecto desde el 1 de enero del primer año hasta el 31 de diciembre del último año:</p> <div class="codigo"> <p>Calendar = CALENDARAUTO()</p> </div> <img alt="Calendario creado con CALENDARAUTO" data-entity-type="file" data-entity-uuid="41837857-0178-4e3c-acd4-d56266d5d974" src="/sites/default/files/inline-images/escenario-dax-0075.png" class="align-center" width="284" height="324" loading="lazy" /><p>Sin embargo, queremos añadir a esta tabla básica otros campos, para lo que vamos a usar las siguientes funciones DAX (obsérvese en la imagen anterior que el campo creado recibe el nombre de "<em>Date</em>"):</p> <ul><li>Para crear el año: <a href="/es/dax/function/year">YEAR</a></li> <li>Para crear el número de mes: <a href="/es/dax/function/month">MONTH</a></li> <li>Para crear el número de día: <a href="/es/dax/function/day">DAY</a></li> </ul><p>Estos nuevos campos los vamos a añadir a la tabla básica usando la función <a href="/es/dax/function/addcolumns">ADDCOLUMNS</a>:</p> <div class="codigo"> <p>Calendar = <br />     ADDCOLUMNS(<br />         CALENDARAUTO();<br />         "Year"; YEAR([Date]);<br />         "Month"; MONTH([Date]);<br />         "Day"; DAY([Date])<br />     )</p> </div> <img alt="Campos para el año, número de mes y día del mes" data-entity-type="file" data-entity-uuid="bf2996fb-ebfe-4052-b2eb-fef704e53234" src="/sites/default/files/inline-images/escenario-dax-0076.png" class="align-center" width="407" height="387" loading="lazy" /><p>Los nombres para el mes y el día de la semana podemos crearlos con la función <a href="/es/dax/function/format">FORMAT</a>, y el número de la semana y el número de día de la semana con las funciones <a href="/es/dax/function/weeknum">WEEKNUM</a> y <a href="/es/dax/function/weekday">WEEKDAY</a>, respectivamente:</p> <div class="codigo"> <p>Calendar = <br />     ADDCOLUMNS(<br />         CALENDARAUTO();<br />         "Year"; YEAR([Date]);<br />         "Month"; MONTH([Date]);<br />         "Month Name"; FORMAT([Date]; "MMMM");<br />         "Week #"; WEEKNUM([Date]);<br />         "Day"; DAY([Date]);<br />         "Week Day"; WEEKDAY([Date]);<br />         "Day Name"; FORMAT([Date]; "DDDD")<br />     )</p> </div> <img alt="Campos para el nombre del mes y del día, el número de la semana y el número del día de la semana" data-entity-type="file" data-entity-uuid="4b970cc7-125a-477c-bdb7-83ac11805295" src="/sites/default/files/inline-images/escenario-dax-0077.png" class="align-center" width="696" height="501" loading="lazy" /><p>Por último, para obtener el número de trimestre podemos recurrir a la fórmula:</p> <div class="codigo"> <p>CEILING(MONTH([Date])/3;1)</p> </div> <p>...que calcula el entero más próximo igual o superior al mes dividido entre 3 (con la función <a href="/es/dax/function/ceiling">CEILING</a>) .</p> <p>Si quisiéramos también tener el equivalente precedido por una "Q", podemos volver a recurrir a la función FORMAT y al operador &amp; que nos permite concatenar cadenas de texto:</p> <div class="codigo"> <p>Calendar = <br />     ADDCOLUMNS(<br />         CALENDARAUTO();<br />         "Year"; YEAR([Date]);<br />         "Quarter #"; CEILING(MONTH([Date])/3;1);<br />         "Quarter"; "Q" &amp; FORMAT(CEILING(MONTH([Date])/3;1); "#");<br />         "Month"; MONTH([Date]);<br />         "Month Name"; FORMAT([Date]; "MMMM");<br />         "Week #"; WEEKNUM([Date]);<br />         "Day"; DAY([Date]);<br />         "Week Day"; WEEKDAY([Date]);<br />         "Day Name"; FORMAT([Date]; "DDDD")<br />     )</p> </div> <img alt="Campos para el trimestre" data-entity-type="file" data-entity-uuid="1ce90b45-3e40-4719-89cf-33c23cef9583" src="/sites/default/files/inline-images/escenario-dax-0078.png" class="align-center" width="836" height="575" 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/90" hreflang="es">CALENDARAUTO</a></div> <div class="field__item"><a href="/es/taxonomy/term/40" hreflang="es">YEAR</a></div> <div class="field__item"><a href="/es/taxonomy/term/42" hreflang="es">MONTH</a></div> <div class="field__item"><a href="/es/taxonomy/term/43" hreflang="es">DAY</a></div> <div class="field__item"><a href="/es/taxonomy/term/84" hreflang="es">ADDCOLUMNS</a></div> <div class="field__item"><a href="/es/taxonomy/term/44" hreflang="es">FORMAT</a></div> <div class="field__item"><a href="/es/taxonomy/term/91" hreflang="es">WEEKNUM</a></div> <div class="field__item"><a href="/es/taxonomy/term/92" hreflang="es">WEEKDAY</a></div> <div class="field__item"><a href="/es/taxonomy/term/93" hreflang="es">CEILING</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">Baja</div> </div> Sun, 07 Jul 2019 16:20:39 +0000 admin 1156 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