Ingegneria delle funzionalità con Microsoft Fabric e Dataflow Gen2 |  di Roger Noble |  Aprile 2024

 | Intelligenza-Artificiale

La follia del tessuto, parte 3

Immagine dell'autore e ChatGPT. “Progetta un'illustrazione con un giocatore di basket paralimpico in azione, questa volta il tema è sulle pipeline di dati”. ChatGPT, 4, OpenAI, 15 aprile. 2024. https://chat.openai.com.

Nel messaggio precedenteabbiamo discusso come utilizzare Notebook con PySpark per la progettazione delle funzionalità. Sebbene Spark offra molta flessibilità e potenza, può essere piuttosto complesso e richiedere molto codice per essere avviato. Non tutti si sentono a proprio agio nello scrivere codice o hanno il tempo di imparare un nuovo linguaggio di programmazione, ed è qui che entra in gioco Dataflow Gen2.

Dataflow Gen2 è un motore di trasformazione e integrazione dei dati low-code che consente di creare pipeline di dati per caricare dati da un'ampia varietà di origini in Microsoft Fabric. Si basa su Power Query, che è integrato in molti prodotti Microsoft, come Excel, Power BI e Azure Data Factory. Dataflow Gen2 è un ottimo strumento per creare pipeline di dati senza codice tramite un'interfaccia visiva, semplificando la creazione rapida di pipeline di dati. Se hai già familiarità con Power Query o non hai paura di scrivere codice, puoi anche utilizzare il linguaggio M (“Mashup”) sottostante per creare trasformazioni più complesse.

In questo post spiegheremo come utilizzare Dataflow Gen2 per creare le stesse funzionalità necessarie per addestrare il nostro modello di machine learning. Utilizzeremo lo stesso set di dati del post precedente, che contiene dati sulle partite di basket del college.

Fig. 1 — Il risultato finale. Immagine dell'autore.

Ci sono due set di dati che utilizzeremo per creare le nostre funzionalità: le partite della stagione regolare e le partite dei tornei. Questi due set di dati sono inoltre suddivisi in tornei maschili e femminili, che dovranno essere combinati in un unico set di dati. In totale ci sono quattro file CSV, che devono essere combinati e trasformati in due tabelle separate nella Lakehouse.

Utilizzando Dataflows ci sono diversi modi per risolvere questo problema e in questo post voglio mostrare tre diversi approcci: un approccio senza codice, un approccio a basso codice e infine un approccio più avanzato a tutto codice.

Il primo e più semplice approccio consiste nell'utilizzare l'interfaccia visiva Dataflow Gen2 per caricare i dati e creare le funzionalità.

I dati

I dati che stiamo esaminando provengono dai tornei di basket universitari statunitensi del 2024, ottenuti dalla competizione Kaggle di marzo Machine Learning Mania 2024 in corso, i cui dettagli possono essere trovati Quied è concesso in licenza con CC BY 4.0

Caricamento dei dati

Il primo passo è ottenere i dati da Lakehouse, operazione che può essere eseguita selezionando il pulsante “Ottieni dati” nella barra multifunzione Home e quindi selezionando Di più… dall'elenco delle origini dati.

Fig. 2 — Scelta di una fonte dati. Immagine dell'autore.

Dall'elenco, seleziona Hub dati OneLake per trovare la Lakehouse e poi, una volta selezionata, trovare il file csv nella cartella Files.

Fig. 3 — Seleziona il file csv. Immagine dell'autore.

Ciò creerà una nuova query con quattro passaggi, che sono:

  • Fonte: una funzione che interroga Lakehouse per tutti i contenuti.
  • Navigazione 1: converte il contenuto di Lakehouse in una tabella.
  • Navigazione 2: filtra la tabella per recuperare il file CSV selezionato in base al nome.
  • CSV importato: converte il file binario in una tabella.
Fig. 4 — Carico iniziale. Immagine dell'autore.

Ora che i dati sono caricati possiamo iniziare con una preparazione di base dei dati per inserirli in un formato che possiamo utilizzare per creare le nostre funzionalità. La prima cosa che dobbiamo fare è impostare i nomi delle colonne in modo che siano basati sulla prima riga del set di dati. Questo può essere fatto selezionando l'opzione “Utilizza la prima riga come intestazioni” nel gruppo Trasforma sulla barra multifunzione Home o nella voce di menu Trasforma.

Il passaggio successivo consiste nel rinominare la colonna “WLoc” in “posizione” selezionando la colonna nella vista tabella oppure facendo clic con il pulsante destro del mouse sulla colonna e selezionando “Rinomina”.

La colonna della posizione contiene la posizione della partita, che può essere “H” per casa, “A” per trasferta o “N” per neutrale. Per i nostri scopi, vogliamo convertirlo in un valore numerico, dove “H” è 1, “A” è -1 e “N” è 0, poiché ciò ne renderà più semplice l'utilizzo nel nostro modello. Questo può essere fatto selezionando la colonna e quindi utilizzando il comando Sostituisci valori… trasformare nella voce di menu Trasforma.

Fig. 5 — Sostituisci valori. Immagine dell'autore.

Questo dovrà essere fatto anche per gli altri due valori di posizione.

Infine, dobbiamo modificare il tipo di dati della colonna posizione in un numero intero anziché testo. Questo può essere fatto selezionando la colonna e quindi selezionando il tipo di dati dall'elenco a discesa nel gruppo Trasforma sulla barra multifunzione Home.

Fig. 6 — Caricamento finale dei dati. Immagine dell'autore.

Invece di ripetere il passaggio di ridenominazione per ciascuno dei tipi di posizione, è possibile utilizzare un po' di codice M per sostituire i valori nella colonna posizione. Questa operazione può essere eseguita selezionando la trasformazione precedente nella query (colonne rinominate) e quindi selezionando il pulsante Inserisci passaggio nella barra della formula. Ciò aggiungerà un nuovo passaggio e potrai inserire il seguente codice per sostituire i valori nella colonna della posizione.

Table.ReplaceValue(#"Renamed columns", each (location), each if Text.Contains((location), "H") then "1" else if Text.Contains((location), "A") then "-1" else "0", Replacer.ReplaceText, {"location"})

Aggiunta di funzionalità

Abbiamo caricato i dati, ma non sono ancora adatti al nostro modello. Ogni riga nel set di dati rappresenta una partita tra due squadre e include i punteggi e le statistiche sia della squadra vincente che di quella perdente in un'unica tabella ampia. Dobbiamo creare funzionalità che rappresentino le prestazioni di ciascuna squadra nel gioco e avere una riga per squadra per partita.

Per fare questo dobbiamo dividere i dati in due tabelle, una per la squadra vincente e una per quella perdente. Il modo più semplice per farlo è creare una nuova query per ogni squadra e poi unirle nuovamente alla fine. Ci sono alcuni modi in cui ciò potrebbe essere fatto, tuttavia per mantenere le cose semplici e comprensibili (specialmente se mai dovessimo tornare su questo argomento in seguito), creeremo due riferimenti alla query di origine e poi li aggiungeremo di nuovo insieme, dopo aver fatto alcune leggere trasformazioni.

È possibile fare riferimento a una colonna dal pannello Query a sinistra o selezionando il menu contestuale della query se si utilizza la vista Diagramma. Verrà creata una nuova query che fa riferimento alla query originale e qualsiasi modifica apportata alla query originale si rifletterà nella nuova query. L'ho fatto due volte, una per la squadra vincente e l'altra per quella perdente, quindi ho rinominato le colonne anteponendo rispettivamente il prefisso “T1_” e “T2_”.

Fig. 7 — Suddivisione del set di dati. Immagine dell'autore.

Una volta impostati i valori delle colonne, possiamo quindi combinare nuovamente le due query utilizzando Append Queries e quindi creare la nostra prima funzionalità, ovvero la differenza di punti tra le due squadre. Questo può essere fatto selezionando le colonne T1_Score e T2_Score e quindi selezionando “Sottrai” dal gruppo “Standard” sulla barra multifunzione Aggiungi colonna.

Fatto ciò, possiamo caricare i dati in Lakehouse come una nuova tabella. Il risultato finale dovrebbe assomigliare a questo:

Fig. 8 — Tutti uniti. Immagine dell'autore.

Esistono alcune limitazioni con l'approccio senza codice, la principale è che non è facile riutilizzare query o trasformazioni. Nell'esempio sopra dovremmo ripetere gli stessi passaggi altre tre volte per caricare ciascuno dei singoli file CSV. È qui che il copia/incolla torna utile, ma non è l'ideale. Di seguito esamineremo un approccio a basso codice.

Nell'approccio low code utilizzeremo una combinazione dell'interfaccia visiva e del linguaggio M per caricare e trasformare i dati. Questo approccio è più flessibile dell'approccio senza codice, ma non richiede comunque la scrittura di molto codice.

Caricamento dei dati

L'obiettivo dell'approccio low code è ridurre il numero di query ripetute necessarie e semplificare il riutilizzo delle trasformazioni. Per fare ciò sfrutteremo il fatto che Power Query è un linguaggio funzionale e che possiamo creare funzioni per incapsulare le trasformazioni che vogliamo applicare ai dati. Quando abbiamo caricato per la prima volta i dati da Lakehouse sono stati creati quattro passaggi, il secondo passaggio è stato convertire il contenuto di Lakehouse in una tabella, con ciascuna riga contenente un riferimento a un file CSV binario. Possiamo usarlo come input in una funzione, che caricherà il csv in una nuova tabella, utilizzando la trasformazione della funzione personalizzata Invoke per ogni riga della tabella.

Fig. 9 — Query Lakehouse con i file CSV binari in una colonna denominata Contenuto. Immagine dell'autore.

Per creare la funzione, seleziona “Query vuota” dal menu Ottieni dati, oppure fai clic con il pulsante destro del mouse sul pannello Query e seleziona “Nuova query” > “Query vuota”. Nella nuova finestra della query, inserisci il seguente codice:

(TableContents as binary) =>let
Source = Csv.Document(TableContents, (Delimiter = ",", Columns = 34, QuoteStyle = QuoteStyle.None)),
PromoteHeaders = Table.PromoteHeaders(Source, (PromoteAllScalars = true))
in
PromoteHeaders

Il codice di questa funzione è stato copiato dal nostro approccio iniziale senza codice, ma invece di caricare direttamente il file CSV, accetta un parametro chiamato Contenuto della tabellalo legge come un file CSV Csv.Document e quindi imposta la prima riga dei dati come intestazioni di colonna Table.PromoteHeaders.

Possiamo quindi utilizzare la trasformazione della funzione personalizzata Invoke per applicare questa funzione a ogni riga della query Lakehouse. Questo può essere fatto selezionando la trasformazione “Richiama funzione personalizzata” dalla barra multifunzione Aggiungi colonna e quindi selezionando la funzione che abbiamo appena creato.

Fig. 10 — Richiama la funzione personalizzata. Immagine dell'autore.

Ciò creerà una nuova colonna nella query Lakehouse, con l'intero contenuto del file CSV caricato in una tabella, rappresentata come (Table) nella vista tabella. Possiamo quindi utilizzare la funzione di espansione sull'intestazione della colonna per espandere la tabella in singole colonne.

Fig. 11 — Espandi colonne. Immagine dell'autore.

Il risultato combina efficacemente i due file CSV in un'unica tabella, da cui possiamo poi continuare a creare le nostre funzionalità come prima.

Ci sono ancora alcune limitazioni con questo approccio, anche se abbiamo ridotto il numero di query ripetute, dobbiamo comunque duplicare tutto sia per i set di dati delle partite della stagione regolare che dei tornei. È qui che entra in gioco l'approccio tutto codice.

L'approccio tutto codice è quello più flessibile e potente, ma richiede anche la scrittura della maggior quantità di codice. Questo approccio è più adatto a coloro che hanno dimestichezza con la scrittura del codice e desiderano avere il pieno controllo sulle trasformazioni applicate ai dati.

Essenzialmente ciò che faremo è prendere tutto il codice M generato in ciascuna query e combinarli in un'unica query. Questo ci consentirà di caricare tutti i file csv in un'unica query e quindi applicare le trasformazioni a ciascuno di essi in un unico passaggio. Per ottenere tutto il codice M, possiamo selezionare ciascuna query e quindi fare clic sull'editor avanzato dalla barra multifunzione Home, che visualizza tutto il codice M generato per quella query. Possiamo quindi copiare e incollare questo codice in una nuova query e quindi combinarli tutti insieme.

Per fare ciò, dobbiamo creare una nuova query vuota e quindi inserire il seguente codice:

(TourneyType as text) => let
Source = Lakehouse.Contents(null){(workspaceId = "...")}(Data){(lakehouseId = "...")}(Data),
#"Navigation 1" = Source{(Id = "Files", ItemKind = "Folder")}(Data),
#"Filtered rows" = Table.SelectRows(#"Navigation 1", each Text.Contains((Name), TourneyType)),
#"Invoked custom function" = Table.AddColumn(#"Filtered rows", "Invoked custom function", each LoadCSV((Content))),
#"Removed columns" = Table.RemoveColumns(#"Invoked custom function", {"Content", "Name", "Extension", "Date accessed", "Date modified", "Date created", "Attributes", "Folder Path", "ItemKind", "IsLeaf"}),
#"Expanded Invoked custom function" = Table.ExpandTableColumn(#"Removed columns", "Invoked custom function", {"Season", "DayNum", "WTeamID", "WScore", "LTeamID", "LScore", "WLoc", "NumOT", "WFGM", "WFGA", "WFGM3", "WFGA3", "WFTM", "WFTA", "WOR", "WDR", "WAst", "WTO", "WStl", "WBlk", "WPF", "LFGM", "LFGA", "LFGM3", "LFGA3", "LFTM", "LFTA", "LOR", "LDR", "LAst", "LTO", "LStl", "LBlk", "LPF"}, {"Season", "DayNum", "WTeamID", "WScore", "LTeamID", "LScore", "WLoc", "NumOT", "WFGM", "WFGA", "WFGM3", "WFGA3", "WFTM", "WFTA", "WOR", "WDR", "WAst", "WTO", "WStl", "WBlk", "WPF", "LFGM", "LFGA", "LFGM3", "LFGA3", "LFTM", "LFTA", "LOR", "LDR", "LAst", "LTO", "LStl", "LBlk", "LPF"}),
#"Renamed columns" = Table.RenameColumns(#"Expanded Invoked custom function", {{"WLoc", "location"}}),
Custom = Table.ReplaceValue(#"Renamed columns", each (location), each if Text.Contains((location), "H") then "1" else if Text.Contains((location), "A") then "-1" else "0", Replacer.ReplaceText, {"location"}),
#"Change Types" = Table.TransformColumnTypes(Custom, {{"Season", Int64.Type}, {"DayNum", Int64.Type}, {"WTeamID", Int64.Type}, {"WScore", Int64.Type}, {"LTeamID", Int64.Type}, {"LScore", Int64.Type}, {"location", Int64.Type}, {"NumOT", Int64.Type}, {"WFGM", Int64.Type}, {"WFGA", Int64.Type}, {"WFGM3", Int64.Type}, {"WFGA3", Int64.Type}, {"WFTM", Int64.Type}, {"WFTA", Int64.Type}, {"WOR", Int64.Type}, {"WDR", Int64.Type}, {"WAst", Int64.Type}, {"WTO", Int64.Type}, {"WStl", Int64.Type}, {"WBlk", Int64.Type}, {"WPF", Int64.Type}, {"LFGM", Int64.Type}, {"LFGA", Int64.Type}, {"LFGM3", Int64.Type}, {"LFGA3", Int64.Type}, {"LFTM", Int64.Type}, {"LFTA", Int64.Type}, {"LOR", Int64.Type}, {"LDR", Int64.Type}, {"LAst", Int64.Type}, {"LTO", Int64.Type}, {"LStl", Int64.Type}, {"LBlk", Int64.Type}, {"LPF", Int64.Type}}),
Winners = Table.TransformColumnNames(#"Change Types", each if Text.StartsWith(_, "W") then Text.Replace(_, "W", "T1_") else Text.Replace(_, "L", "T2_")),
#"Rename L" = Table.TransformColumnNames(#"Change Types", each if Text.StartsWith(_, "W") then Text.Replace(_, "W", "T2_") else Text.Replace(_, "L", "T1_")),
#"Replaced Value L" = Table.ReplaceValue(#"Rename L", each (location), each if (location) = 1 then -1 else if Text.Contains((location), -1) then 1 else (location), Replacer.ReplaceValue, {"location"}),
Losers = Table.TransformColumnTypes(#"Replaced Value L", {{"location", Int64.Type}}),
Combined = Table.Combine({Winners, Losers}),
PointDiff = Table.AddColumn(Combined, "PointDiff", each (T1_Score) - (T2_Score), Int64.Type)
in
PointDiff

Nota: i valori di connessione Lakehouse sono stati rimossi

Quello che sta succedendo qui è che noi siamo:

  1. Caricamento dei dati dalla Lakehouse;
  2. Filtrare le righe per includere solo i file CSV che corrispondono al parametro TourneyType;
  3. Caricamento dei file CSV nelle tabelle;
  4. Espansione delle tabelle in colonne;
  5. Rinominare le colonne;
  6. Modifica dei tipi di dati;
  7. Combinare nuovamente le due tabelle;
  8. Calcolo della differenza punti tra le due squadre.

Utilizzare la query è quindi semplice come selezionarla e quindi richiamare la funzione con il parametro TourneyType.

Fig. 12 — Funzione di richiamo. Immagine dell'autore.

Ciò creerà una nuova query con la funzione come origine e i dati caricati e trasformati. Si tratta quindi solo di caricare i dati in Lakehouse come una nuova tabella.

Fig. 13 — Carico di funzioni. Immagine dell'autore.

Come puoi vedere, la funzione LoadTournamentData viene invocata con il parametro “RegularSeasonDetailedResults” che caricherà sia le partite della stagione regolare maschile che quella femminile in un'unica tabella.

E questo è tutto!

Ci auguriamo che questo post ti abbia fornito una buona panoramica su come utilizzare Dataflow Gen2 per preparare i dati e creare funzionalità per il tuo modello di machine learning. Il suo approccio low-code semplifica la creazione rapida di pipeline di dati e contiene molte funzionalità potenti che possono essere utilizzate per creare trasformazioni complesse. È un ottimo primo punto di riferimento per chiunque abbia bisogno di trasformare i dati ma, cosa ancora più importante, ha il vantaggio di non dover scrivere codice complesso soggetto a errori, difficile da testare e difficile da mantenere.

Al momento in cui scrivo, i flussi di dati Gen2 non sono supportati dall'integrazione Git e quindi non è possibile controllare la versione o condividere i flussi di dati. Questa funzionalità dovrebbe essere rilasciato nel quarto trimestre del 2024.

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *