Olá!

Continuando o post sobre as funções calculate e keepfilter, hoje gostaria de estender ainda mais o seu uso e mostrar outras situações que envolvem o uso de keepfilters

Então, sem mais enrolação, vamos ao post.

FILTROS AVANÇADOS – CALCULATE E KEEPFILTERS

Sabemos que para uma boa expressão contendo filtro, menos é mais, ou seja, quanto menor a quantidade de colunas, mais otimizado o código.

Suponha que para uma determinada medida, queiramos aplicar um filtro que calcule apenas os produtos que possuem seu preço unitário maior do que um determinado valor.

Por via de regra dentro de uma calculate não conseguimos criar uma expressão de colunas, ou seja, se eu quiser criar uma medida como esta, a função acusará erro. 

Isso acontece por se tratar de uma expressão de tabela e como não temos uma coluna ou tabela que armazene as linhas que atendam a este filtro, a calculate não consegue concluir a operação. Além do mais, ela não é booleana.  Uma saída para tal, seria criar uma coluna calculada.

Mas em outros exemplos utilizamos  múltiplas colunas como filtro. Sim, isso ocorreu, mas perceba a diferença sutil na construção da expressão.

Na expressão acima, utilizamos o And para filtro. Deste modo, não estamos criando nenhuma expressão tabular dentro da calculate, apenas adicionando mais filtros ao contexto.

A forma correta de chegar ao resultado do primeiro exemplo, é incluindo o FILTER e ALL. Como estas funções conseguem operar com predicados boolean e criar uma expressão de tabela, toda a operação pode ser criada aqui e processada na calculate para o resultado final.

Ainda sobre a medida acima, se porventura adicionar um slicer filtrando os valores ou não, ele não irá alterar o resultado desta medida. Isso por que ele atua somente em uma coluna, a coluna Net Price.

É importante deixar claro que a avaliação lógica está aplicada para a coluna Net Price. Isto não impede de aplicar uma avaliação lógica para a coluna Quantity.

Existe uma pequena ‘falha’, se assim podemos dizer, neste gráfico. Quando criamos um limite no slicer para o valor mínimo de 500 e manipulamos o filtro, a medida mrCalcSAmount mostra que não há vendas com o range entre 500 e 3000, porém, continua aparecendo na medida mrSlgreater1000.

Aqui podemos ver mais uma vez o poder da função ALL, veja que Audio, Games and Toys e Music não possuem vendas na medida mrCalcSAmount, o que é normal não exibir ali, mas sua exibição na outra coluna da matriz pode ser considerada “anormal”, se não soubéssemos da função ALL.

Existem duas formas de solucionar esta situação.

Aplicando estas duas novas medidas no gráfico e comparando com a primeira que criamos, note que o resultado é completamente diferente.

NOTA: ainda que a função filter seja funcional nesta situação, se a sua tabela fato for muito grande, poderá se deparar com problemas de performance. Opte sempre por keepfilters, a função filter agirá como uma iteradora, escaneando linha por linha.

Sabendo que o contexto de filtro é um dos mais importantes em DAX e que rege o PBI como um todo, decidi pesquisar um pouco mais e estudar a fundo essa função para entender melhor como ela funciona e seu real poder dentro das expressões em DAX. Então, este talvez seja um tópico um pouco mais avançado.

Ele irá tratar sobre o uso de KEEPFILTER com funções iteradoras como: sumx, averagex, filter e etc.

KEEPFILTER COM ITERADORES – TÓPICO AVANÇADO

Quando a calculate é utilizada dentro de um row context e aqui inclui as iteradoras, por natureza, há uma transição de contexto sobrepondo  o existente. Como não existe uma situação em que o filtro seja mantido somente com a calculate, a função keepfilters deve ser utilizada dentro da função iteradora para não sobrepor o row context.

Com as medidas criadas e utilizando um slicer para filtro, selecionei os meses:

  • Novembro e Dezembro (2007);
  • Janeiro e Fevereiro (2008).

Destaco que essa seleção foi feita fora do meu gráfico de matrix e por ser desta forma, é considerada uma arbitrary shape no filter context. Isso porque, ela não está filtrando todos os valores visíveis obtidos na interseção.

Por ser uma simples medida e que não altera o contexto, não há problema algum.

Se fôssemos considerar apenas a medida aditiva com sumx, a forma arbitrária criada pelo slicer não impactaria em nada no resultado. Mas quando aplicamos o values e criamos uma medida para média mensal, o resultado não é muito correto.

mrAvgBYMnth =
   AVERAGEX(
          VALUES( 'Date'[Month]),
                   [mrSumSalesAm])

Como utilizamos a função values e ela retorna apenas os valores visíveis, o correto seria selecionar os meses e anos do gráfico de matriz e não no slicer. Por isso ele é considerado uma forma arbitrária (arbitrary shape).

Perceba duas coisas neste gráfico:

  • Os valores mensais exibidos na medida mrAvgByMonth, estão errados; O resultado é o mesmo da medida de soma das vendas.
  • Embora o agrupado anual esteja correto, seu total não está. A média dos dois valores deveria ser: 768,499.

E afinal, o que aconteceu?! Podemos atribuir essa falha de resultado ao context transition que houve quando utilizamos avegarex + values, que implicitamente, acabou executando uma calculate. 

Como temos dois filtros MONTH & CALENDARYEAR, apenas o month é sobrescrito mantendo o segundo intacto. Como assim?!

Entenda que no slicer criado para o filter context temos:

  1. 2007 – Novembro e Dezembro
  2. 2007 – Janeiro e Fevereiro
  3. 2008 – Novembro e Dezembro
  4. 2008 – Janeiro e Fevereiro

Se alterarmos a forma de exibição da tabela, fica mais evidente. Veja na prática o que aconteceu com a explicação acima.

Perceba que o subtotal abaixo de cada coluna que representa o ano, foi somado retornando o total de 1,709,299. Valor incorreto, ainda que o subtotal, mesmo com os filtros errados, esteja correto.

Voltando um pouco no post quando aplicamos o contexto com o slicer, CY2008 filtrava apenas para os meses de Janeiro e Fevereiro, mas, como houve uma arbitrariedade por conta da transição de contexto e a sua sobrescrita, a função AVERAGEX passa calcular a medida dos 4 meses para cada ano, o que não era o desejado.

Se for para manter com a função values, teríamos de utilizar a coluna Calendar Year Month, que dentro da função de agregação, mostraria um resultado melhor. 

Veja a imagem retirada da tabela Date.

Como values trabalha com valores distintos e cada mês e ano são únicos, a coluna Calendar Year Month é a mais apropriada.

Agora temos os resultados corrigidos.

SOLUÇÃO COM KEEPFILTERS

Até aqui vimos possíveis problemas quando temos uma arbitrariedade nos contextos ou quando há uma transição implícita. 

Ainda que a solução com values tenha sido válida, vamos verificar como poderíamos solucionar utilizando keepfilters.

Como mostrado, o problema abaixo se deu por termos a coluna Month dentro de uma values, sendo que para o filtro Calendar Year, existem dois meses de Janeiro, Fevereiro e etc.

Se quiséssemos manter essa solução, o ideal seria adicionar a função keepfilters que irá manter os filtros criados pelo slicer que modificaram de forma arbitrária o filter context  que criamos com a medida.

Veja que o resultado para as duas formas de solução são exatamente iguais, mostrando a versatilidade para resolver a questão.

Uma outra maneira de se chegar ao resultado, mas conhecendo as limitações e sabendo que irá sobrepor o filter context, seria utilizando a função ALL.

Para esta situação, acaba funcionando bem, mas tenha cuidado com as restrições que são impostas aos filtros.

CONCLUSÃO

Neste post quis aprofundar um pouco mais nos conceitos de filtro e da função keepfilters para entender melhor como ela age dentro do PBI.

Deu para perceber que ela pode ser de grande ajuda quando ocorre uma transição implícita, mantendo o resultado desejado mesmo com funções iteradoras.

Vimos um pouco mais do funcionamento da keepfilters e como ela interage quando temos alguma X-function na sua construção. 

Tenha cuidado quando for mudar o filtro de forma arbitrária, o resultado pode não ser o desejado, tenha consciência de como aquele filtro irá agir e o que ele irá modificar.

E se for utilizar values, tenha cuidado com a coluna escolhida, vimos como o resultado pode ser modificado!

Espero que gostem, saúde.