Filtrare i dati con Envision - Software di ottimizzazione delle scorte

Filtrare i dati con Envision












Home » Risorse » Qui

Filtrare i dati è un'operazione fondamentale per le analisi commerciali. Ed Envision ti offre il supporto necessario per farlo. Puoi filtrare sia grandi blocchi di script che istruzioni singole, con l'aiuto delle condizioni where (dove) e when (quando). In questa pagina spieghiamo come fare.


Lo script: un esempio

Come al solito, partiamo dal nostro campione dati, che dovrebbe essere accessibile dal percorso /sample nel tuo account Lokad. Lo script qui sotto è mediamente complesso e illustra alcune delle opzioni di filtro disponibili in Envision. Se non l'hai già fatto, ti consigliamo di leggere la pagina Eseguire calcoli con Envision prima di passare al tutorial in questa pagina.
read "/sample/Lokad_Items.tsv"
read "/sample/Lokad_Orders.tsv" as Orders
show label "Filtering and Aggregating with Envision" a1f1 tomato
oend := max(Orders.Date)
when date > oend - 365
  LastYearQty = sum(Orders.Quantity)
  where StockOnHand + StockOnOrder > LastYearQty
    show table "Overstocked items, +1 year of stock" a2f3 tomato with 
      Id
      Name
      StockOnHand + StockOnOrder as "Stock"
      LastYearQty

  where Orders.NetAmount > 1000
    show table "Large transactions over $1000" a4f5 tomato with 
      Id
      Name 
      Orders.Date 
      Orders.Quantity
      Orders.NetAmount
      Orders.Client

when date >= monday(oend) - 52 * 7 & date < monday(oend)
  Week.sold := sum(Orders.NetAmount)
  show linechart "Sold by week{$}" a6f7 tomato with Week.sold
Copia e incolla lo script qui sopra nel tuo account Lokad, poi esegui lo script. Dovresti visualizzare un pannello di controllo come quello qui sotto.

Image

Filtrare blocchi di script

Quando si comincia a lavorare su dati storici relativamente recenti, non più vecchi di un anno, i calcoli da eseguire sono spesso molto numerosi. In questi casi, il filtro più adatto da applicare a tutti i calcoli è solo scorso anno. Per questi motivi, con Envision i dati vengono filtrati soprattutto per blocchi di script. Il frammento di script qui sotto illustra due blocchi filtrati annidati: il primo blocco inizia alla riga 1 con la condizione when, il secondo blocco inizia alla riga 3 con una condizione where.
when date > oend - 365
  LastYearQty = sum(Orders.Quantity)
  where StockOnHand + StockOnOrder > LastYearQty
    show table "Overstocked items, +1 year of stock" a2f3 tomato with
      Id
      Name
      StockOnHand + StockOnOrder as "Stock"
      LastYearQty
Envision è un linguaggio sensibile agli spazi bianchi lasciati all'inizio di ogni riga. Chiamiamo blocco di codice una sequenza di righe che iniziano con lo stesso numero di spazi. I blocchi possono anche essere annidati, ossia trovarsi l'uno all'interno dell'altro.

Se è la prima volta che ti trovi di fronte a un meccanismo simile, forse potresti essere disorientato. Non preoccuparti, però, perché l'editor di script Envision ti offre tutto il supporto necessario: se clicchi su “Invio” alla fine di una riga di script che contiene una condizione filtro, la riga successiva conterrà già un rientro automatico di due spazi.

Nota per gli sviluppatori: Envision adotta un sistema di spaziatura simile a quello di Python, che ben si adatta a una sintassi concisa per la gestione dei dati, come appunto quella di Envision. Data l'assenza di loop e salti, il rientro raramente si spinge oltre i 3 livelli, rendendo la spaziatura molto semplice da gestire.

Un blocco filtrato inizia con una condizione (che vedremo nel dettaglio più avanti) che può essere vera o falsa per ogni riga di ogni tabella. All'interno del blocco sono conservate soltanto le righe per cui la condizione è vera, mentre tutte le altre vengono filtrate. Come suggerisce il nome, la condizione when implica un filtro temporale e può essere applicata a tutte le tabelle indicizzate con una colonna Date (data). La condizione where, invece, è usata solitamente per filtrare gli articoli ed è applicabile a tutte le tabelle indicizzate con una colonna Id (identificativo dell'articolo).

Una volta iniziato il blocco di codice, vengono conservate solo le righe della tabella che soddisfano la condizione posta dal filtro. Tutto il resto rimane uguale. Rimane anche la possibilità di spostare all'interno del blocco qualsiasi elemento scritto all'esterno del blocco. Più precisamente, è possibile definire un altro blocco filtrato all'interno del primo blocco, secondo la tecnica dell'annidamento (nesting in inglese). Nell'esempio sopra, il blocco where, che inizia alla riga 3, è annidato all'interno del blocco when, che inizia alla riga 1.

Creare una condizione

Una condizione è un'espressione che può essere valutata come vera o falsa. Per filtrare i dati bisogna prima di tutto creare delle condizioni da valutare con i dati di input ed eventualmente con i risultati dei calcoli intermedi. Envision ti dà la possibilità di creare condizioni complesse, come illustra lo script qui sotto:
when date >= monday(oend) - 52 * 7 & date < monday(oend)
  Week.sold := sum(Orders.NetAmount)
  show linechart "Sold by week{$}" a6f7 tomato with Week.sold
In questo snippet, l'operatore and (e) indica che entrambe le espressioni, sia quella a sinistra che quella a destra dell'operatore, devono essere vere. Envision supporta gli operatori logici and, or (o) e not (non), oltre agli operatori numerici == (uguaglianza), != (diseguaglianza), <= (minore o uguale), >= (maggiore o uguale), < (minore), > (maggiore). Nello snippet qui sotto vediamo come gli operatori possono essere combinati e valutati.
a := 1 > 10 // falso
b := not a // vero
c := a | b // vero
d := a & b // falso
e := 10 >= 3 | 5 > 7 // vero
show table "Conditions" with a, b, c, d, e
Quando un blocco filtrato è annidato all'interno di un altro blocco, è come se le due condizioni fossero una a destra e l'altra a sinistra dell'operatore and.

Nell'esempio sopra, avrai forse notato la sintassi monday(oend) (lunedì (fine)): si tratta di una chiamata della funzione monday (lunedì). Per ogni data, questa funzione torna all'ultimo lunedì prima della data indicata come argomento (inclusa). Quindi, se come argomento abbiamo un lunedì, la funzione rimanderà alla stessa data.

La funzione monday() è usata per definire una finestra temporale composta da settimane intere, invece che parziali. Infatti, quando aggreghiamo i dati su base settimanale, dobbiamo considerare solo le settimane intere, altrimenti corriamo il rischio che la prima e l'ultima data corrispondano a valori estremamente bassi, considerando che riflettono una settimana parziale.

Filtrare le tabelle per data

Envision offre il supporto necessario per filtrare le tabelle in base a condizioni temporali. Lo script qui sopra filtra tutte le righe che risalgono a più di 1 anno prima. Vediamo meglio la parte di script corrispondente.
oend := max(Orders.Date)
when date > oend - 365
  LastYearQty = sum(Orders.Quantity)
Envision tratta le date come un numero intero di giorni a partire dal 1° gennaio 2001. Questo meccanismo offre numerosi vantaggi, tra cui la possibilità di eseguire operazioni aritmetiche con le date: ad esempio, se sottraiamo 7 da una data avremo la settimana precedente, o se sottraiamo 365 avremo una data (approssimativa) dell'anno precedente.

La parola chiave date ha un comportamento particolare: implicitamente, la variabile date si riferisce a tutte le tabelle che contengono una colonna Date. Poiché in questo caso abbiamo una sola tabella (Orders, la tabella degli ordini), è come se avessimo scritto Orders.Date > oend - 365. La sintassi date non solo è più concisa, ma considera tutte le tabelle insieme. Se, invece di cercare le quantità vendute, avessimo voluto visualizzare le quantità acquistate, allora avremmo dovuto scrivere:
read "/sample/Lokad_PurchaseOrders.tsv" as PO // caricamento degli ordini di acquisto

// tagliato

when date > oend - 365
  LastYearQty = sum(PO.Quantity)

Filtrare gli articoli

Oltre che filtrare le righe per data, Envision offre anche la possibilità di filtrare gli articoli, rispondendo così all'esigenza di escludere prodotti, posizioni, categorie, etc. Lo script all'inizio di questa pagina illustra come restringere il campo agli articoli che possiamo definire come inventario morto. Ripetiamo qui sotto le righe di script corrispondenti.
  where StockOnHand + StockOnOrder > LastYearQty
    show table "Overstocked items, +1 year of stock" a2f3 tomato with
      Id
      Name
      StockOnHand + StockOnOrder as "Stock"
      LastYearQty
La parola chiave where è seguita da una condizione, applicata a tre vettori che appartengono, implicitamente, alla tabella Items (articoli): questa è infatti l'unica tabella per cui possiamo evitare di inserire un prefisso con il nome della tabella nel nome della variabile (quando ci riferiamo alle righe della tabella Orders, infatti, dobbiamo usare il nome Orders.NetAmount, non possiamo limitarci a un semplice NetAmount). La condizione nello script qui sopra è quella di includere solo gli articoli per cui la somma delle scorte disponibili e delle scorte già ordinate è superiore al numero di unità vendute nell'ultimo anno.

Una volta definita una condizione per la tabella Items, ogni tabella che include una colonna Id (identificativo dell'articolo, secondo le convenzioni di Envision) viene filtrata allo stesso modo, secondo un procedimento identico a quello che abbiamo visto precedentemente per il filtro temporale. Non appena inizierai a filtrare gli articoli, infatti, non avrà molto senso farti visualizzare tutti gli ordini di acquisto o di vendita, compresi quelli corrispondenti agli articoli che hai scartato con il filtro.

Filtrare gli articoli significa anche modificare il campo d'azione dei vettori appena calcolati. Vediamo perché, con l'aiuto dello script qui sotto.
where StockOnHand > 5
  GreaterThanFive = "yes"
  show table "Hello" with Name, GreaterThanFive // GIUSTO!
// il blocco filtro termina qui
show table "Hello" with Name, GreaterThanFive // SBAGLIATO!
La riga 5 non è corretta, poiché il vettore GreaterThanFive (maggiore di cinque) è stato definito solo per le righe per cui è vera la condizione StockOnHand > 5 (scorte disponibili superiori a 5). Così, se il vettore è definito correttamente all'interno del blocco (e quindi può essere usato come nella riga 3), lo stesso vettore non può essere usato al di fuori del blocco filtrato, perché altrimenti alcuni valori non verrebbero definiti. Possiamo ovviare al problema assicurandoci che il vettore sia definito correttamente per tutti i valori degli articoli, come nell'esempio qui sotto.
GreaterThanFive = "no"
where StockOnHand > 5
  GreaterThanFive = "yes"
  show table "Hello" with Name, GreaterThanFive // GIUSTO!
// il blocco filtro termina qui
show table "Hello" with Name, GreaterThanFive // GIUSTO!
Questo snippet inizia, alla riga 1, con una definizione corretta del vettore GreaterThanFive per tutti gli articoli. La definizione viene modificata alla riga 3 per un sottoinsieme di articoli. Questa modifica, tuttavia, non cambia il fatto che il vettore GreaterThanFive sia definito esplicitamente per tutti gli articoli. Di conseguenza, il contenuto della riga 6 è ora corretto.

Filtrare una tabella specifica

Filtrare i dati per articolo o per data è molto utile. A volte, però, capita di dover filtrare una specifica tabella. Con Envision, possiamo filtrare una tabella con la parola chiave where, come nelle righe di script qui sotto.
  where Orders.NetAmount > 1000
    show table "Large transactions over $1000" a4f5 tomato with
      Id
      Name
      Orders.Date
      Orders.Quantity
      Orders.NetAmount
      Orders.Client
Nel nostro caso, vogliamo filtrare la tabella Orders per escludere tutte le righe con un valore inferiore a $1000. Le righe non filtrate sono mostrate attraverso show table alle righe 2 e 3. L'esempio mostra come filtrare un'unica tabella, scremando i risultati della tabella Orders, ma lasciando invariate tutte le altre tabelle.

Se un vettore associato alla tabella Orders viene calcolato all'interno di un blocco filtrato, l'accesso a questo vettore sarà ristretto al blocco. Abbiamo già visto un comportamento simile nel filtro per articolo, vediamo ora come applicare il meccanismo anche al filtro per tabella.
where Orders.NetAmount > 1000
  Orders.LargeTxn = "yes"
  show table "Large transactions" with Name, Orders.LargeTxn // GIUSTO!
// ultima riga del blocco
// rientro diminuito, il blocco è terminato
show table "Large transactions" with Name, Orders.LargeTxn // SBAGLIATO!
Poiché il vettore Orders.LargeTxn (ordini, superiore a 1000) non è definito per tutte le righe della tabella Orders, solo la riga 3 risulta corretta, mentre la riga 5 non lo è. Come nell'esempio precedente, possiamo risolvere il problema definendo correttamente un valore LargeTxn per l'intera tabella Orders, come nello script qui sotto.
Orders.LargeTxn = "no"
where Orders.NetAmount > 1000
  Orders.LargeTxn = "yes"
  show table "Large transactions" with Name, Orders.LargeTxn // GIUSTO!
// ultima riga del blocco
// rientro diminuito, il blocco è terminato
show table "Large transactions" with Name, Orders.LargeTxn // GIUSTO!
In linea di massima, Envision cerca di rendere i blocchi il più fluidi possibile: un vettore calcolato all'interno di un blocco può essere usato all'esterno del blocco, a condizione di non violare la regola per cui tutti i valori del vettore devono essere definiti esplicitamente quando il vettore appare a destra di un assegnamento.

Filtrare con gli zuccheri sintattici

Lo schema sintattico usato da Envision per filtrare i dati, basato sul rientro, è conciso e leggibile. Tuttavia, quando sono usati più filtri, il rientro diventa un parametro difficile da seguire. Envision offre allora degli zuccheri sintattici, ossia una sintassi alternativa che richiede un rientro minore. In questa sezione ne analizzeremo in dettaglio il funzionamento.

Rientri e filtri multipli

Envision richiede un solo livello supplementare di rientro per filtro, a patto però che i filtri siano usati separatamente. Se ci interessa solo l'ultimo filtro, allora possiamo usare un solo livello di rientro, come si vede nell'esempio seguente:
// ogni filtro "where" ha il proprio rientro
where Orders.Quantity > 10
  where StockOnHand < 100
    show table "Filtered orders" with Orders.Quantity

// se sono usati più filtri, è necessario un solo rientro
where Orders.Quantity > 10
where StockOnHand < 100 // non c'è rientro!
  show table "Filtered orders" with Orders.Quantity
Il secondo blocco segue la stessa semantica del primo, ma richiede un solo rientro. Più in generale, la sintassi è la seguente:
where A
  when B
    where C
      show table "Filtered by A, B and C" with X
// uguale a
where A
when B
where C
  show table "Filtered by A, B and C" with X

Unire i filtri con la parola chiave and

Abbiamo già visto come, in Envision, l'operatore booleano AND è rappresentato dal simbolo &. Envision offre però anche una parola chiave and, a cui corrisponde una semantica leggermente diversa:
// i due filtri annidati "where"
where Orders.NetAmount > 1000 
  where StockOnHand > 10
    show table "Filtered transactions" with Name, Orders.Quantity

// possono essere riscritti come filtro singolo con la parola chiave "and"
where Orders.NetAmount > 1000 and StockOnHand > 10
  show table "Filtered transactions" with Name, Orders.Quantity
La parola chiave and equivale all'annidare i due filtri where: può essere quindi utilizzata per introdurre più filtri in sequenza, con un solo livello di rientro. Più in generale, avremo:
where A
  where B
   where C
     // ...

// può essere riscritto come
where A and B and C
  // ...
Insomma, la parola chiave and offre la possibilità di unire più filtri che vogliamo utilizzare l'uno in aggiunta all'altro.

Evitare il rientro con la parola chiave keep

Uno schema frequente consiste nell'introdurre i filtri all'inizio dello script per restringere l'analisi dei dati a un campo specifico. La sintassi filtro di Envision gestisce in modo adeguato questi scenari, ma tutto lo script Envision finisce poi con uno o due livelli di rientro. A questo proposito, la parola chiave keep (mantieni) permette di rimuovere tutti i rientri:
// il filtro "where" introduce un blocco rientrato
where Orders.Quantity > 10
  // inizio del blocco
  show table "Inside the filter" with sum(Orders.Quantity)
  // fine del blocco
show table "Outside the filter" with sum(Orders.Quantity)

// se invece usiamo "keep", il filtro è applicato senza rientro
keep where Orders.Quantity > 10
show table "Inside the filter" with sum(Orders.Quantity)
La parola chiave keep deve essere posta prima dei filtri where o when, a indicare che il filtro non presenta un rientro. Il filtro resta attivo per tutto il blocco.
where A
  keep where B
  show table "Filtered by A and B" with X
  // fine dei filtri A e B
show table "Not filtered" with X
Quindi, se keep viene posto in una riga di script senza rientro, il filtro viene applicato fino alla fine dello script.

Filtrare con i suffissi interni alla riga

Finora abbiamo visto come le condizioni fossero scritte in blocchi. Envision offre anche un'altra sintassi, più compatta: i suffissi di condizione. Torniamo al calcolo delle quantità vendute nell'ultimo anno.
when date > oend - 365
  LastYearQty = sum(Orders.Quantity)
Lo script può essere riscritto in questo modo:
LastYearQty = sum(Orders.Quantity) when date > oend - 365
A chi ha familiarità con i database relazionali questa sintassi sembrerà forse simile alle condizioni where di SQL. All'interno di Envision, questa sintassi è prima di tutto uno zucchero sintattico che evita di introdurre blocchi di una sola riga quando l'istruzione da inserire nel blocco è una sola: sia la condizione where che la condizione when, infatti, possono essere “ridotte a un suffisso” da aggiungere all'estrema destra dell'assegnamento.

Gli aggregatori possono essere filtrati all'interno della riga con le condizioni when o where. Envision offre inoltre la possibilità di aggiungere un modificatore else (altro da) alla condizione. Ad esempio, non è corretto scrivere:
oend := max(Orders.Date)
Week.sold := sum(Orders.NetAmount) when date < monday(oend)
show linechart "Sold by week" with Week.sold // SBAGLIATO
perché Week.sold è stato filtrato alla riga precedente e non è quindi definito interamente. Aggiungendo però l'opzione else, definiamo correttamente Week.sold in tutto lo script:
oend := max(Orders.Date)
Week.sold := sum(Orders.NetAmount) when date < monday(oend) else 0
show linechart "Sold by week" with Week.sold // GIUSTO