Running Total L’argomento è risolto

Il software per la BI di Microsoft, leader nel mercato
Rispondi
Avatar utente

Mohican1989
Messaggi: 8 | Topic creati
Iscritto il: ven 10 lug 2020, 16:40
Ringraziato: 1 volta
Contatta:

Running Total

Messaggio da Mohican1989 »

Buonasera (notte ?)
Allego file identico a quello della volta scorsa, ho solo aggiunto la lookup table "Calendario"
edit:sostituito file tramite allegandolo attraverso il forum.

Il problema principale è sempre lo stesso, come penso l'80% che immagino si hanno su DAX, il contesto o meglio capirlo.

L'esercizio come avrete già capito è visualizzare, per ogni giorno del mese di Gennaio 2015, il totale venduto e al suo fianco, il totale venduto incrementale.

La Sales Amount measure è una semplice

Codice: Seleziona tutto

Sales Amount := Sumx(Sales,Sales[Quantità]*Related(ADV_Products_LUT[Prodotto_Prezzo]))
la Running Total Sales Amount è :

Codice: Seleziona tutto

Running Total Sales Amount := CALCULATE([SALES_AMOUNT],filter(all(Calendario[Date]),Calendario[Date]<=max(Calendario[Date])))
Il mio dubbio è la parte "Calendario[Date]<=max(Calendario[Date])".

Parto dal mio ragionamento:
1- Devo fare la stessa operazione di somma di Sales Amount ma devo far si che la misura avvenga con un filtro diverso, attualmente è il context filter determinato dalla mia Calendario[Date] inserita come riga di matrice.
2- Sappiamo che per "cambiare il filtro" devo utilizzare CALCULATE.
3- So che devo far in modo che il mio context filter non venga applicato, altrimenti la mia tabella viene filtrata per ogni data del filter context (01/01/2015, 02/01/2015 e cosi via) è sarei nella situazione del punto 1, aggiungo quindi come secondo argomento della CALCULATE la funzione FILTER.Come si elimina un context filter ? Mettendo una ALL() intorno alla tabella da filtrare.
4- Ora teoricamente, se non uso misure, sono nel "mondo normale", non ho al momento nessuno tipo di filtro applicato.
5- Devo definire l' espressione per filtrare la tabella Calendario[Date] (quindi più la colonna che la tabella) e qui mi confondo.
Voglio solo le righe che soddisfano la seguente espressione-> la data della riga è <= alla massima data della colonna.
Per quanto detto nel mio altro post, "max(Calendario[Date])" secondo il mio ragionamento dovrebbe essere "31/12/2017" in quanto non ho nessun contesto che interferisce con questa funzione,ne quello di filtro (eliminato tramite ALL()) ne row context, si esiste il row context di filter ma la mia funzione max() non è affetta da questo contesto e la mia funzione non è una measure quindi non avviene alcun "context transition".

Ho provato anche a ragionare "per assurdo", mettiamo quindi che la mia funzione max() venga influenzata dal filter context, prendiamo per esempio la riga della data "02/01/2015". La mia Calendario[Date] avrebbe solo la riga con quella data ed il massimo di 1 data è solo quella data.
Ma allora perché usare max ? Se tolgo max dovrebbe essere la stessa ... e invece no.

Scusate... sarò il vostro incubo :mrgreen:
Allegati
RunningTotal.pbix
Esempio Running Total
(957.31 KiB) Scaricato 45 volte
Ultima modifica di Mohican1989 il gio 23 lug 2020, 12:16, modificato 2 volte in totale.


Avatar utente

Andrea90
Messaggi: 2255 | Topic creati
Iscritto il: dom 28 giu 2020, 19:41
Luogo: Bologna
Ringraziato: 672 volte
Contatta:

Running Total

Messaggio da Andrea90 »

Buongiorno Mohican1989 :wave: ,

La stessa espressione che hai postato potrebbe essere scritta nel seguente modo (utilizzando le variabili così da meglio esplicitare ogni passaggio della formula):

Codice: Seleziona tutto

Running Total Sales Amount =
VAR DataCorrente =
    MAX ( Calendario[Date] )
VAR Tabella =
    FILTER ( ALL ( Calendario[Date] ), Calendario[Date] <= DataCorrente )
VAR Somma =
    CALCULATE ( [SALES_AMOUNT], Tabella )
RETURN
    Somma
Come puoi vedere utilizzando una variabile il valore della "DataCorrente" e cioè quella che definisce il limite massimo del tuo range viene definita di volta in volta, prendendo come valore il valore MAX del campo date. Questo perché quando andrà a valorizzare la variabile prenderà come contesto filtro quello attualmente presente nella riga del report. Ricordando che non è possibile richiamare il solo nome del campo al di fuori di un contesto riga (che qui, nel calcolo della VAR, non è presente) allora necessitiamo di un aggregatore che sia in grado di riportarmi il valore della data attualmente presente sulla riga del report (contesto filtro). (per assurdo avrei potuto anche scrivere SUM() o MIN() ed il risultato non sarebbe cambiato poiché comunque vi è solo un valore data all'interno della tabella calendario, non ci possono essere due righe con la stessa data).

Ora la variabile salva il valore della data corrente ogni volta che considererà una riga diversa del report. Questo valore verrà utilizzato come parametro di confronto all'interno di Filter, andando così a riportare una lista di date valide (cioè di date che rispondono alla condizione filtrante).
Perchè questo? perchè FILTER filtra una tabella che di norma sarebbe affetta dal contesto filtro corrente (singola data), ma utilizzando ALL() rendi la tabella "da filtrare" indifferente da tale contesto.

Questa lista di date verrà utilizzata all'interno di CALCULATE in modo tale da modificare il contesto filtro attuale (ogni riga del report una sola data) così da effettuare il calcolo di [SALES AMOUNT] su una tabella filtrata per le sole date valide.

Spero di aver meglio chiarito la logica del calcolo di questa running total.

Nel caso siamo qui se servono ulteriori approfondimenti.

A presto,

Andrea
Se hai gradito l'aiuto che hai ricevuto considera di contribuire alle spese per il mantenimento del forum facendo una libera DONAZIONE --> Link

Ricordarsi di segnare come "RISOLTE" le discussioni per le quali si è ricevuto un feedback positivo. Per vedere come fare --> Link
Avatar utente

Enrico Galli
Messaggi: 890 | Topic creati
Iscritto il: dom 28 giu 2020, 19:03
Luogo: San Giovanni in Persiceto (BO)
Ringraziato: 325 volte
Contatta:

Running Total

Messaggio da Enrico Galli »

Ciao Simone, innanzi tutto nessun incubo :o :lol: anzi ti ringraziamo per questi topic che ci permettono di spiegare quelli che sono i concetti alla base del DAX. Ricordi il mantra? :mrgreen:
Il row context itera, il filter context filtra... il filter context non itera, il row context non filtra...
Mi affianco quindi alla (correttissima) spiegazione di Andrea90 analizzando la formula che tu hai tentato di scomporre con il tuo ragionamento:

Codice: Seleziona tutto

CALCULATE(
	[SALES_AMOUNT],
	FILTER(
		ALL(Calendario[Date]),
		Calendario[Date]<=MAX(Calendario[Date])
	)
)
Tu scrivi:
Per quanto detto nel mio altro post, "max(Calendario[Date])" secondo il mio ragionamento dovrebbe essere "31/12/2017" in quanto non ho nessun contesto che interferisce con questa funzione,ne quello di filtro (eliminato tramite ALL()) ne row context, si esiste il row context di filter ma la mia funzione max() non è affetta da questo contesto e la mia funzione non è una measure quindi non avviene alcun "context transition".
La parte evidenziata in rosso è quella su cui voglio farti concentrare. Quando tu entri nella funzione FILTER, come giustamente osservi attivi un Row context. Il primo argomento è la tabella. Che tabella voglio filtrare? Se metto solo "Calendario", questa sarà la porzione di calendario visibile nel filter context (ovvero: un solo giorno se lavoro con un report di dettaglio giornaliero).
Quindi faccio intervenire ALL(), che mi ignora (e NON elimina!) il filtro sulla colonna Date, permettendo a FILTER di iterare su tutta la lookup calendario.
Attenzione: Il filter context non è stato modificato in alcun modo, ed è ancora quello che era all'inizio del calcolo! Ma grazie a ALL(), adesso FILTER può iterare su tutta la colonna Date, e per ciascuna riga valutare l'espressione. Qual è l'espressione? Calendario[Date]<=MAX(Calendario[Date]). MAX è un aggregatore, quindi lavora sul filter context che, lo ribadisco, non è mai stato modificato. Pertanto, il risultato della funzione MAX (che, come dice Andrea, potrebbe essere scritta anche con SUM o MIN) è sempre l'unica data visibile nel filter context.

Il risultato finale pertanto sarà l'intera colonna Date, filtrata per le sole righe che hanno una data minore o uguale alla data in riga. Questo elenco di date viene passato a CALCULATE, che a questo punto sì: MODIFICA il filter context, ed esegue il calcolo non sul filter context originale, ma su questo nuovo filter context, formato dalle sole date filtrate come sopra. E produce il running total.

Spero di averti aiutato a capire un po' meglio, anche se all'inizio è richiesto uno sforzo di astrazione notevole: me lo ricordo bene :lol: :wave:

P.S: come mai non hai allegato il file direttamente attraverso il forum? Le dimensioni dovrebbero consentirlo, e quando ho provato l'ultima volta il tuo link mi dava errore
Enrico Galli
Link utili: I nostri tutorial | Come inserire: Immagini - Codice - Risolto
Se il forum ti è stato utile, considera di supportarlo con una libera donazione
Avatar utente

Autore del topic
Mohican1989
Messaggi: 8 | Topic creati
Iscritto il: ven 10 lug 2020, 16:40
Ringraziato: 1 volta
Contatta:

Running Total

Messaggio da Mohican1989 »

Innanzitutto ho ricaricato il file tramite il forum (avevo completamente ignorato il tab Allegati). L' altro probabilmente avendolo caricato su una piattaforma ha una scadenza.

Tornando al mio problema .... che n00b :oops: .

Leggendo mi accorgo di aver avuto fin ad ora un grave misunderstanding.

Il commento di Andrea90 ha iniziato a farmi pensare.
Ha fatto un esempio con i VAR, che per quel poco di assunto, è valutata a meno di utilizzo di funzioni che modificano il contesto, proprio seguendo il filter context attivo. Lo fa proprio con la "mia" Date e mi dico: "non ha capito, perché mi sta facendo vedere un esempio in cui la data sarà palesemente affetta dal filter context quando ho specificato nella mio ragionamento che MAX(Calendario[Date]) non è affetta da alcun contesto ?".

Iniziano quindi a vacillare le mie convinzioni, forse sono proprio io che non ho capito e Andrea da per scontato che sappia che è così.
Infine, Enrico fuga ogni dubbio evidenziando proprio questa mia errata assunzione.
Nel mio altro topic ("Formule Dax - Filter Context / Row Context - Problema con esempio") usavo una formula sintatticamente simile e mi avete avvertito che la mia soluzione sarebbe andata bene fintanto che non vi fosse stato alcun filter context ad inficiare il calcolo della media.
Mi pareva corretto, la mia formula funzionava perché non essendoci relazioni tra le tabelle il filter context "non funziona".
Pensavo però, sempre erroneamente, che nel caso fosse attiva una relazione che triggerava il filter context, con Filter(ALL(tabella)) l'avrei ignorato PER OGNI funzione utilizzata all' interno della funzione filter, non solo dalla tabella da filtrare (non so perché ma ero convinto di ciò).

Spero fosse solo questa errata assunzione a bloccarmi tutto il ragionamento ma mi pare sia così.

Grazie ancora per la celerità e per le risposte sempre molto esaustive e dettagliate.
Avatar utente

Enrico Galli
Messaggi: 890 | Topic creati
Iscritto il: dom 28 giu 2020, 19:03
Luogo: San Giovanni in Persiceto (BO)
Ringraziato: 325 volte
Contatta:

Running Total

Messaggio da Enrico Galli »

Mohican1989 ha scritto: gio 23 lug 2020, 13:14 Pensavo però, sempre erroneamente, che nel caso fosse attiva una relazione che triggerava il filter context, con Filter(ALL(tabella)) l'avrei ignorato PER OGNI funzione utilizzata all' interno della funzione filter, non solo dalla tabella da filtrare (non so perché ma ero convinto di ciò).

Spero fosse solo questa errata assunzione a bloccarmi tutto il ragionamento ma mi pare sia così.
E' esattamente così. E a costo di essere tremendamente noioso e ripetitivo... FILTER è un iteratore; iteratore -> row context. E "il row context itera, il row context NON filtra...".

Come forse avrai già letto da qualche parte, ALL() è una funzione ambivalente: può essere usata come table function o come modificatore di CALCULATE. In questo caso è una table function, e infatti quello che fa è restituire una tabella, ovvero l'intera tabella in argomento (o una o più colonne di essa, se specificate). Su questa tabella, FILTER va a iterare riga per riga.
Se usata come modificatore di CALCULATE, invece, non restituisce una tabella, bensì rimuove ogni filtro presente su quella tabella (o colonna), tant'è vero che da qualche mese esiste anche l'alias REMOVEFILTERS, che è equivalente a ALL ma può essere usato solo come modificatore di CALCULATE.

Ma non voglio mettere troppa carne al fuoco in un solo messaggio: mi fa piacere che tu abbia capito di più e che le nostre spiegazioni ti siano state utili. Alla prossima! :wave:
Enrico Galli
Link utili: I nostri tutorial | Come inserire: Immagini - Codice - Risolto
Se il forum ti è stato utile, considera di supportarlo con una libera donazione
Avatar utente

Excel8020
Messaggi: 2 | Topic creati
Iscritto il: mer 15 lug 2020, 10:08
Ringraziato: 3 volte
Contatta:

Running Total

Messaggio da Excel8020 »

Complimenti per le spiegazioni, sempre molto dettagliate :clap:

Solo i miei two cents:

la formula dei Running Totals, per quanto di semplice editing, richiede un livello di astrazione che non sempre, ovviamente si possiede quando ci si avvicina a questo mondo.
Chi insegna il DAX si trova davanti ad un problema: di solito la misura serve per procedere con il programma, ma contemporaneamente, per ovvi motivi, i vari concetti espressi nei post devono ancora sedimentare...allora si cerca di darne una spiegazione ovviamente non del tutto esaustiva, rimandandone ad una trattazione più dettagliata nel momento in cui si accenderà la lampadina, il che per la mia esperienza, succede relativamente presto! :thumbup:

In ogni caso questa è una misura tra le più maligne ma anche più utili nel campo della Data Analysis, è può essere applicata laddove ci sia e sia utilizzabile un qualsiasi criterio di sorting... nel database in questione anche per calcolare ad esempio il Running Totals del fatturato dei clienti indipendentemente dalla data, o dei prodotti, in quanto il meccanismo di sorting sarà il relativo ID...molto utile es. nella Paretiana.

Ma, indipendentemente da questo, giusto due commenti sulla seconda parte della misura, quando si usa il MAX(). Se io considero il risultato riga per riga posso usare, come si diceva, anche altri aggregatori es: MIN(), SUM(), o SELECTEDVALUE(), in quanto l'unico obiettivo di questa parte della misura è "fissare" l'elemento corrente, ma il TOTALE cambia in quanto, come sappiamo, il meccanismo di calcolo è differente:

Se uso MAX() per il TOTALE verrà riportato il valore relativo all'elemento di riga che ha il max valore, che è appunto l'RT che mi aspetto
Se uso MIN() per il TOTALE verrà riportato il valore relativo all'elemento di riga che ha il min valore, che NON è l'RT che mi aspetto
Se uso SUM() ricado nel caso MAX() e ho l'RT che mi aspetto
Se uso SELECTEDVALUE() non verrà riportato nulla (sono in asssenza di evaluation context, sostanzialmente).

Questo spiega perchè alcuni usano MIN o SELECTEDVALUE ma poi, non vedendo nel totale il risultato atteso...panic mode...

Quindi il mio suggerimento è quello di utilizzare, salvo giustificate esigenze, MAX(), ed è questo il motivo per cui in letteratura si troverà quasi sempre MAX()...anche se è pur vero che nel calcolo dell'RT non siamo in genere interessati al totale, basta sapere che il panic mode può essere evitato :)

Un saluto,

Massimo
Avatar utente

Enrico Galli
Messaggi: 890 | Topic creati
Iscritto il: dom 28 giu 2020, 19:03
Luogo: San Giovanni in Persiceto (BO)
Ringraziato: 325 volte
Contatta:

Running Total

Messaggio da Enrico Galli »

Grazie Excel8020 per le (giustissime) osservazioni: per totali e subtotali in effetti solo MAX fornisce i risultati corretti. Le altre formule sono state ipotizzate al solo scopo di dimostrare che, sulla riga del singolo giorno, il filter context è limitato esclusivamente a quel giorno. Ma la tua precisazione è molto opportuna e ti ringrazio per l'attenzione e i complimenti :wave:
Enrico Galli
Link utili: I nostri tutorial | Come inserire: Immagini - Codice - Risolto
Se il forum ti è stato utile, considera di supportarlo con una libera donazione
Avatar utente

Autore del topic
Mohican1989
Messaggi: 8 | Topic creati
Iscritto il: ven 10 lug 2020, 16:40
Ringraziato: 1 volta
Contatta:

Running Total

Messaggio da Mohican1989 »

Ciao Excel8020 , felice di trovarti anche qui.

Per capire bene, Max e Min funzionano indistintamente fintanto che, ad esempio, utilizzo nel campo Rows di una matrice le date non gerarchiche, ad esempio di gennaio 2015.
Invece se inserisco le date come gerarchia e mi tengo Year e Month ed espando al massimo livello la gerarchia, avrò ad esempio Gennaio come filter context quindi non avrò più 1 riga alla volta ma 31 righe dal 01/01/2015 al 31/01/2015. In questo caso ovviamente Max ritorna 31/01/2015 per gennaio, 28/02/2015 per febbraio e così via restituendo l RT desiderato. Usando MIN questa volta non funziona perché ritornerebbe non più le date indicate precedentemente bensi 01/01/2015, 01/02/2015 e così via. SelectedValue senza averlo verificato posso immaginare il perché non dia il risultato attesso per RT (ci sono + di 1 value selezionata e quindi non viene elaborato alcun risultato). Ho provato invece con SUM ma non capisco come possa essere ricondotto a MAX e quindi funzionare ...non posso sommare le date tra di loro, se provo a sostituire MAX con SUM il running total si fissa per tutti i mesi al TOTALE finale. Sto sbagliando qualcosa?
Rispondi