VAR https://interactivechaos.ovh/es es Cálculo de totales acumulados https://interactivechaos.ovh/es/dax/scenario/calculo-de-totales-acumulados <span class="field field--name-title field--type-string field--label-hidden">Cálculo de totales acumulados</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, 03/12/2018 - 20:19</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Un escenario frecuente es aquel en el que queremos calcular el total acumulado, incluyendo todos los datos hasta una cierta fecha y no solo los implicados en el contexto de filtro actual. Por ejemplo, si suponemos la existencia de una tabla de ventas <span class="code">Sales</span> que contiene una columna <span class="code">Amount</span> con la cifra de ventas, podríamos definir una medida que calculase la suma de esta columna con la siguiente expresión DAX:</p> <div class="codigo">Sales = SUM(Sales[Amount])</div> <p>Lógicamente, esta medida se va a adaptar al contexto de filtro, por lo que si la llevamos a una matriz en la que se esté desagregando las ventas por año:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2023-01/dax_scenario_calculo_totales_01.png"><img alt="Sales by year" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-48c7d6c1-4870-4d74-b165-caba177a4d17" data-insert-attach="{&quot;id&quot;: &quot;48c7d6c1-4870-4d74-b165-caba177a4d17&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/2023-01/dax_scenario_calculo_totales_01.png" /></a> <p>...vemos que, para cada año, la cifra de ventas mostrada incluye solo las ventas de dicho año.</p> <p>Lo que querríamos sería que, en cada evaluación de la medida, el período temporal considerado comenzase el primer día para que el hubo ventas (supongamos que el 1 de enero de 2015) y se extendiese hasta la última fecha del "período actual". Por ejemplo, en la cifra correspondiente al año 2018 el período actual es el correspondiente a los 365 días de dicho año. Pues bien, para el cálculo del acumulado para dicho año querríamos que se considerase el período comprendido entre el 1 de enero de 2015 y el 31 de diciembre de 2018. Es decir, escrito en pseudo-código:</p> <div class="codigo">Accumulated sales = <br /> CALCULATE(<br />     [Sales],<br />     Período entre la primera fecha disponible y la última fecha del contexto actual<br /> )</div> <p>El período en cuestión puede calcularse usando la función <a href="/es/dax/function/datesbetween">DATESBETWEEN</a> que devuelve el conjunto de fechas entre dos dadas. Si el calendario está contenido en la tabla <span class="code">Calendar</span>, tendríamos, por lo tanto, que codificar la siguiente medida:</p> <div class="codigo"> <p>Accumulated sales = <br /> CALCULATE(<br />     [Sales],<br />     DATESBETWEEN(<br />         'Calendar'[Date],<br />         Primera fecha disponible,<br />         Última fecha del contexto actual<br />     )<br /> )</p> </div> <p>Ahora bien, es el cálculo de esas dos fechas lo que resulta más complejo, pues queremos que la primera fecha ("<em>Primera fecha disponible</em>") sea absoluta y haga referencia a la primera fecha disponible en el calendario, mientras que la segunda ("<em>Última fecha del contexto actual</em>") queremos que se adapte al contexto.</p> <p>Para la obtención de la primera fecha disponible en el calendario deberemos asegurarnos de que no se están aplicando filtros al mismo, para lo que podemos usar la función <a href="/es/dax/function/all">ALL</a> (lo que va a devolver una tabla). Y para la extracción de la primera fecha disponible de dicha tabla no podemos usar la función <a href="/es/dax/function/min">MIN</a> pues ésta exige una columna como argumento, de forma que tendremos que usar la función <a href="/es/dax/function/firstdate">FIRSTDATE</a>, que sí acepta una tabla como argumento y devuelve otra tabla (con la fecha más antigua disponible), tabla que podemos pasar a la función <a href="/es/dax/function/calculate">CALCULATE</a> como argumento de filtro:</p> <div class="codigo">FIRSTDATE(ALL(Sales[Order Date]))</div> <p>Para el cálculo de la última fecha del contexto actual no tenemos más que utilizar la función <a href="/es/dax/function/max">MAX</a> pasando como argumento la columna temporal que está desagregando nuestro objeto visual: la columna <span class="code">'Calendar'[Date]</span>. Es decir, la última fecha del contexto actual vendría dada por:</p> <div class="codigo">MAX('Calendar'[Date])</div> <p>Es decir, la expresión</p> <div class="codigo">DATESBETWEEN(<br />     'Calendar'[Date],<br />     FIRSTDATE(ALL(Sales[Order Date])),<br />     MAX('Calendar'[Date])<br /> )</div> <p>...va a devolver el conjunto de fechas del calendario entre la más antigua disponible y la última implicada en el contexto actual. Podemos escribir, por lo tanto, la medida que devuelve las ventas acumuladas:</p> <div class="codigo">Accumulated sales =<br /> CALCULATE(<br />     [Sales],<br />     DATESBETWEEN(<br />         'Calendar'[Date],<br />         FIRSTDATE(ALL(Sales[Order Date])),<br />         MAX('Calendar'[Date])<br />     )<br /> )</div> <p>Si quisiéramos utilizar variables para hacer más legible el código, podríamos crear dos variables, <span class="code">__firstdate</span> y <span class="code">__lastDate</span> conteniendo las dos fechas implicadas en el código anterior:</p> <div class="codigo">Accumulated sales =<br /> VAR __firstDate = FIRSTDATE(ALL(Sales[Order Date]))<br /> VAR __lastDate = MAX('Calendar'[Date])<br /> RETURN<br />     CALCULATE(<br />         [Sales],<br />         DATESBETWEEN(<br />             'Calendar'[Date],<br />             __firstDate,<br />             __lastDate<br />         )<br />     )</div> <p>Ahora, si llevamos la medida anterior a nuestra matriz, comprobamos que se están acumulando las ventas tal y como queríamos:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2023-01/dax_scenario_calculo_totales_02.png"><img alt="Accumulated sales by year" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-dab86cbb-c629-45d8-9afa-ee730c81aa40" data-insert-attach="{&quot;id&quot;: &quot;dab86cbb-c629-45d8-9afa-ee730c81aa40&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/2023-01/dax_scenario_calculo_totales_02.png" /></a> <p>Y, por supuesto, si mostramos la matriz desagregada por cualquier otro nivel de nuestra jerarquía temporal, por trimestres, por ejemplo, el cálculo de las ventas acumuladas sigue realizándose correctamente:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2023-01/dax_scenario_calculo_totales_03.png"><img alt="Accumulated sales by quarter" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-e40ca511-b94c-46b4-bc19-759574e3d53f" data-insert-attach="{&quot;id&quot;: &quot;e40ca511-b94c-46b4-bc19-759574e3d53f&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/2023-01/dax_scenario_calculo_totales_03.png" /></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/28" hreflang="es">SUM</a></div> <div class="field__item"><a href="/es/taxonomy/term/33" hreflang="es">CALCULATE</a></div> <div class="field__item"><a href="/es/taxonomy/term/38" hreflang="es">DATESBETWEEN</a></div> <div class="field__item"><a href="/es/taxonomy/term/37" hreflang="es">ALL</a></div> <div class="field__item"><a href="/es/taxonomy/term/77" hreflang="es">MIN</a></div> <div class="field__item"><a href="/es/taxonomy/term/59" hreflang="es">MAX</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/68" hreflang="es">VAR</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, 03 Dec 2018 19:19:03 +0000 admin 16 at https://interactivechaos.ovh Cálculo del número de valores previos a cada fecha https://interactivechaos.ovh/es/dax/scenario/calculo-del-numero-de-valores-previos-cada-fecha <span class="field field--name-title field--type-string field--label-hidden">Cálculo del número de valores previos a cada fecha</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é, 23/06/2021 - 15:10</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 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/2021-06/dax-0110.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-26f2094f-6fc5-4d8f-9cc2-da5576c21073" data-insert-attach="{&quot;id&quot;: &quot;26f2094f-6fc5-4d8f-9cc2-da5576c21073&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/2021-06/dax-0110.jpg" /></a> <p>Obsérvese que en la tabla las fechas se muestran ordenadas (de más antiguas a más modernas) para hacer más sencilla su interpretación, pero tengamos en mente que podría no ser así.</p> <p>El objetivo es crear una columna calculada que nos indique, para cada fila, cuántas filas de la misma tabla contienen fechas anteriores a la de la fila siendo considerada (teniendo en cuenta, como se ha comentado, que las filas podrían estar no ordenadas según la fecha).</p> <p>Como queremos añadir una columna calculada, se va a crear por defecto un contexto de fila para cada una de las filas evaluadas. Es decir, para la primera fila (la correspondiente al campo <em>Id</em> 1) se creará un contexto de fila en el que podremos acceder a los valores de sus campos, <em>Id</em> y <em>Date</em>. A partir del valor del campo <em>Date</em> querríamos filtrar la tabla de forma que solo se considerasen las filas con fechas anteriores y contar el número de filas resultantes.</p> <p>En pseudo código sería algo así:</p> <div class="codigo">Number of Previous values = <br /> COUNTROWS(<br />     FILTER(<br />         data,<br />         data[Date] &lt; fecha-de-la-fila-actual<br />     )<br /> )</div> <p>Se ha utilizado la etiqueta <em>fecha-de-la-fila-actual</em> para hacer referencia al valor del campo <em>Date</em> de la fila siendo considerada.</p> <p>La función <a href="/es/dax/function/filter">FILTER</a>, siendo un iterador, va a crear un nuevo contexto de fila en el que evaluar la condición para cada fila de la tabla <em>data</em>:</p> <div class="codigo">data[Date] &lt; fecha-de-la-fila-actual</div> <p>Lógicamente, el siguiente código no funcionaría:</p> <div class="codigo">Number of Previous values = <br /> COUNTROWS(<br />     FILTER(<br />         data,<br />         data[Date] &lt; data[Date])<br />     )<br /> )</div> <p>...pues dentro del contexto de fila creado por <a href="/es/dax/function/filter">FILTER</a>, el valor de <em>data[Date]</em> es el valor del campo <em>Date</em> de la fila que <a href="/es/dax/function/filter">FILTER</a> esté iterando. Lo que querríamos es que el valor de <em>fecha-de-la-fila-actual</em> fuese el definido en el contexto de fila "anterior", el creado por la columna calculada previo a la ejecución de la función <a href="/es/dax/function/filter">FILTER</a>.</p> <p>Para ello tenemos dos soluciones:</p> <p>La primera -la única existente hasta que se introdujeron las variables en DAX en 2015- es recurrir a la función <a href="/es/dax/function/earlier">EARLIER</a>. Esta función nos devuelve exactamente lo que queremos: el valor de un campo para un contexto de fila anterior (pudiendo indicarse cuántos contextos de fila hay que retroceder). Por ejemplo:</p> <div class="codigo">Number of Previous values = <br /> COUNTROWS(<br />     FILTER(<br />         data,<br />         data[Date] &lt; EARLIER(data[Date], 1)<br />     )<br /> )</div> <p>...devolvería el resultado que buscábamos:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0111.jpg"><img alt="Función EARLIER" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-5b76a210-02d1-4484-a8b9-c3a70422d70c" data-insert-attach="{&quot;id&quot;: &quot;5b76a210-02d1-4484-a8b9-c3a70422d70c&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/2021-06/dax-0111.jpg" /></a> <p>Vemos que, para la primera fila, la función devuelve un <em>Blank</em> pues no hay filas con fechas anteriores y la función <a href="/es/dax/function/countrows">COUNTROWS</a> devuelve este valor para tablas vacías. Podemos forzar que, en este caso, se devuelva un 0 usando la función <a href="/es/dax/function/coalesce">COALESCE</a>:</p> <div class="codigo">Number of Previous values = <br /> COALESCE(<br />     COUNTROWS(<br />         FILTER(<br />             data,<br />             data[Date] &lt; EARLIER(data[Date], 1)<br />         )<br />     ),<br />     0<br /> )</div> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0112.jpg"><img alt="Función EARLIER" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0" data-insert-attach="{&quot;id&quot;: &quot;08e7a98a-1bb0-4e89-bba5-5402a6b3a4c0&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/2021-06/dax-0112.jpg" /></a> <p>En este caso, como el contexto "anterior" es el único contexto previo, también podríamos usar la función <a href="/es/dax/function/earliest">EARLIEST</a> que nos devuelve el valor del campo indicado en el último contexto de fila existente:</p> <div class="codigo">Number of Previous values = <br /> COALESCE(<br />     COUNTROWS(<br />         FILTER(<br />             data,<br />             data[Date] &lt; EARLIEST(data[Date])<br />         )<br />     ),<br />     0<br /> )</div> <p>El segundo método es, precisamente, haciendo uso de variables, lo que resulta mucho más sencillo y fácilmente interpretable. Concretamente:</p> <div class="codigo">Number of Previous values = <br /> VAR __currentDate = data[Date]<br /> RETURN<br />     COUNTROWS(<br />         FILTER(<br />             data,<br />             data[Date] &lt; __currentDate<br />         )<br />     )</div> <p>En primer lugar creamos una variable (<em>__currentDate</em>) con la palabra reservada <a href="/es/dax/function/var">VAR</a>. Esta variable se va a crear en el contexto de evaluación existente que, en ese punto del código, es el contexto de fila creado por la columna calculada. Es decir, en este punto, el campo <em>data[Date]</em> toma el valor del campo <em>Date</em> en la fila que se esté evaluando.</p> <p>Una vez registrado el valor de la fecha se ejecuta el filtrado de la tabla <em>data</em>. Tal y como se ha comentado, la función <a href="/es/dax/function/filter">FILTER</a> crea un nuevo contexto de fila en el que se recorre la tabla en cuestión comparando cada valor del campo <em>Date</em> -evaluado en el contexto de fila de la función <a href="/es/dax/function/filter">FILTER</a>- con el valor registrado en la variable <em>__currentDate</em> (valor que se registró en el contexto de fila de la columna calculada):</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2021-06/dax-0113.jpg"><img alt="Función EARLIER" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-3cb7ced5-598b-408d-9d7d-d253ab959bba" data-insert-attach="{&quot;id&quot;: &quot;3cb7ced5-598b-408d-9d7d-d253ab959bba&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/2021-06/dax-0113.jpg" /></a> <p>Comprobamos que el resultado es el mismo que obtuvimos con la función <a href="/es/dax/function/earlier">EARLIER</a> pero más fácil de interpretar.</p> <p>Una vez más, podríamos usar la función <a href="/es/dax/function/coalesce">COALESCE</a> para forzar ceros allí donde <a href="/es/dax/function/countrows">COUNTROWS</a> devuelva <em>Blanks</em>:</p> <div class="codigo">Number of Previous values = <br /> VAR __currentDate = data[Date]<br /> RETURN<br />     COALESCE(<br />         COUNTROWS(<br />             FILTER(<br />                 data,<br />                 data[Date] &lt; __currentDate<br />             )<br />         ),<br />         0<br />     )</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/67" hreflang="es">COUNTROWS</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/89" hreflang="es">EARLIER</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/135" hreflang="es">COALESCE</a></div> <div class="field__item"><a href="/es/taxonomy/term/136" hreflang="es">EARLIEST</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, 23 Jun 2021 13:10:10 +0000 admin 3092 at https://interactivechaos.ovh Creación de un gráfico mostrando el signo de un valor https://interactivechaos.ovh/es/dax/scenario/creacion-de-un-grafico-mostrando-el-signo-de-un-valor <span class="field field--name-title field--type-string field--label-hidden">Creación de un gráfico mostrando el signo de un valor</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, 30/06/2020 - 13:41</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 métrica que queremos calcular para cada valor de una variable categórica. Por ejemplo, el "resultado" por país (con independencia de lo que represente este resultado) incluido en un libro Excel:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0084.jpg"><img alt="Dataset" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-04047a52-b525-444e-918f-fa45163c0cbb" data-insert-attach="{&quot;id&quot;: &quot;04047a52-b525-444e-918f-fa45163c0cbb&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/2020-06/dax-0084.jpg" /></a> <p>Nuestro objetivo es mostrar en Power BI un pequeño gráfico para cada uno de estos valores que muestre el signo del mismo, de forma que si el valor es positivo (o cero) se muestre un cierto gráfico y si es negativo, se muestre otro.</p> <p>Leemos los datos desde Power BI y creamos una tabla que nos servirá para mostrar el listado de países y sus respectivos resultados:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0085.jpg"><img alt="Matriz con la tabla leída" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-d08322e8-ce9e-43c3-9aa9-f2a9265c60af" data-insert-attach="{&quot;id&quot;: &quot;d08322e8-ce9e-43c3-9aa9-f2a9265c60af&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/2020-06/dax-0085.jpg" /></a> <p>Las gráficas a mostrar serán los <a href="https://www.vertex42.com/ExcelTips/unicode-symbols.html">símbolos unicode</a> 128200 y 128201:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0086.jpg"><img alt="Símbolos unicode" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-d93c8f76-b083-4d6f-b9c6-bcbc619540a8" data-insert-attach="{&quot;id&quot;: &quot;d93c8f76-b083-4d6f-b9c6-bcbc619540a8&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/2020-06/dax-0086.jpg" /></a> <p>Aun cuando los colores que muestran no están especialmente bien escogidos (el rojo cuando la gráfica sube y el azul cuando baja) servirán para nuestros propósitos.</p> <p>Para ver estos símbolos en pantalla podemos crear una sencilla medida DAX que devuelva el símbolo en cuestión:</p> <div class="codigo"> <p>up = UNICHAR(128200)</p> </div> <p>Y llevar esta medida a, por ejemplo, una tarjeta:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0087.jpg"><img alt="Símbolo unicode 128200" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-07805a95-30f5-454b-b582-d2f9322b1e50" data-insert-attach="{&quot;id&quot;: &quot;07805a95-30f5-454b-b582-d2f9322b1e50&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/2020-06/dax-0087.jpg" /></a> <p>En todo caso, lo que nos interesa es crear una medida que, a partir de un cierto valor -como el resultado de cada país-, devuelva un símbolo u otro en función del signo de dicho valor.</p> <p>Como -en este sencillo caso, solo tenemos dos opciones (que el valor sea mayor o igual a cero, o que sea menor que cero), bastaría usar la función <a href="https://www.interactivechaos.com/dax/function/if">IF</a> para evaluar el valor en cuestión y devolver el símbolo adecuado:</p> <div class="codigo"> <p>Trend = IF(<br />     SUM(data[Result]) &gt;= 0,<br />     UNICHAR(128200),<br />     UNICHAR(128201)<br />     )</p> </div> <p>Es decir, calculamos la suma del campo "<em>Result</em>" (hay que aplicar una función de agregación al campo necesariamente) y, si es mayor o igual a cero, devolvemos el símbolo unicode 128200. Y si no, el 128201. Si llevamos esta medida a nuestra tabla vemos el resultado:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0088.jpg"><img alt="Matriz con el gráfico" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-cdcdfc2b-374d-4e69-a8ee-3b29dda83494" data-insert-attach="{&quot;id&quot;: &quot;cdcdfc2b-374d-4e69-a8ee-3b29dda83494&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/2020-06/dax-0088.jpg" /></a> <p>En un escenario con más de dos opciones tendríamos que usar IFs anidados o, mejor, la función <a href="https://www.interactivechaos.com/dax/function/switch">SWITCH</a>, que también podemos usar aquí:</p> <div class="codigo"> <p>Trend = <br /> VAR results = SUM(data[Result])<br /> RETURN<br /> SWITCH(<br />     TRUE(),<br />     results &gt;= 0, UNICHAR(128200),<br />     results &lt; 0, UNICHAR(128201)<br />     )</p> </div> <p>En la expresión anterior se ha calculado previamente la suma del campo <em>Result</em> para no tener que ejecutarla posteriormente más de una vez, y devolvemos el resultado de la primera condición que se cumpla: si <em>results</em> (suma del campo <em>Result</em>) es mayor o igual a cero, devolvemos el primer símbolo unicode, y si es negativo el segundo. También podríamos devolver un valor por defecto en el caso de que ninguna de las condiciones se cumpliese. Gráficamente el resultado es, en todo caso, el mismo que teníamos antes:</p> <a class="colorbox insert-colorbox" data-colorbox-gallery="gallery-node" data-insert-class="" data-insert-type="image" href="/sites/default/files/2020-06/dax-0089.jpg"><img alt="Matriz con el gráfico" class="image-colorbox__max_800_px align-center" data-entity-type="file" data-entity-uuid="insert-max_800_px-0365df4b-0c0d-4db4-9924-1b98edaf0cc0" data-insert-attach="{&quot;id&quot;: &quot;0365df4b-0c0d-4db4-9924-1b98edaf0cc0&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/2020-06/dax-0089.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/127" hreflang="es">UNICHAR</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/28" hreflang="es">SUM</a></div> <div class="field__item"><a href="/es/taxonomy/term/65" hreflang="es">SWITCH</a></div> <div class="field__item"><a href="/es/taxonomy/term/68" hreflang="es">VAR</a></div> </div> </div> Tue, 30 Jun 2020 11:41:33 +0000 admin 2212 at https://interactivechaos.ovh 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 Obtención de los datos de ventas relativos a los 10 productos con mayores ventas https://interactivechaos.ovh/es/dax/scenario/obtencion-de-los-datos-de-ventas-relativos-los-10-productos-con-mayores-ventas <span class="field field--name-title field--type-string field--label-hidden">Obtención de los datos de ventas relativos a los 10 productos con mayores ventas</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, 28/07/2019 - 12:17</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 de ventas ("<em>Sales</em>") y otra con información sobre los productos ("<em>Products</em>"). En la primera tenemos un campo que indica el producto vendido y otro ("<em>Amount</em>") con la cifra de venta. En la tabla de productos tenemos un campo ("<em>Product name</em>") con el nombre del producto. Obviamente, ambas tablas están relacionadas a través de un campo que contiene el identificador de cada producto.</p> <p>El objetivo es la creación de una tabla que contenga toda la información de ventas para los 10 productos con mayores ventas (es decir, queremos un subconjunto de la tabla de ventas <em>Sales</em> que contenga información solo de los 10 productos más vendidos). Este escenario es semejante al de <a href="/dax/scenario/obtencion-de-los-10-clientes-que-mas-compras-acumulan">Obtención de los 10 clientes que más compras acumulan</a>, salvo por el hecho de que, aquí, no queremos un simple listado de productos con sus cifras de ventas, sino toda la información de ventas relativa a dichos productos.</p> <p>El plan es sencillo: Generamos una tabla de ventas totales por cada producto, seleccionamos los 10 con mayores cifras y utilizamos esta información para filtrar la tabla original de ventas de forma que solo incluya datos para dichos productos.</p> <p>Comencemos con la primera de las tablas: la agregación de ventas por producto. Para ello recurrimos a la función <a href="/dax/function/summarize">SUMMARIZE</a>, especificando como campo de agregación <em>Products[Product name]</em> y como columna a crear la resultante de aplicar la función <a href="/dax/function/sum">SUM</a> al campo <em>Sales[Amount]:</em></p> <div class="codigo"> <p>10 productos más vendidos = <br />     VAR<br />         VentasPorProducto =<br />             SUMMARIZE(<br />                 Sales;<br />                 Products[Product name];<br />                 "Sales"; SUM(Sales[Amount])<br />             )<br />     RETURN<br />         VentasPorProducto</p> </div> <img alt="Tabla agregada de total de ventas por producto" data-entity-type="file" data-entity-uuid="380d4e69-2687-4abc-a532-496e93fe4d2c" src="/sites/default/files/inline-images/dax-0002.png" class="align-center" width="402" height="438" loading="lazy" /><p>Como puede verse, se ha llevado la expresión a una variable para poder seguir completando la tabla calculada.</p> <p>Ahora queremos quedarnos solamente con los 10 productos con mayores ventas, para lo que recurrimos a la función <a href="/dax/function/topn">TOPN</a>. Como tabla de partida indicamos la anteriormente obtenida y como expresión, el campo <em>Sales</em> creado:</p> <div class="codigo"> <p>10 productos más vendidos = <br />     VAR<br />         VentasPorProducto =<br />             SUMMARIZE(<br />                 Sales;<br />                 Products[Product name];<br />                 "Sales"; SUM(Sales[Amount])<br />             )<br />     VAR<br />         ProductosMasVendidos =<br />             TOPN(<br />                 10;<br />                 VentasPorProducto;<br />                 [Sales]<br />             )<br />     RETURN<br />         ProductosMasVendidos</p> </div> <img alt="Tabla de los 10 productos más vendidos" data-entity-type="file" data-entity-uuid="03d644f3-2f19-4daa-b993-832d31ba74bf" src="/sites/default/files/inline-images/dax-0003.png" class="align-center" width="407" height="558" loading="lazy" /><p>Por último queremos utilizar esta tabla para filtrar la tabla de ventas, <em>Sales</em>. Para ello vamos a utilizar la función <a href="/dax/function/naturalinnerjoin">NATURALINNERJOIN</a> para crear una combinación natural entre la tabla de ventas y la tabla generada con los productos más vendidos. Al tratarse de una combinación natural interna, solo se mantendrán los registros que compartan los mismos valores en el campo o campos comunes. El campo común es <em>Product Name</em> (en el caso de la tabla <em>Sales</em>, el campo viene dado por su relación con la tabla <em>Products</em>). El código es el siguiente:</p> <div class="codigo"> <p>10 productos más vendidos = <br />     VAR<br />         VentasPorProducto =<br />             SUMMARIZE(<br />                 Sales;<br />                 Products[Product name];<br />                 "Sales"; SUM(Sales[Amount])<br />             )<br />     VAR<br />         ProductosMasVendidos =<br />             TOPN(<br />                 10;<br />                 VentasPorProducto;<br />                 [Sales]<br />             )<br />     RETURN<br />         NATURALINNERJOIN(<br />             Sales;<br />             ProductosMasVendidos<br />         )</p> </div> <p>...y el resultado es el que podemos ver en la siguiente imagen:</p> <img alt="Tabla resultante de la combinación natural interna" data-entity-type="file" data-entity-uuid="548c9793-7802-4047-a9bc-34fdf7c4bf6d" src="/sites/default/files/inline-images/dax-0004.png" class="align-center" width="708" height="555" loading="lazy" /><p>Vemos que se incluyen los campos de ambas tablas. Podríamos seleccionar solo los campos de interés con <a href="/dax/function/selectcolumns">SELECTCOLUMNS</a>, por ejemplo.</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/51" hreflang="es">SUMMARIZE</a></div> <div class="field__item"><a href="/es/taxonomy/term/28" hreflang="es">SUM</a></div> <div class="field__item"><a href="/es/taxonomy/term/55" hreflang="es">TOPN</a></div> <div class="field__item"><a href="/es/taxonomy/term/98" hreflang="es">NATURALINNERJOIN</a></div> </div> </div> Sun, 28 Jul 2019 10:17:30 +0000 admin 1202 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 Cálculo del año y mes fiscal correspondientes a cada fecha https://interactivechaos.ovh/es/dax/scenario/calculo-del-ano-y-mes-fiscal-correspondientes-cada-fecha <span class="field field--name-title field--type-string field--label-hidden">Cálculo del año y mes fiscal correspondientes a cada fecha</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/07/2019 - 13:01</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 calendario en la que se incluyen campos para el año y el número de mes:</p> <img alt="Tabla calendario" data-entity-type="file" data-entity-uuid="b61fe087-d3d8-4380-91bb-c1f556178ac0" src="/sites/default/files/inline-images/escenario-dax-0082.png" class="align-center" width="520" height="400" loading="lazy" /><p>El objetivo es añadir una columna con el año fiscal y otra con el mes fiscal. Para esto vamos a suponer que el mes en el que termina el año fiscal es marzo (tercer mes del año), lo que podremos modificar en cada bloque de código fácilmente.</p> <p>Para el cálculo del año fiscal vamos a comparar el mes incluido en cada fila con el mes en el que termina el año fiscal: si es superior, sumaremos 1 al valor del año:</p> <div class="codigo"> <p>FY = <br />     VAR<br />         UltimoMes = 3<br />     RETURN<br />         'Calendar'[Año] + <br />         IF(<br />             MONTH('Calendar'[Date]) &gt; UltimoMes;<br />             1;<br />             0<br />         )</p> </div> <img alt="Calendario con el campo correspondiente al año fiscal" data-entity-type="file" data-entity-uuid="91048b56-9864-408e-b66b-e30f09799deb" src="/sites/default/files/inline-images/escenario-dax-0083.png" class="align-center" width="433" height="434" loading="lazy" /><p>En el código anterior definimos la variable "UltimoMes" con el valor correspondiente al último mes del año fiscal (3) usando la función <a href="/dax/function/var">VAR</a>, y devolvemos el valor del campo <em>Año</em> sumándole 1 o no en función de que el mes de la fecha sea o no superior al último mes del año fiscal. Para esto extraemos el mes de la fecha con la función <a href="/dax/function/month">MONTH</a> y realizamos la comparación con <a href="/dax/function/if">IF</a>. En la imagen anterior se muestran fechas de marzo y abril para ver el salto de un período al otro.</p> <p>Un par de notas interesantes: Los nombres de las variables no pueden contener espacios, guiones, vocales acentuadas... de ahí el nombre de "UltimoMes". Y observa que es posible incrustar una función (IF, en este caso) donde se espera un valor númerico (siempre que la función devuelva un valor adecuado).</p> <p>Para añadir el mes fiscal, podemos restar al mes de la fecha (que extraeremos con la función <a href="/dax/function/month">MONTH</a>) el último mes del año fiscal. Esto devolverá un número positivo para los meses posteriores a éste (abril = 1, mayo = 2...), pero un número negativo o cero para los anteriores (enero = -2, febrero = -1 y marzo = 0). Para solucionar esto podemos simplemente calcular el módulo de la resta y 12 usando la función <a href="/dax/function/mod">MOD</a> y sumando 1 al resultado (el módulo devolverá cero para marzo, por ejemplo):</p> <div class="codigo"> <p>FM = <br />     VAR<br />         UltimoMes = 3<br />     RETURN<br />         MOD(MONTH('Calendar'[Date]) - UltimoMes; 12) + 1</p> </div> <img alt="Calendario con el campo correspondiente al mes fiscal" data-entity-type="file" data-entity-uuid="a3f6c097-0ac4-40e9-bade-3ad0519a03ea" src="/sites/default/files/inline-images/escenario-dax-0084.png" class="align-center" width="486" height="408" 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/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/42" hreflang="es">MONTH</a></div> <div class="field__item"><a href="/es/taxonomy/term/94" hreflang="es">MOD</a></div> </div> </div> Tue, 09 Jul 2019 11:01:19 +0000 admin 1158 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 Nombre del producto más vendido https://interactivechaos.ovh/es/dax/scenario/nombre-del-producto-mas-vendido <span class="field field--name-title field--type-string field--label-hidden">Nombre del producto más vendido</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, 15/01/2019 - 15:14</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Calcular el importe total de ventas para el producto más vendido es relativamente fácil. Si el campo que contiene los nombres de los productos es <em>'Product'[ProductName]</em> y la medida <em>[Total sales]</em> suma el campo correspondiente al importe de ventas, bastaría con crear la siguiente medida:</p> <div class="codigo"> <p>max_sales = MAXX(VALUES('Product'[ProductName]); [Total sales])</p> </div> <p>Básicamente estamos extrayendo los nombres de los productos con la función <a href="/dax/function/values">VALUES</a>, calculando las ventas totales para cada uno de ellos y quedándonos con el mayor valor utilizando la función <a href="/dax/function/maxx">MAXX</a>:</p> <img alt="Cifra de ventas totales correspondiente al producto más vendido" data-entity-type="file" data-entity-uuid="4d15db25-b820-4d55-a64a-11e6c7873911" src="/sites/default/files/inline-images/dax_scenario_nombre_producto_mas_vendido_01.JPG" class="align-center" width="465" height="207" loading="lazy" /><p>Sin embargo, extraer el nombre del producto correspondiente a dicha cifra no es tan sencillo. Una estrategia -una vez que sabemos cuál es su cifra de ventas- sería recorrer la lista de nombres de productos, calcular su cifra de ventas, compararla con el valor máximo ya conocido y devolver el nombre si la cifra calculada coincide. En pseudo-código, sería algo así:</p> <div class="codigo"> <p>max_sales = valor de ventas para el producto más vendido</p> <p>Para cada nombre de producto:</p> <p>    sales = valor de ventas</p> <p>    si sales = max_sales:</p> <p>        return nombre de producto</p> </div> <p>Una forma de devolver solo el nombre de producto de interés es recorrer el listado de nombres de producto, comparar su cifra de ventas con la cifra máxima ya calculada y devolviendo un valor no vacío si coinciden y un <a href="/dax/function/blank">BLANK()</a> si no coinciden, y usar a continuación la función iteractiva <a href="/dax/function/firstnonblank">FIRSTNONBLANK</a> que extrae el nombre del producto correspondiente al primer (y único en este ejemplo) valor no vacío. En pseudo-código sería:</p> <div class="codigo"> <p>max_sales = valor de ventas para el producto más vendido</p> <p>return</p> <p>    FIRSTNONBLANK(</p> <p>        Para cada nombre de producto</p> <p>        sales = valor de ventas</p> <p>        si sales = max_sales:</p> <p>            return cualquier valor no vacío</p> <p>        else:</p> <p>            return BLANK()</p> <p>    )</p> </div> <p>Es decir, podemos verlo como si estuviésemos generando una tabla temporal de una única columna en la que todos los valores serían blank salvo el correspondiente al producto para el que se cumpliese la condición sales = max_sales (es decir, al producto más vendido). El código anterior en DAX quedaría de la siguiente forma:</p> <div class="codigo"> <p>Max sales product =</p> <p>    VAR</p> <p>        max_sales = MAXX(VALUES('Product'[ProductName]); [Total sales])</p> <p>    RETURN</p> <p>        FIRSTNONBLANK(</p> <p>            'Product'[ProductName];</p> <p>            IF(</p> <p>                [Total sales] = max_sales ;</p> <p>                1;</p> <p>                BLANK()</p> <p>            )</p> <p>        )</p> </div> <p>Comenzamos calculando el valor máximo de ventas (<em>max_sales</em>) y devolviendo el nombre del producto correspondiente al primer (y único) valor no vacío de una imaginaria tabla intermedia, tabla que creamos a partir de la columna conteniendo los nombres de los productos en la que "sustituímos" el nombre del producto más vendido por un 1 (podría ser cualquier otro valor) y el resto de nombres de productos por un blank utilizando la función <a href="/dax/function/if">IF</a>:</p> <img alt="Nombre del producto con mayores ventas totales" data-entity-type="file" data-entity-uuid="d95f7abc-caec-4c13-a131-40b2d7adf918" src="/sites/default/files/inline-images/dax_scenario_nombre_producto_mas_vendido_02.JPG" class="align-center" width="526" height="396" loading="lazy" /><p>Para confirmar que el resultado es correcto podemos crear una visualización con la lista de productos y sus ventas totales, y ordenar dicha tabla según la cifra de ventas:</p> <img alt="Listado ordenado de productos por sus ventas totales" data-entity-type="file" data-entity-uuid="3ec18874-8211-46d1-a026-3ad2abd9e35a" src="/sites/default/files/inline-images/dax_scenario_nombre_producto_mas_vendido_03.JPG" class="align-center" width="492" height="505" loading="lazy" /><p>Como la "tabla temporal" solo contiene un único valor no vacío, también podríamos haber utilizado la función <a href="/dax/function/lastnonblank">LASTNONBLANK</a>:</p> <div class="codigo"> <p>Max sales product =</p> <p>    VAR</p> <p>        max_sales = MAXX(VALUES('Product'[ProductName]); [Total sales])</p> <p>    RETURN</p> <p>        LASTNONBLANK(</p> <p>            'Product'[ProductName];</p> <p>            IF(</p> <p>                [Total sales] = max_sales ;</p> <p>                1;</p> <p>                BLANK()</p> <p>            )</p> <p>)</p> </div> <img alt="Código alternativo usando la función LASTNONBLANK" data-entity-type="file" data-entity-uuid="91de6bd0-e7e0-403e-8b7b-6fe56db90e0d" src="/sites/default/files/inline-images/dax_scenario_nombre_producto_mas_vendido_04.JPG" class="align-center" width="542" height="402" 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/39" hreflang="es">MAXX</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/50" hreflang="es">LASTNONBLANK</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/31" hreflang="es">BLANK</a></div> <div class="field__item"><a href="/es/taxonomy/term/68" hreflang="es">VAR</a></div> </div> </div> Tue, 15 Jan 2019 14:14:28 +0000 admin 378 at https://interactivechaos.ovh Suma de los valores máximos de cada categoría https://interactivechaos.ovh/es/dax/scenario/suma-de-los-valores-maximos-de-cada-categoria <span class="field field--name-title field--type-string field--label-hidden">Suma de los valores máximos de cada categoría</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, 08/01/2019 - 19:19</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 de hechos ("Data") en la que cada registro tiene asignado una categoría:</p> <img alt="Datos de partida" data-entity-type="file" data-entity-uuid="96494bcb-fe75-421b-8f87-db56ee6f1d2b" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_01.JPG" class="align-center" width="159" height="220" loading="lazy" /><p>El objetivo es calcular el valor máximo de cada categoría (7 para la categoría A, 3 para B y 8 para C) y sumarlos (18 en este caso).</p> <p>La resolución de este escenario incluye dos pasos:</p> <p>1. En el primero deberemos calcular el valor máximo de cada categoría para llegar a una tabla intermedia semejante a la siguiente:</p> <img alt="Tabla intermedia con los valores máximos por categoría" data-entity-type="file" data-entity-uuid="df216c47-c8c0-41eb-ab0a-991917599a9d" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_02.JPG" class="align-center" width="197" height="93" loading="lazy" /><p>2. En el segundo deberemos recorrer la tabla intermedia generada sumando los valores encontrados.</p> <h3>1er paso</h3> <p>Para crear esta tabla intermedia podemos recurrir tanto a la función <a href="/dax/function/summarize">SUMMARIZE</a> como a <a href="/dax/function/summarizecolumns">SUMMARIZECOLUMNS</a> o <a href="/dax/function/groupby">GROUPBY</a>. Veamos los tres casos:</p> <h4>SUMMARIZE</h4> <p>El código sería el siguiente:</p> <div class="codigo"> <p>Max value per category summarize =</p> <p>    SUMMARIZE(</p> <p>        Data;</p> <p>        Data[Category];</p> <p>        "Max value"; MAX(Data[Value])</p> <p>    )</p> </div> <p>Básicamente estaríamos agregando el campo <em>Category</em> de la tabla <em>Data</em> para crear el campo <em>"Max value"</em> calculando el valor máximo del campo <em>Data[Value]</em> con la función <a href="/dax/function/max">MAX</a> una vez filtrado:</p> <img alt="Cálculo de la tabla intermedia con la función SUMMARIZE" data-entity-type="file" data-entity-uuid="3acd4fe9-c39b-4752-8526-ede313a7b2a0" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_03.JPG" class="align-center" width="380" height="215" loading="lazy" /><h4>SUMMARIZECOLUMNS</h4> <p>En este segundo caso el código sería el siguiente:</p> <div class="codigo"> <p>Max value per category summarizecolumns =</p> <p>    SUMMARIZECOLUMNS(</p> <p>    Data[Category];</p> <p>    "Max value";</p> <p>    MAX(Data[Value])</p> <p>)</p> </div> <img alt="Cálculo de la tabla intermedia con la función SUMMARIZECOLUMNS" data-entity-type="file" data-entity-uuid="7b547472-d0c6-41d8-8711-3a5192760aee" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_04.JPG" class="align-center" width="393" height="215" loading="lazy" /><p>Estamos agregando el campo <em>Data[Category]</em> y creando el nuevo campo <em>"Max value"</em> calculando el valor máximo del campo <em>Data[Value]</em> una vez filtrado, también con la función <a href="/dax/function/max">MAX</a>.</p> <h4>GROUPBY</h4> <p>Por último, utilizando la función <em>GROUPBY </em>el código quedaría de la siguiente forma:</p> <div class="codigo"> <p>Max value per category groupby =</p> <p>GROUPBY(</p> <p>    Data;</p> <p>    Data[Category];</p> <p>    "Max value"; MAXX(CURRENTGROUP(); Data[Value])</p> <p>    )</p> </div> <p>En este caso estamos agrupando la tabla <em>Data</em> según la columna <em>Data[Category]</em> y generando la columna "<em>Max value</em>" utilizando la función iterativa <a href="/dax/function/maxx">MAXX</a>. </p> <img alt="Cálculo de la tabla intermedia con la función GROUPBY" data-entity-type="file" data-entity-uuid="390dbbbf-8984-403f-8733-8d3ee69b5dc7" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_05.JPG" class="align-center" width="469" height="218" loading="lazy" /><h3>2º paso</h3> <p>Una vez conseguida la tabla anterior usando cualquiera de los tres métodos (supongamos que usando la función <em>GROUPBY</em>), tendremos que volver a agregar la tabla sumando los valores de la columna <em>"Max value"</em>. En este paso, al contrario que en el paso anterior, no todas las funciones mencionadas servirían. Veámoslas una a una:</p> <h4>SUMMARIZE</h4> <p>El código para esta primera función sería el siguiente:</p> <div class="codigo"> <p>Sum of max value per category = <br />     SUMMARIZE(<br />         'Max value per category groupby';<br />         "Max"; SUM('Max value per category groupby'[Max value])<br />     )</p> </div> <p>Es decir, estaríamos agregando la tabla intermedia creada, <em>Max value per category groupby</em>, sin especificar el campo de agregación (pues no queremos más que la suma) y el nuevo campo a crear, <em>Max</em>, como la suma de los valores. El resultado sería el mostrado en la siguiente imagen:</p> <img alt="Cálculo del resultado final con la función SUMMARIZE" data-entity-type="file" data-entity-uuid="79fc6517-49bf-471c-b440-a221ce20a369" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_06.JPG" class="align-center" width="535" height="158" loading="lazy" /><p>Al no haber especificado el campo de agregación, DAX agrega todos los valores numéricos.</p> <p>Obsérvese, en todo caso, que con esta función, SUMMARIZE, no sería posible crear la tabla intermedia y realizar la agregación en una única expresión DAX, pues la función SUMMARIZE devuelve un error ("se espera una tabla de base")</p> <h4>SUMMARIZECOLUMNS</h4> <p>Con esta función, el código sería el siguiente:</p> <div class="codigo"> <p>Sum of max value per category = <br />     SUMMARIZECOLUMNS(<br />         "Max"; SUM('Max value per category groupby'[Max value])<br />     )</p> </div> <p>Estamos agregando la tabla intermedia creada, nuevamente sin especificar el campo de agregación, y creando el campo <em>Max</em> como la suma del campo <em>Max value</em> de la tabla intermedia. El resultado es el siguiente:</p> <img alt="Cálculo del resultado final con la función SUMMARIZECOLUMNS" data-entity-type="file" data-entity-uuid="ed2d8e78-9985-4165-8118-f0824d06bd9b" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_07.JPG" class="align-center" width="536" height="163" loading="lazy" /><p>Al igual que ocurre con SUMMARIZE, el intento de aplicar la función SUMMARIZECOLUMNS a una tabla intermedia creada en la misma expresión DAX devolvería un error.</p> <h4>GROUPBY</h4> <p>En este último caso, el código sería el siguiente:</p> <div class="codigo"> <p>Sum of max value per category = <br />     VAR Max_value_per_category =<br />         GROUPBY(<br />             Data;<br />             Data[Category];<br />             "Max value"; MAXX(CURRENTGROUP(); Data[Value])<br />         )<br />     RETURN<br />         GROUPBY(<br />             Max_value_per_category;<br />             "Sum"; SUMX(CURRENTGROUP(); [Max value])<br />         )</p> </div> <p>Estaríamos simplemente agregando la tabla intermedia generando como resultado la suma de los valores <em>Max value</em> de forma iterativa. El resultado es el siguiente:</p> <img alt="Cálculo del resultado final con la función GROUPBY" data-entity-type="file" data-entity-uuid="d918aa2a-faf7-41b9-9eab-c48896db8378" src="/sites/default/files/inline-images/dax_scenario_suma_maximos_cat_08.JPG" class="align-center" width="501" height="280" loading="lazy" /><p>En este tercer caso, sí es posible crear la tabla intermedia en la misma expresión DAX, lo que hace de esta función la más adecuada para este escenario.</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/51" hreflang="es">SUMMARIZE</a></div> <div class="field__item"><a href="/es/taxonomy/term/59" hreflang="es">MAX</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/28" hreflang="es">SUM</a></div> <div class="field__item"><a href="/es/taxonomy/term/61" hreflang="es">SUMX</a></div> <div class="field__item"><a href="/es/taxonomy/term/58" hreflang="es">SUMMARIZECOLUMNS</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/68" hreflang="es">VAR</a></div> </div> </div> Tue, 08 Jan 2019 18:19:22 +0000 admin 356 at https://interactivechaos.ovh