Práce s časem
Cíle kapitoly
- Pochopit koncept Time Intelligence v Power BI
- Vytvořit a správně označit tabulku kalendáře
- Ovládat funkce DATEADD, TOTALYTD
, SAMEPERIODLASTYEARNa blogu Jiřího Neorala: Časové kalkulace v DAX problém otevřeného období — specifický problém s otevřeným obdobím v time intelligence
- Implementovat srovnání rok na rok (YoY)
- Vytvářet running totals (YTD, QTD, MTD)
- Vyhnout se běžným chybám
Co je Time Intelligence?
Time Intelligence (TI) je technika v Power BI/DAX, která umožňuje provádět pokročilé časové analýzy. Jde o porovnávání dat v čase, vytváření trendů a Year-over-Year analýz.
Typické případy použití
- Year-over-Year (YoY): Srovnání prodeje letošního roku s minulým rokem
- Year-to-Date (YTD): Součet tržeb od začátku roku po dnešek
- Trend analýza: Vývoj prodeje v čase (týdny, měsíce)
- Sezónnost: Analýza opakujících se vzorů v čase
- Running totals: Kumulativní součty
Proč ne klasický CALCULATE?
Ačkoli můžete psát Time Intelligence "ručně" pomocí CALCULATE, speciální TI funkce jsou čitelnější a méně náchylné k chybám (výkonnostně jsou na úrovni moderního DAX kompilátoru ekvivalentní):
-- Ručně (verbose, ale výkonnostně ekvivalentní): CALCULATE(SUM(FactInternetSales[SalesAmount]), DimDate[Year] = MAX(DimDate[Year]) - 1 ) -- Pomocí TI funkce (čitelnější, doporučeno): CALCULATE(SUM(FactInternetSales[SalesAmount]), PREVIOUSYEAR(DimDate[Date]) )
Tabulka kalendáře (Date Table)
Tabulka kalendáře je ústřední prvek Time Intelligence. Bez ní TI funkce nefungují správně nebo vůbec.
Požadavky na Date Table
- Kontinuální data: Bez mezer mezi daty (žádné víkendy či svátky, pokud nejsou vynechány záměrně)
- Jedinečnost: Jeden řádek na den (žádné duplikáty)
- Označení v modelu: Musí být označena jako "Date Table" v Power BI
- Primární klíč: Sloupec DateKey nebo FullDate bez duplikátů
- Rozsah: Pokryje všechny data v FactTable (minimálně o rok více)
Vytvoření Date Table v Power Query
Klasický vzor v M (Power Query):
let MinDate = Date.From("2015-01-01"), MaxDate = Date.From("2024-12-31"), DateRange = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, Duration.FromDays(1)), DateTable = Table.FromList(DateRange, Splitter.SplitByNothing(), {"Date"}), AddYear = Table.AddColumn(DateTable, "Year", each Date.Year([Date])) in AddYear
Označení Date Table v Power BI
Po importu musíte tabulku označit jako Date Table:
- Klikněte na tabulku v modelu (v Modeling tab)
- Vyberte "Mark as date table
"Na blogu Jiřího Neorala: Zlo dostalo jméno Auto date/time intelligence — proč vypnout Auto date/time — důležitý antipattern
- Zvolte správný sloupec (obvykle DateKey nebo FullDate)
Standardní sloupce v Date Table
Typicky Date Table obsahuje:
- Date – Samotné datum (YYYY-MM-DD)
- DateKey – Číslo jako 20231225 (YYYYMMDD)
- Year – Rok (2023)
- Quarter – Čtvrtletí (1, 2, 3, 4)
- Month – Měsíc (1-12)
- MonthName – Název měsíce (Jan, Feb...)
- Day – Den v měsíci (1-31)
- DayOfWeek – Den týdne (1=pondělí...7=neděle)
- WeekNumber – Číslo týdne v roce
Základní Time Intelligence funkce
DATEADD — Posun o období
Posunuje datum o specifikovaný počet období. V kurzu používáme přesně -365 dní místo SAMEPERIODLASTYEAR, protože data přesahují roky s různým počtem dní:
-- Internet LY: prodeje stejného období před rokem (přesně -365 dní) -- SAMEPERIODLASTYEAR je zakomentovaná varianta — obě fungují. -- DATEADD(-365, DAY) je záměrná volba pro přesné denní srovnání v tomto modelu; -- SAMEPERIODLASTYEAR je vhodnější pro měsíční/kvartální granularitu (mapuje celá období). # Internet LY = CALCULATE( [# Internet] ,--SAMEPERIODLASTYEAR('DimDate'[Date]) DATEADD('DimDate'[Date], -365, DAY) )
TOTALYTD — Kumulativně od začátku roku
Součet od začátku roku do aktuálního data. Reálná míra z kurzu pro Reseller kanál:
-- Reseller YTD: kumulativní součet reseller prodejů od 1.1. # Reseller YTD = TOTALYTD([# Reseller], 'DimDate'[Date])
TOTALMTD, TOTALQTD — Měsíc a čtvrtletí
Podobně jako TOTALYTD, ale pro měsíc nebo čtvrtletí:
Sales MTD = TOTALMTD(SUM(FactInternetSales[SalesAmount]), DimDate[Date]) Sales QTD = TOTALQTD(SUM(FactInternetSales[SalesAmount]), DimDate[Date])
SAMEPERIODLASTYEAR — Stejné období loni
Vrací data ze stejného období minulého roku. Používá se v Calculation Groups pro elegantní opakovanou aplikaci na libovolnou míru:
-- Přímé použití v míře: Sales LY = CALCULATE( [# Internet] ,SAMEPERIODLASTYEAR('DimDate'[Date]) ) -- V Calculation Group (viz sekce Calculation Groups níže): -- calculationItem LY = CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR(...))
PREVIOUSYEAR, PREVIOUSMONTH — Celé období
Vrací data z minulého roku/měsíce, ale zachovává všechna data daného období:
Sales Previous Year Total = CALCULATE( SUM(FactInternetSales[SalesAmount]), PREVIOUSYEAR(DimDate[Date]) )
DATESYTD, DATESINPERIOD — Custom periody
Vytvoří vlastní seznam dat pro Time Intelligence:
Sales Last 30 Days = CALCULATE( SUM(FactInternetSales[SalesAmount]), DATESINPERIOD( DimDate[Date], MAX(DimDate[Date]), -30, DAY ) )
Year-over-Year (YoY) srovnání
YoY je jedním z nejčastějších případů použití Time Intelligence – srovnání výkonu letošního roku s minulým rokem.
Jednoduchý YoY — Aktuální vs Minulý rok
Sales Current Year = SUM(FactInternetSales[SalesAmount]) Sales Last Year = CALCULATE( SUM(FactInternetSales[SalesAmount]), SAMEPERIODLASTYEAR(DimDate[Date]) ) YoY Growth Absolute = Sales Current Year - Sales Last Year YoY Growth % = DIVIDE( YoY Growth Absolute, Sales Last Year )
YoY v kontextu měsíce
Pokud je měra použita v kontingenční tabulce s měsíci, YoY funguje automaticky:
Sales = SUM(FactInternetSales[SalesAmount]) Sales PY (Prior Year) = CALCULATE( Sales, SAMEPERIODLASTYEAR(DimDate[Date]) )
Když vizuál filtruje na "Leden", měra se vyhodnotí pro Leden aktuálního roku a SAMEPERIODLASTYEAR vrátí Leden minulého roku.
Running Totals (YTD, QTD, MTD)
Running total (kumulativní součet) ukazuje součet od začátku období po aktuální datum.
Year-to-Date (YTD)
Revenue YTD = TOTALYTD( SUM(FactInternetSales[SalesAmount]), DimDate[Date] )
V kontingenční tabulce s měsíci:
- Leden: součet Leden
- Únor: součet Leden + Únor
- Březen: součet Leden + Únor + Březen
- atd.
Quarter-to-Date (QTD) a Month-to-Date (MTD)
Revenue QTD = TOTALQTD( SUM(FactInternetSales[SalesAmount]), DimDate[Date] ) Revenue MTD = TOTALMTD( SUM(FactInternetSales[SalesAmount]), DimDate[Date] )
CALCULATE + DATESYTD — alternativní YTD
TOTALYTD a CALCULATE + DATESYTD jsou ekvivalentní. Zajímavé je použít DATESYTD s jiným agregátem než SUM — například AVERAGE:
-- Internet YTD: PRŮMĚRNÁ hodnota transakce od začátku roku -- Kombinace AVERAGE + DATESYTD — TOTALYTD by SUM nestačil # Internet YTD = CALCULATE( --[# Internet] --'FactInternetSales'[SalesAmount] AVERAGE('FactInternetSales'[SalesAmount]) ,DATESYTD('DimDate'[Date]) )
CALCULATE([míra], DATESYTD(...)). Pokud potřebujete jiný agregát (AVERAGE, MAX, COUNTROWS), použijte CALCULATE + DATESYTD přímo. Zakomentované řádky ukazují, jak lze v míře snadno přepínat mezi různými agregáty.Calculation Groups — Time Calculations
Calculation Group je speciální tabulka, která aplikuje výpočet na libovolnou míru v modelu. Místo psaní zvláštních měr (# Internet LY, # Reseller LY, # Internet YTD, # Reseller YTD...) napíšete výpočet jednou a použijete ho na všechny míry.
Calculation Group Time Calculations z kurzu
-- Calculation Item: Actual — vrátí míru beze změny SELECTEDMEASURE() -- Calculation Item: LY — stejné období loni CALCULATE( SELECTEDMEASURE() ,SAMEPERIODLASTYEAR('DimDate'[Date]) ) -- Calculation Item: VLY (Variance vs Last Year) VAR _ty = SELECTEDMEASURE() VAR _ly = CALCULATE( SELECTEDMEASURE() ,SAMEPERIODLASTYEAR('DimDate'[Date]) ) VAR _rslt = IF(ISBLANK(_ty) || ISBLANK(_ly), BLANK(), _ty - _ly) RETURN _rslt -- Calculation Item: VLY% — formatStringDefinition = "0.0%" VAR _ty = SELECTEDMEASURE() VAR _ly = CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('DimDate'[Date])) VAR _vly = IF(ISBLANK(_ty) || ISBLANK(_ly), BLANK(), _ty - _ly) RETURN DIVIDE(_vly, _ly)
Time Calculations do osy nebo sloupců matice. Volba "LY" automaticky aplikuje CALCULATE + SAMEPERIODLASTYEAR na všechny míry ve vizuálu — # Internet LY, # Reseller LY i # TGT LY najednou, bez psaní samostatných měr.Časté chyby a jak se jim vyhnout
Chyba 1: Date Table není označena
Příznaky: Time Intelligence funkce vrací chybu nebo špatné výsledky.
Řešení: Vždy označte Date Table v modelu. Jděte do Modeling > Mark as date table.
Chyba 2: Mezery v datech v Date Table
Příznaky: Výsledky DATEADD nebo YoY jsou nesprávné (chybí data).
Řešení: Zajistěte, aby Date Table obsahovala všechny dny bez mezer. Pokud máte data pouze pro pracovní dny, musíte stále mít všechny dny v tabulce.
Chyba 3: Problém s filtrováním v DATEADD
Příznaky: DATEADD vrací "prázdné" výsledky.
Řešení: Ujistěte se, že Date Table má správný vztah (1:N) s faktickou tabulkou a že je aktivní.
Chyba 4: Použití DateKey (integer) místo Date sloupce
Problém: TI funkce (SAMEPERIODLASTYEAR, TOTALYTD, DATEADD apod.) vyžadují sloupec datového typu Date/DateTime. Pokud jim předáte DimDate[DateKey] (integer YYYYMMDD), výsledek bude chybný nebo míra vrátí prázdno.
-- CHYBNĚ — DateKey je integer, ne datum: TOTALYTD(SUM(...), DimDate[DateKey]) -- ❌ nefunguje CALCULATE(..., SAMEPERIODLASTYEAR(DimDate[DateKey])) -- ❌ nefunguje -- SPRÁVNĚ — Date je sloupec datového typu DateTime: TOTALYTD(SUM(...), DimDate[Date]) -- ✓ CALCULATE(..., SAMEPERIODLASTYEAR(DimDate[Date])) -- ✓
Řešení: Pro TI funkce vždy používejte sloupec typu Date/DateTime (v kurzu: DimDate[Date] resp. Kalendar[Date]). Sloupec DateKey (číslo) slouží jako primární klíč pro relace, ne jako argument TI funkcí.
Doporučení 5: preferujte TI funkce před ručním CALCULATE
Ruční přístup pomocí CALCULATE funguje, ale TI funkce jsou robustnější:
-- Méně robustní (funguje, ale neošetřuje edge cases automaticky): CALCULATE(SUM(...), DimDate[Year] = YEAR(TODAY()) - 1) -- Doporučeno — TI funkce ošetřují meziroční přechody a fiskální rok: CALCULATE(SUM(...), PREVIOUSYEAR(...) )
Důvod: TI funkce automaticky ošetřují edge cases (meziroční přechody, přestupné roky, fiskální rok). Ruční filtr přes sloupec Year funguje pro jednoduché scénáře, ale při složitějších požadavcích se rychle zkomplikuje.