DAX funkce FILTER

Úvodní obrázek

Funkce FILTER() je iterační funkce, která vrací tabulku uvedenou v prvním argumentu funkce se všemi řádky z této tabulky, pro které platí logická podmínka uvedená ve druhém argumentu funkce. Prvním argumentem funkce FILTER() může být tabulka nebo také funkce vracející tabulku. Druhým argumentem funkce je logický výraz, který je vyhodnocen v kontextu každého řádku tabulky uvedené v prvním argumentu funkce. Syntaxe funkce FILTER() vypadá následovně.

Syntaxe funkce FILTER:

FILTER(<tabulka>,<filtr>)

Přestože funkce FILTER() vrací tabulku, můžeme tuto funkci používat v různých typech DAX výpočtů. Funkce FILTER() může být použita samostatně nebo jako součást více funkcí při vytvoření nové počítané tabulky. Pro funkci FILTER() můžeme najít uplatnění také při vytváření nových počítaných sloupců, v definici měřítek nebo při psaní DAX dotazů. Níže jsou uvedeny některé možné příklady využití funkce FILTER(). Příklady jsou v uvedeny postupně od nejjednodušších až po složitější, včetně příkladů s použitím funkce FILTER() jako filtru ve funkci CALCULATE() při vytváření měřítek.  

Příklady funkce FILTER

Výpočty uvedené v tomto příspěvku jsou vytvořeny ve cvičném souboru Adventure Works DW 2020.pbix. Tyto ukázky jsou vytvořeny za účelem vysvětlení dané funkcionality a ne k vyřešení konkrétního příkladu. Praktické příklady můžete najít na stránce DAX - Příklady. Soubor s řešenými příklady z tohoto příspěvku je dostupný ke stažení níže pod tímto článkem.

Jednoduchý příklad použití funkce FILTER 

V prvním příkladu si ukážeme nejjednodušší způsob použití funkce FILTER(). Funkci FILTER() můžeme použít pro vytvoření nové počítané tabulky, kde při vyhodnocení nepůsobí na funkci žádné vnější filtry, jak je tomu obvykle v reportech při vyhodnocení měřítek. Následující výraz vrací tabulku se všemi sloupci z tabulky 'Product', ale pouze s řádky, které obsahují záznamy o produktech v bílé barvě.

Počítaná tabulka:

Bílé produkty =
FILTER
(
'Product',
'Product'[Color] = "White"
)

Prvním argumentem je v modelu již existující tabulka 'Product'. Druhým argumentem funkce FILTER() je logický výraz, který je vyhodnocen pro každý řádek tabulky uvedené v prvním argumentu. Pokud je logický výraz v konkrétním řádku vyhodnocen jako TRUE, bude tento řádek součástí výsledné tabulky. Řádky, pro které je logický výraz ve druhém argumentu funkce vyhodnocen jako FALSE, nebudou součástí výsledku. V původní tabulce 'Product' jsou pouze čtyři produkty v bílé barvě. Výsledná tabulka má proto pouze tyto čtyři řádky.

DAX funkce FILTER 2

Pokud bychom chtěli ve výsledné tabulce pouze vybrané sloupce, například sloupce 'Product'[ProductKey] a 'Product'[Product], můžeme vložit funkci FILTER() do funkce SELECTCOLUMS() následujícím způsobem.

Počítaná tabulka:

Bílé produkty (2) =
SELECTCOLUMNS
(
FILTER
(
'Product',
'Product'[Color] = "White"
),
"ProductKey", 'Product'[ProductKey],
"ProductName", 'Product'[Product]
)

Funkce SELECTCOLUMNS() z tabulky uvedené v prvním argumentu funkce vrátí pouze ty sloupce, které uvedeme ve druhém a dalších argumentech této funkce. Výsledkem jsou čtyři stejné řádky jako v prvním příkladu, ale pouze se dvěma sloupci, které jsme vybrali ve vnější funkci SELECTCOLUMNS(). 

DAX funkce FILTER 3

Při pohledu na předchozí příklad by nás mohlo napadnout, proč načítáme ve vnitřní funkci FILTER() všechny sloupce z tabulky 'Product', když ve výsledku potřebujeme pouze dva sloupce z původní tabulky. V prvním argumentu funkce FILTER() se nemusíme odkazovat na celou tabulku, ale můžeme použít také funkci vracející tabulku. Můžeme si tak vytvořit tabulku se dvěma sloupci, a pro tuto tabulku následně vyhodnotit podmínku ve druhém argumentu funkce FILTER(). Myšlenka by to mohla být dobrá, ale nefunkční.

Počítaná tabulka:

Bílé produkty (špatně) =
FILTER
(
ALL('Product'[ProductKey],'Product'[Product]),
'Product'[Color] = "White"
)

Výše uvedený výraz nebude fungovat. Narážíme tím na důležitou vlastnost všech iteračních funkcí, se kterou musíme při psaní DAX výrazů počítat. V iteračních funkcích se můžeme odkazovat pouze na sloupce, které jsou součástí iterované tabulky a její rozšířené verze. Ve funkci FILTER() nefiltrujeme model, jako je tomu u funkcí CALCULATE() nebo CALCULATETABLE(), ale filtrujeme pouze tabulku uvedenou v prvním argumentu této funkce. Pokud tedy do tabulky v prvním argumentu této funkce přidáme i sloupec s barvami produktů, můžeme již tento sloupec použít v logickém výrazu ve druhém argumentu a výsledkem bude opět tabulka se čtyřmi řádky, ale tentokrát se třemi sloupci.

Počítaná tabulka:

Bílé produkty (4) =
FILTER
(
ALL('Product'[ProductKey],'Product'[Product],'Product'[Color]),
'Product'[Color] = "White"
)

Výsledek můžeme vidět na následujícím obrázku.

DAX funkce FILTER 4

Ve výše uvedených jednoduchých příkladech jsme pracovali s jednou podmínkou. Pokud chceme pomocí ve funkci FILTER() vyhodnotit v každém řádku tabulky více podmínek, máme opět několik možností, jak dosáhnout stejného výsledku.

Více logických filtrů ve funkci FILTER

Ve funkci FILTER() můžeme použít ve druhém argumentu pouze jeden výraz, který vrací pro každý řádek tabulky hodnotu TRUE nebo FALSE. Pokud chceme vyhodnotit v každém řádku tabulky více podmínek, můžeme tyto podmínky vložit do jedné nebo více funkcí AND(). Následující výraz vrátí tabulku produktů, které mají bílou barvu a katalogovou cenu menší než 9.

Počítaná tabulka:

Bílé produkty a cena < 9 =
FILTER
(
'Product',
AND
(
'Product'[Color] = "White",
'Product'[List Price] < 9
)
)

Stejný výsledek dostaneme zřetězením podmínek pomocí AND operátoru, kterým je v jazyku DAX dvojitý ampersand (&&).

Počítaná tabulka:

Bílé produkty a cena < 9 (2) =
FILTER
(
'Product',
'Product'[Color] = "White" &&
'Product'[List Price] < 9
)

Další možností, nicméně zřejmě méně čitelnou a asi i méně používanou, může být vložení dvou funkcí FILTER() do sebe, kdy v každé funkci vyhodnotíme jinou podmínku.

Počítaná tabulka:

Bílé produkty a cena < 9 (3) =
FILTER
(
FILTER
(
'Product',
'Product'[Color] = "White"
),
'Product'[List Price] < 9
)

Všechny tři předchozí výrazy vrací stejný výsledek, a to bílé produkty s katalogovou cenou menší než 9. 

DAX funkce FILTER 5

Jednotlivé logické výrazy můžeme vyhodnotit také v logickém OR vztahu. Pomocí funkce OR() můžeme například vytvořit tabulku produktů, ve které budou produkty s bílou barvou nebo produkty s katalogovou cenou menší než 7.

Počítaná tabulka:

Bílé produkty nebo cena < 7 =
FILTER
(
'Product',
OR
(
'Product'[Color] = "White",
'Product'[List Price] < 7
)
)

Stejného výsledku dosáhneme použitím operátoru OR (||).

Počítaná tabulka:

Bílé produkty nebo cena < 7 (2) =
FILTER
(
'Product',
'Product'[Color] = "White" ||
'Product'[List Price] < 7
)

Výsledkem obou předchozích výrazů bude stejná tabulka, obsahující produkty v bílé barvě nebo s produkty s cenou nižší než 7.

DAX funkce FILTER 6

V logickém výrazu ve druhém argumentu funkce FILTER() můžeme použít i komplexní výpočty nebo měřítka. Před psaním podobných výrazů ale doporučuji seznámit se s principem kontextu řádku, s principem kontextu filtru a s funkcionalitou změny kontextu řádku na kontext filtru, ke které dochází při vyhodnocení funkcí CALCULATE() a CALCULATETABLE() v kontextu řádku. Dále bychom měli pamatovat, že každé měřítko je vždy na pozadí obaleno do pro autora skryté funkce CALCULATE(), a proto dochází ke změně kontextu řádku na kontext filtru také při vyhodnocení jakéhokoliv měřítka v kontextu řádku.

Logické filtry ve funkci FILTER na základě výsledku měřítka

Logickou podmínku ve funkci FILTER() můžeme vytvořit také na základě výsledku výpočtu nebo měřítka. To je jednou z velkých výhod funkce FILTER(). Pro tento příklad si můžeme vytvořit měřítko, které bude vracet sumu za prodané produkty v aktuálním kontextu vyhodnocení.

Měřítko:

Prodeje = SUM(Sales[Sales Amount])

Měřítko [Prodeje] pak můžeme použít jako součást logické podmínky ve druhém argumentu funkce FILTER(). Následující výraz vrací tabulku s produkty, jejichž prodeje jsou vyšší než 2 000 000 Kč. 

Pozn.: V této části příspěvku budu z důvodu přirozenosti uvádět částky v korunách, přestože v modelu jsou částky uvedené v amerických dolarech.

Počítaná tabulka:

Produkty prodeje > 2 mil Kč =
FILTER
(
'Product',
[Prodeje] > 2000000
)

Výsledkem je tabulka se třemi produkty, pro které platí, že suma prodejů přesáhla 2 mil Kč.

DAX funkce FILTER 7

Do takto vytvořené tabulky si pak můžeme jednoduše přidat také sloupec se sumou celkových prodejů, abychom viděli hodnotu pro podmínku zhmotněnou přímo v tabulce.

Počítaná tabulka:

Produkty prodeje > 2 mil Kč (2) =
ADDCOLUMNS
(
FILTER
(
'Product',
[Prodeje] > 2000000
),
"Prodeje",
[Prodeje]
)

Výsledkem je podobná tabulka jako v předchozím případě, navíc je v tabulce pouze sloupec se sumou celkových prodejů pro aktuální produkt.

DAX funkce FILTER 8

Stejného výsledku můžeme dosáhnout i bez použití měřítka. Výpočet sumy prodejů pouze musíme vložit do funkce CALCULATE(), abychom vyvolali změnu kontextu řádku na kontext filtru.

Počítaná tabulka:

Produkty prodeje > 2 mil Kč (3) =
ADDCOLUMNS
(
FILTER
(
'Product',
CALCULATE(SUM(Sales[Sales Amount])) > 2000000
),
"Prodeje",
CALCULATE(SUM(Sales[Sales Amount]))
)

Funkce FILTER() může být užitečná také při použití jako filtru ve funkci CALCULATE(), jak můžete vidět v následující části příspěvku.

Funkce FILTER jako filtr ve funkci CALCULATE

Jako filtry ve funkci CALCULATE() můžeme použít tabulky nebo funkce vracející tabulky. Díky tomu můžeme vytvořit komplexní filtry pro ovlivnění výsledku prvního argumentu ve funkci CALCULATE(). Komplexní filtry dávají DAX vývojáři velkou sílu k vytvoření užitečných kalkulací, ale na druhou stranu také velkou zodpovědnost za správné výsledky. 

Pokud ve funkci CALCULATE() používáme komplexní filtry, měli bychom vědět, jak jsou jednotlivé argumenty použitých funkcí vyhodnoceny v aktuálním kontextu. Filtry ve funkci CALCULATE() jsou vyhodnoceny v originálním kontextu vyhodnocení, ve kterém je měřítko použito. Pokud tedy budeme chtít použít funkci FILTER() jako argument ve funkci CALCULATE(), měli bychom vědět, že:

  • Tabulka uvedená v prvním argumentu funkce FILTER() je vyhodnocena v originálním kontextu vyhodnocení.
  • Výraz uvedený v druhém argumentu funkce FILTER() je vyhodnocen v originálním kontextu vyhodnocení a současně v nově vytvořeném kontextu řádku.
Jako příklad si můžeme v měřítku spočítat počet produktů, jejichž prodeje jsou větší než 2 mil Kč. Jako filtr ve funkci CALCULATE() tedy použijeme stejný výraz založený na funkci FILTER(), který jsme již dříve použili pro vytvoření počítané tabulky.

Měřítko:

Počet produktů s prodeji > 2 mil Kč =
CALCULATE
(
COUNTROWS('Product'),
FILTER
(
'Product',
[Prodeje] > 2000000
)
)

Z předchozího příkladu, ve kterém jsme si vytvořili novou počítanou tabulku právě s produkty s prodeji většími než 2 mil Kč již víme, že v použitém modelu jsou takové produkty tři. Pokud vložíme nové měřítko ve vizuálu tabulky bez jakýchkoliv aktivních vnějších filtrů, výsledek bude podle očekávání číslo tři.

DAX funkce FILTER 9

Jedná se o stejné tři produkty, které jsme mohli vidět při vytvoření nové počítané tabulky s hodnotami z tabulky 'Product' filtrované pouze na produkty se sumou prodejů větší než 2 mil Kč. To si můžeme ověřit, pokud přidáme do řádků použitého vizuálu názvy produktů z tabulky 'Product'.

DAX funkce FILTER 10

V tabulce vidíme pouze tři produkty, protože pro všechny ostatní produkty je výsledkem měřítka [Počet produktů s prodeji > 2 mil Kč] hodnota BLANK. Co se ale stane, pokud v řádcích tabulky použijeme například roky z tabulky 'Date'? 

DAX funkce FILTER 11

Měřítko [Počet produktů s prodeji > 2 mil Kč] je nyní vyhodnoceno v každém řádku tabulky v kontextu filtru daného roku. Pokud se zaměříme na argumenty funkce FILTER(), tak na sedmém řádku v kódu na obrázku výše je odkaz na tabulku 'Product'. Tuto tabulku neovlivňuje filtr nastavený na aktuální rok, protože tabulka 'Date' s roky filtruje pouze tabulku 'Sales' s prodeji, díky vazbám nastaveným v použitém modelu.

DAX funkce FILTER 12

Nyní se ještě jednou můžeme podívat na definici popisovaného měřítka.

DAX funkce FILTER 13

Na osmém řádku kódu v měřítku [Počet produktů s prodeji > 2 mil Kč] vyhodnocujeme, zda je měřítko [Prodeje] v aktuálním kontextu větší než 2 000 000 Kč. Jaký je ale aktuální kontext, ve kterém je měřítko [Prodeje] vyhodnoceno? Na měřítko [Prodeje] působí vnější filtr s aktuálním rokem v každém řádku vizuálu tabulky. Současně na měřítko [Prodeje] působí filtr vytvořený změnou kontextu řádku na kontext filtru v každém řádku tabulky 'Produkt', v prvním argumentu funkce FILTER(). Jelikož suma prodejů žádného jednotlivého produktu v kterémkoliv roku nepřesáhla částku 2 000 000 Kč, výsledkem měřítka [Počet produktů s prodeji > 2 mil Kč] je pro každý rok hodnota BLANK. V řádku souhrnů již na výpočet měřítka nepůsobí filtr s roky, a měřítko [Prodeje] je ve druhém argumentu funkce FILTER()  vyhodnoceno pouze v kontextu řádku každého produktu, a vrací tedy v každém řádku tabulky 'Product' sumu prodejů aktuálně iterovaného produktu za všechny roky. Jak již víme, v souhrnu za všechny roky přesáhla suma prodejů částku 2 mil Kč u tří produktů, proto je výsledek měřítka v řádku souhrnů číslo 3. 

Vytvořme si nyní obdobné měřítko, u kterého pouze změníme částku, která tvoří hranici pro započítání konkrétního produktu do výpočtu. Hranici si můžeme změnit z 2 000 000 Kč na 1 000 000 Kč, abychom ve vizuálu viděli více produktů. Nové měřítko tedy bude vypadat následovně.

Měřítko:

Počet produktů s prodeji > 1 mil Kč =
CALCULATE
(
COUNTROWS('Product'),
FILTER
(
'Product',
[Prodeje] > 1000000
)
)

Měřítko si opět vložíme do vizuálu tabulky s roky v řádcích. Na obrázku níže můžeme vidět, že pro rok 2019 jsou v použitém modelu čtyři produkty, které v měli v daném roce sumu prodejů větší než 1 000 000 Kč. V roce 2020 šlo o tři produkty, které splňovali definovanou podmínku. V řádku souhrnů můžeme vidět číslo 41. Nyní již víme, že v řádku souhrnů nepůsobí na měřítko žádný filtr s roky, a výsledek proto můžeme interpretovat tak, že v použitém modelu je 41 produktů s prodeji vyššími než 1 000 000 Kč, nicméně s prodeji souhrnně za všechny roky.

DAX funkce FILTER 14

Nyní s větším počtem produktů splňující podmínku filtru můžeme lépe vidět složitost takto definovaného výpočtu. Na první pohled je zřejmé, že suma v řádku souhrnů je vyšší, než součet jednotlivých hodnot v řádcích tabulky. Nejde ovšem o problém, který by spočíval ve špatném vyhodnocení měřítka. Měřítko [Počet produktů s prodeji > 1 mil Kč] vrací přesně takové hodnoty, jaké jsme definicí měřítka požadovali. Nicméně pokud měřítko použijeme s roky v řádcích vizuálu, nechceme pravděpodobně vědět počet prodejů s celkovou částkou vyšší než 1 000 000 Kč za všechny roky, ale počet produktů se sumou prodejů vyšší než 1 000 000 Kč v každém jednotlivém roce. Proč měřítko vrací jinou hodnotu v řádku souhrnů a jinou hodnotu v jednotlivých řádcích tabulky už jsme si řekli při popisu vyhodnocení měřítka s hranicí prodejů 2 000 000 Kč. Zaměřme se nyní na to, jak dostat v takto definovaném výpočtu správný výsledek v řádku souhrnů. V řádku souhrnů potřebujeme celý výpočet vyhodnotit v každém jednotlivém roce samostatně, a výsledky výpočtu za každý jednotlivý rok následně sečíst. Takto popsanou definici výpočtu splňuje následující měřítko.

Měřítko:

Počet produktů s ROČNÍMI prodeji > 1 mil Kč =
SUMX
(
VALUES('Date'[Rok]),
CALCULATE
(
COUNTROWS('Product'),
FILTER
(
'Product',
[Prodeje] > 1000000
)
)
)

Nové měřítko si můžeme zobrazit spolu s původním výpočtem ve stejném vizuálu.

DAX funkce FILTER 15

Nyní již suma v řádku souhrnů odpovídá součtu jednotlivých hodnot v řádcích s roky. Zaměřme se nejdříve na vyhodnocení výpočtu v řádku souhrnů. V řádku souhrnů je výsledkem prvního argumentu funkce SUMX() (v kódu na řádku 4) tabulka s jedním sloupcem, která obsahuje všechny roky viditelné v aktuálním kontextu vyhodnocení. Funkce SUMX() je iterační funkce, která vytvoří pro každý řádek tabulky s roky (ve 4 řádku kódu) nový kontext řádku. Tento kontext řádku je díky funkci CALCULATE() přeměněn na kontext filtru. Pro každý jeden rok proto dojde k vyhodnocení výpočtu na řádku 5 až 13 v kontextu filtru aktuálně iterovaného roku. Výpočet na řádku 5 až 13 je stejný výpočet, který jsme dnes již několikrát použili pro získání počtu produktů  splňujících podmínku definovanou ve druhém argumentu funkce FILTER(). Jakmile je tento výpočet vyhodnocen v kontextu nově vzniklého filtru pro každý rok, díky změně kontextu řádku na kontext filtru ve funkci CALCULATE(), dojde k sečtení všech jednotlivých výsledků za všechny roky. Výsledná hodnota v řádku souhrnů je tedy součtem hodnoty 4 pro rok 2019 a hodnoty 3 pro rok 2020. V ostatních letech neměl žádný produkt tržby větší než 1 000 000 Kč. 

Nyní se můžeme podívat na vyhodnocení měřítka v řádcích s roky. Zaměřme se například na rok 2019. V řádku vizuálu Tabulky s rokem 2019 působí  na měřítko filtr obsahující pouze rok 2019. Výsledkem prvního argumentu funkce SUMX() (řádek 4 v kódu) je proto tabulka pouze s rokem 2019, díky filtru působícím na výpočet právě z řádků tabulky. Proto je celý výpočet mezi řádky 5 až 13 vyhodnocen pouze pro tento jeden konkrétní rok zobrazený v aktuálním řádku vizuálu. Funkce SUMX() následně vrátí výsledek pouze pro rok 2019, protože žádné další hodnoty nemá k dispozici, opět díky filtru pouze na rok 2019. Obdobně je měřítko vyhodnoceno v ostatních řádcích vizuálu s roky, pouze v každém řádku vizuálu působí na výpočet filtr s jiným rokem.

Na výše uvedeném jednoduchém příkladu jsem se pokusil popsat, jak jsou vyhodnoceny jednotlivé argumenty ve funkci FILTER() v případě, že tuto funkci použijeme jako filtr ve funkci CALCULATE(). Nejednalo se o žádný smysluplný výpočet, který bychom mohli používat v reportech, ale o teoretický příklad s cílem vysvětlit vlastnosti funkce FILTER(). I přesto ale platí, že informace uvedené v této části příspěvku jsou velmi důležité, pokud obecně pracujeme s komplexními filtry při definici měřítek.

Další ukázky s použitím tabulek jako filtrů ve funkci CALCULATE() můžete najít v příspěvku Tabulky jako filtry ve funkci CALCULATE().  Praktický příklad použití funkce FILTER() jako filtru ve funkci CALCULATE() můžete najít například v příspěvku s výpočtem klouzavých průměrů.

Shrnutí

Příklady uvedené v tomto příspěvku jsou pouze ilustrativní s cílem popsat konkrétní funkcionalitu. Nicméně čím více příkladů a způsobů použití jednotlivých funkcí dokážeme vstřebat, tím kreativnější a efektivnější můžeme být při řešení komplexních příkladů. Nejdůležitější částí tohoto příspěvku je popis vyhodnocení funkce FILTER() při použití jako filtru ve funkci CALCULATE() při vytváření měřítek. Pokud totiž použijeme funkci FILTER() v měřítku, musíme vždy myslet na to, že argumenty ve funkci CALCULATE() jsou vyhodnoceny v originálním kontextu, ve kterém je vyhodnoceno celé měřítko. Současně pokud používáme funkci FILTER() jako argument ve funkci CALCULATE(), měli bychom v prvním argumentu funkce FILTER() používat pouze sloupce nezbytně nutné k vytvoření filtru, a ne celou tabulku, pokud to není nezbytně nutné. 

Oficiální Microsoft dokumentace funkce FILTER:

č. 42

Komentáře