La Scala giusta per i Big Data

Negli ultimi anni, con il crescente fiorire dei dati “BIG”, si è andato sempre più affermando il linguaggio di programmazione Scala, il quale sembra proprio aver stabilmente conquistato il podio tra i linguaggi preferiti dai data scientists.

Il primo importante credito guadagnato da Scala sulla scena del coding risale a qualche anno fa, quando alcune parti di Twitter – il cinguettio social che si trova a gestire qualche milione di piccoli testi al giorno – scritte in C++, sono state completamente ricodificate in linguaggio Scala.

Il data scientist lavora abitualmente con grandi, anzi enormi set di dati, i quali devono essere caricati ma soprattutto trasformati, per le successive elaborazioni, in modo semplice e potente. E su questo terreno Scala non ha rivali: con poche righe di codice (rispetto a Java e Python) e con tempi di risposta eccellenti, si eseguono senza problemi operazioni e trasformazioni su ingenti volumi di informazioni.

Molti dei framework ad alte prestazioni utilizzate dai data scientists sono costruiti on top su Hadoop e di solito sono scritti in Scala o in Java. Scala offre, molto più di Java, un supporto ineguagliato nella gestione della concorrenza, la vera chiave per analizzare i big data attraverso l’elaborazione parallela. Scala è praticamente il linguaggio standard de facto per uno dei framework più in voga per le analisi su ingenti volumi di dati: Apache Spark.

Scopo di questo articolo, a cui torneremo a breve, è proprio quello di vedere Scala e Spark insieme in azione.

Quali sono le caratteristiche di Scala che lo differenziano ad esempio da Java?

Riassumiamo le principali:

  • compatibilità: Scala è open source e compatibile; una volta compilato con il suo compilatore, il codice diventa bytecode per la JVM, che può usare librerie Java ed essere perfettamente integrato in applicazioni Java esistenti, senza referenziare classloader particolari, visto che non viene interpretato a run-time;
  • scalabilità: le applicazioni moderne necessitano sempre più di essere scalabili e Scala (SCAlable LAnguage) permette, anzi favorisce, la scrittura di un codice che come può essere eseguito in un’applicazione console di un PC, può essere eseguito in un cluster; in Java, invece, non è semplice rendere un’applicazione o una libreria veramente scalabile, richiede uno sforzo di progettazione non indifferente;
  • ottimizzato per lavorare coi dati: Scala è il linguaggio ideale per sviluppare applicazioni data-centric: ad esempio, una delle differenze più importante con Java riguarda le Collection: in Java, lavorare con le collezioni porta a codice verboso, spesso poco scalabile e non intuitivo, se paragonato alla potenza e alla coincisione di Scala.

L’obiettivo di Martin Odersky, il creatore di Scala, era quello di creare un linguaggio strutturalmente molto più agile di Java ma ottimizzato per lo sviluppo di applicazioni data-driven. Possiamo dire che l’obiettivo è stato raggiunto, vista anche la notevole diffusione di Scala. Tra l’altro, se volete avere Martin come insegnante, non dovete che registrarvi all’ottimo corso introduttivo a Scala che tiene su Coursera.

Scala adotta un paradigma di programmazione ibrido: è un linguaggio funzionale e orientato agli oggetti allo stesso tempo.

Iniziamo la nostra incursione operativa su Scala partendo dall’ottima GUI di sviluppo Scala Activator, creata dalla Lightbend, azienda estremamente focalizzata su Scala, che ci viene messa a disposizione per il download sul website ufficiale di Scala.

Scala Activator ha alcuni template già pronti all’uso e, dalla home page, possiamo attivare vari componenti dell’ecosistema, come Akka concurrency tool kit, Play web framework, Spark for big data handling.

Figura 1 - Selezione del template

Figura 1 – Selezione del template

Nel tab Tutorials, dopo aver cercato Spark e selezionato il template Spark Workshop, decidiamo di posizionarlo su disco (ad esempio, nel folder di Activator) e di creare il progetto: Activator verifica le dipendenze e, aprendo il progetto, ci viene mostrato un tutorial, ma possiamo anche vedere il codice, compilarlo, testarlo ed eseguirlo.

Questo Workshop spiega Spark e include il famoso esempio base per i big data, ossia WordCount. Ma, sempre nei template, troviamo anche l’evoluzione, WordCount2: ad esempio, possiamo richiedere il numero di volte in cui “the” è usato in tutte le opere di Shakespeare: si può vedere il codice sorgente ed eseguire il test (vedi Figura 2).

Figura 2 - Ambiente di sviluppo Activator

Figura 2 – Ambiente di sviluppo Activator

Creiamo ora un nuovo progetto con Activator (per utilizzare Spark) eseguendo i passi a seguire:

  • creazione della directory di progetto
  • creazione del file build.sbt con le minime direttive
  • esecuzione del comando activator che crea il progetto

Il comando activator deve essere visibile nel vostro contesto $PATH.

Il contenuto del file di build (scaricato via curl da github per comodità) è quello mostrato a seguire:

L’esecuzione del comando activator crea la seguente struttura:

e una volta ricevuto il prompt di Activator (>), possiamo ad esempio compilare il progetto con il comando compile (oppure lanciare test). Se invece lanciassimo il comando run (ossia, tiriamo su la JVM) darà errore perché non ci sono ancora sorgenti/classi. Per avere maggiori informazioni sui comandi a disposizione lanciare il comando help.

Lo strumento più utile per entrare da subito a stretto contatto con il linguaggio Scala è sicuramente il REPL (acronimo di Read, Evaluate and Print Loop): si tratta di una sessione shell interattiva per provare la logica Scala.

Per entrare nella console Scala (che funge da interprete del linguaggio), prima lanciamo activator dalla directory di progetto, e poi il comando console. Apparirà così il prompt (scala>), e potremo ad esempio scrivere:

ossia, abbiamo potuto direttamente sfruttare res0 dove Scala ha messo il resultset del primo comando e applicare una trasformazione che ora dà come risultato res1.

Con REPL possiamo effettuare operazioni di data science elementari come l’operazione di split  di un data set di numeri in N subset modulo 3 ossia:

Con REPL possiamo anche effettuare operazioni Spark. Lavoriamo ad esempio con gli n-grams. Un n-gramma è una sotto sequenza di n elementi di una data sequenza. Esempio di 2-gram: “il treno”, esempio di 3-gram: “il treno è”

Vediamo un esempio di come usare Spark (eseguire, quindi, task in parallelo) per elaborare un ingente file di testo e, tramite un semplice codice Scala che editeremo direttamente nella console, avere in output una serie di 2-grams.

Non vi preoccupate se l’esempio non risulterà sufficientemente chiaro: lo scopo è solo quello di fornire una dimostrazione di come lavora Scala.

Nel nostro esempio, l’input sarà:

e l’output consisterà in una serie di 2-grammi estrapolati a partire dalle righe in input:

Creata una sessione console, prima di tutto importiamo le librerie Spark entrando in modalità paste (modalità che ci dà la possibilità di inserire più righe):

usciamo dalla modalità paste con CTRL+D. Creiamo, proseguendo nella stessa sessione console, un context Spark, dapprima creando il config:

e quindi il context, basato sul config creato:

e poi anche un SQL context, basato sul context Spark creato poc’anzi:

Siamo pronti per inserire i dati di input, o meglio la loro rappresentazione dataframes [1]:

Ora creiamo un n-gram per il nostro esempio:

a partire da questo , creiamo l’n-gram dataframe, trasformando il data frame wordDataFrame:

e infine trasformiamo l’n-gram dataframe ottenendo l’output desiderato:

osserviamo che con take(2) prendiamo le prime due linee del data frame, con map mappiamo tali linee in due stream.

Chiudiamo questa breve introduzione a Scala dando un’occhiata a uno dei costrutti alla base del linguaggio: le classi. Cos’è una classe Scala?

E’ la denominazione di un type:

  • impersona lo stato di una istanza della classe (es: classe/type customer, dove gli stati sono name, age, phone number);
  • rappresenta il comportamento di come quello stato può essere trasformato (come posso cambiare il phone number);
  • non è operativa fino a quando non viene istanziata attraverso il suo costruttore new (istanziando una classe con new inizio a rappresentare un customer con una persona specifica);
  • possono esistere istanza multiple di una classe (più customers nel sistema).

Vediamo una semplicissima classe Scala:

res5.String=Hello@2f1ebc37 e viene restituita la locazione all’interno del heap Java (ossia lo spazio di memoria nella JVM)

Ogni classe ha automaticamente un primary constructor, che definisce la firma di come creare una istanza, il corpo della classe è l’implementazione del constructor:

Mi fermo qui. Lascio alla vostra curiosità l’approfondimento delle caratteristiche di Scala, ricordando che, oltre alle fonti ufficiali già citate, è disponibile un eccellente corso online a cura della Lightbend, nonchè ottimi articoli introduttivi in italiano di MokaByte.

[1] Un DataFrame Spark è una collezione distribuita di dati organizzati in colonne che permette operazioni di filtro, di raggruppamento, o di aggregazione, e che può essere usato con Spark SQL. Il DataFrame è concettualmente equivalente a una tabella in un database relazionale (o a un dataframe Python). I DataFrames possono essere costruiti a partire da svariate tipologie di fonti come: files strutturati, tabelle Hive, database esterno, RDD esistenti.

Alessandro Cecconi

Sono ingegnere elettronico, e dal 1995 sforno applicazioni software in ambito enterprise. Negli ultimi anni mi sto occupando di CRM e di SugarCRM in particolare, il mio blog è nato proprio con l’obiettivo di contribuire a far conoscere sempre di più questo software open source potente e, al tempo stesso, semplice e alla portata di tutti anche in Italia.

Potrebbero interessarti anche...