Výpočet průměru a váženého průměru v jazyku DAX

Úvodní obrázek

Tento příspěvek obsahuje příklady výpočtu průměru a váženého průměru v jazyku DAX a v Power BI. Cílem příspěvku je porovnání těchto dvou přístupů k výpočtu průměru, a to z pohledu rychlosti vyhodnocení. V první části příspěvku si na jednoduchém příkladu ukážeme způsob, jak vytvořit v jazyku DAX dva výpočty průměru každý jiným způsobem. Druhá část příspěvku popisuje situaci, ve které může být vážený průměr brán jako jedna z optimalizačních technik v případě problémů s rychlostí vyhodnocení měřítka s výpočtem průměru.

K tomuto tématu je k dispozici také video:

Všechny příklady v tomto příspěvku jsou vytvořeny ve cvičném Power BI souboru Power BI Adventure Works DW 2020.pbix. Soubor s řešením je k dispozici ke stažení níže pod tímto příspěvkem.

Poznámka: Všechny obrázky v tomto příspěvku pocházejí z jiného souboru, než je soubor přiložený ke stažení pod tímto příspěvkem. Struktura souboru použitého jako podklad pro tento článek je stejná jako struktura přiloženého souboru, pouze tento soubor použitý pro obrázky obsahuje více záznamů ve faktové tabulce 'Sales' (více než 10 000 000 záznamů). Důvodem pro použití tohoto většího Power BI souboru jako podklad pro tento článek je zdůraznění rozdílu mezi rychlostí vyhodnocení jednotlivých výpočtů. Tyto rozdíly by byly v originálním (menším) souboru Adventure Works DW 2020.pbix zanedbatelné.

Výpočet váženého průměru v jazyku DAX

V použitém Power BI souboru je v tabulce 'Sales' mimo jiné sloupec 'Sales'[Order Quantity], který obsahuje v každém řádku počet prodaných kusů aktuálního produktu. Pokud bychom chtěli získat průměrný počet prodaných kusů v aktuálním kontextu vyhodnocení, stačí tento sloupec vložit do agregační funkce AVERAGE() následujícím způsobem.

Měřítko:

Množství (průměr) = AVERAGE(Sales[Order Quantity])

Nové měřítko si můžeme vložit do vizuálu matice spolu s roky a kategoriemi produktů v řádcích.

Výpočet průměru a váženého průměru v jazyku DAX

Na obrázku výše můžeme vidět hodnoty měřítka [Množství (průměr)], které je vyhodnocené pro jednotlivé kategorie a roky. V řádku souhrnů pak na toto měřítko nepůsobí žádné filtry, a průměr je proto v řádku souhrnů spočítán na základě všech hodnot ve sloupci 'Sales'[Order Quantity].

V jazyku DAX můžeme stejného výsledku obvykle dosáhnout více různými způsoby. Pro výpočet průměru můžeme použít například metodu nazvanou vážený průměr. Výpočet váženého průměru spočívá v sečtení násobků hodnot pro průměr vynásobených jejich váhami, a vydělením tohoto součtu násobků součtem vah. Pokud tuto logiku převedeme na výpočet průměrných hodnot ze sloupce 'Sales'[Order Quantity], budeme dělit součet násobků počtů odebraného množství počtem výskytů každé hodnoty, a tento součet vydělíme součtem výskytů jednotlivých hodnot.

Měřítko:

Množství (vážený průměr) =
VAR HodnotyAPocty =
    ADDCOLUMNS
    (
        VALUES(Sales[Order Quantity]),
        "Pocty",
        CALCULATE(COUNTROWS(Sales))
    )
VAR Vypocet =
    DIVIDE
    (
        SUMX(HodnotyAPocty, Sales[Order Quantity] * [Pocty]),
        SUMX(HodnotyAPocty, [Pocty])
    )
RETURN
    Vypocet

Z pohledu jazyka DAX ve výše již uvedeném výpočtu nepracujeme se všemi více než 10 miliony hodnotami ze sloupce 'Sales'[Order Quantity], ale pouze s jedinečnými hodnotami z tohoto sloupce, které načítáme pomocí funkce VALUES() v první proměnné. Jedinečných hodnot ve sloupci 'Sales'[Order Quantity] je 41. Pro každou tuto hodnotu pak zjišťujeme počet výskytů v tabulce 'Sales', kdy tento počet výskytů představuje ve výpočtu váženého průměru váhu každé hodnoty.

Nové měřítko si můžeme vložit do původního vizuálu matice a zobrazit si výsledky.

Výpočet průměru a váženého průměru v jazyku DAX 2

Jak je možné vidět na obrázku výše, obě měřítka vrací stejné hodnoty. Nás ale bude více zajímat rychlost vyhodnocení obou měřítek.

Na následujícím obrázku je možné vidět výstřižek z DAX studia, a to z podokna Server Timings při vyhodnocení DAX dotazu s měřítkem [Množství (průměr)] v kontextu let a kategorií produktů.

Výpočet průměru a váženého průměru v jazyku DAX 3

Čas pro vyhodnocení stejného DAX dotazu s použitím měřítka [Množství (vážený průměr)] je pak možné vidět na následujícím obrázku.

Výpočet průměru a váženého průměru v jazyku DAX 4

Při prostém porovnání časů vyhodnocení obou DAX dotazů je zřejmé, že měřítko [Množství (průměr)] je vyhodnoceno rychleji (20 ms) než měřítko [Množství (vážený průměr)] (57 ms). V tomto konkrétním případě nemá smysl dodatečná práce s převodem výpočtu na vážený průměr.

Existují ovšem situace, kdy hodnoty pro průměr nejsou uloženy přímo ve sloupcích v modelu a musíme je získat na základě dodatečného výpočtu. V těchto situacích může být výpočet váženého průměru vyhodnocen výrazně rychleji, jak si ukážeme v následujícím příkladu.

Optimalizace výpočtu průměrné doby vyřízení objednávky

V tabulce 'Sales' jsou mimo jiné sloupce 'Sales'[Order Date] a 'Sales'[Ship Date]. Jak už anglické názvy těchto sloupců napovídají, v prvním případě se jedná o datumy objednávek, ve druhém případě se jedná o sloupec s datumy odeslání zboží. Na základě těchto dvou sloupců můžeme jednoduše dopočítat počet dnů mezi objednávkou a odesláním zboží a z těchto hodnot spočítat průměr. Výpočet průměrné doby vyřízení objednávky ale nebude tak jednoduchý, protože budeme chtít vyloučit z doby vyřízení objednávky víkendy a svátky. Informace, zda je konkrétní den pracovní den jsou uloženy v tabulce 'Date' ve sloupci 'Date'[Working day]. Ve sloupci 'Date'[Holiday] jsou pak hodnotou TRUE označeny ty dny, které jsou svátky. Celý výpočet průměrného počtu pracovních dnů potřebných pro vyřízení objednávky tak může vypadat například následovně.

Měřítko:

Zpracování objednávky (průměr) =
AVERAGEX
(
    Sales,
    CALCULATE
    (
        COUNTROWS('Date'),
        DATESBETWEEN('Date'[Date], Sales[Order Date], Sales[Ship Date]),
        'Date'[Working day] = TRUE(),
        'Date'[Holiday] = FALSE()
    )
)

Nové měřítko si můžeme opět vložit do vizuálu Matice s roky a kategoriemi produktů v řádcích.

Výpočet průměru a váženého průměru v jazyku DAX 5

Problémem měřítka [Zpracování objednávky (průměr)] je funkce CALCULATE(), která je použita v iterační funkci AVERAGEX(), kde je vyhodnocena v kontextu řádku tabulky 'Sales'. Funkce CALCULATE() mimo jiné mění kontext řádku na kontext filtru, a tuto operaci musí v měřítku [Zpracování objednávky (průměr)] provést pro každý řádek tabulky 'Sales', přesněji řečeno pro všechny řádky tabulky 'Sales' které jsou dostupné v aktuálním kontextu filtru.

V tomto konkrétním výpočtu proto může dávat smysl snížení počtu řádků v tabulce v prvním argumentu funkce AVERAGEX() na nezbytné minimum. Toho můžeme docílit právě technikou popsanou v prvním příkladu v tomto příspěvku.

Logika výpočtu průměrné doby zpracování objednávek pomocí váženého průměru tedy bude spočívat v tom, že si vytvoříme virtuální tabulku pouze s platnými kombinacemi hodnot ze sloupců 'Sales'[Order Date] a 'Sales'[Ship Date]. Následně si pro každý řádek této virtuální tabulky spočítáme počet pracovních dnů mezi těmito dvěma datumy a počet výskytů každé kombinace hodnot ze sloupců 'Sales'[Order Date] a 'Sales'[Ship Date] v tabulce 'Sales'. Na základě těchto dvou hodnot pak můžeme vypočítat průměrnou dobu zpracování objednávek pomocí váženého průměru.

Měřítko:

Zpracování objednávky (vážený průměr) =
VAR DnyOdDo = SUMMARIZE(Sales, Sales[Order Date], Sales[Ship Date])
VAR DnyOdDoHodnotyAPocty =
    ADDCOLUMNS
    (
        DnyOdDo,
        "Hodnoty",
        CALCULATE
        (
            COUNTROWS('Date'),
            DATESBETWEEN('Date'[Date], Sales[Order Date], Sales[Ship Date]),
            'Date'[Working day] = TRUE(),
            'Date'[Holiday] = FALSE()
        ),
        "Pocty",
        CALCULATE(COUNTROWS(Sales))
    )
VAR Vypocet =
    DIVIDE
    (
        SUMX(DnyOdDoHodnotyAPocty, [Hodnoty] * [Pocty]),
        SUMX(DnyOdDoHodnotyAPocty, [Pocty])
    )
RETURN
    Vypocet

Nové měřítko si můžeme přidat do původního vizuálu a ověřit si, že vrací stejné výsledky jako měřítko [Zpracování objednávky (průměr)].

Výpočet průměru a váženého průměru v jazyku DAX 6

Oba výpočty vrací stejné výsledky, a proto můžeme přistoupit k porovnání rychlosti vyhodnocení. Následující obrázek obsahuje mimo jiné čas vyhodnocení DAX dotazu s měřítkem [Zpracování objednávky (průměr)] v kontextu let a kategorií produktů.

Výpočet průměru a váženého průměru v jazyku DAX 7

Následující obrázek obsahuje čas vyhodnocení stejného DAX dotazu, tentokrát však s měřítkem [Zpracování objednávky (vážený průměr)].

Výpočet průměru a váženého průměru v jazyku DAX 8

Jak je možné vidět na obrázcích výše, měřítko [Zpracování objednávky (vážený průměr)] bylo vyhodnoceno výrazně rychleji než měřítko [Zpracování objednávky (průměr)]. V tomto konkrétním příkladu tedy dodatečná práce s vytvořením váženého průměru namísto prostého průměru může dávat smysl.

Shrnutí

Jak bylo možné vidět na prvním příkladu v tomto příspěvku, při výpočtu průměru z hodnot uložených ve sloupci v modelu většinou nebude mít smysl převádět výpočet průměru na výpočet váženého průměru, protože tyto jednoduché výpočty jsou velice dobře optimalizované. Situace je však jiná v případě, kdy hodnoty pro průměr nejsou uložené ve sloupcích v modelu a získáváme je na základě dalšího výpočtu, který je nutné vyhodnotit uvnitř iterační funkce AVERAGEX(). V takovéto situaci může dávat smysl snížení počtu iterovaných řádků v prvním argumentu funkce AVERAGEX() na minimum, jak bylo možné vidět ve druhém příkladu v tomto příspěvku.

č. 93

Komentáře