Grafico a linee

Partiamo sempre dall’esempio su cui stiamo lavorando da un po’ di tempo a questa parte e creiamo un grafico a linee in Processing.

L’unica modifica al codice che dobbiamo fare è quella relativa alla grafica: lasciamo, dunque, inalterata la prima parte, quella relativa alla lettura dei dati dal file .csv e l’inserimento degli stessi in un array e passiamo direttamente alla grafica.

Ripensare la posizione della x

La prima differenza rispetto al grafico a barre sarà determinare correttamente la posizione x del valore estratto dall’array. Se, infatti, il ciascuna barra del grafico aveva una sua larghezza, ora dobbiamo rappresentare solo un punto.

Per risolvere il problema, pur mantenendo il risultato finale che avevamo realizzato nell’ultimo post, creo una nuova variabile chiamata x1 che mi servirà come nuovo riferimento e aggiungo un nuovo ciclo for solo per disegnare i punti di riferimento nel grafico.


int x1 = 68;
  
for(int i = 0; i < tempMin.length; i++) {
  ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
  ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
  x1 += 40;
}

Ecco il risultato:

Grafico a linee in Processing

Creiamo il grafico a linee

Creare un grafico a linee potrebbe essere più complesso di quanto ipotizzato: dovremmo, infatti, creare manualmente una linea per congiungere tra loro i punti disegnati in precedenza.

Per fortuna ci vengono in aiuto alcune funzione di Processing: beginShape()vertex() ed endShape().

Con beginShape() diciamo al programma di iniziare a creare una forma, aggiungiamo tutti i vertici che la compongono con vertex() e, una volta richiamata la funzione endShape(), Processing si preoccuperà di collegare insieme tutti i punti.

Come al solito, consiglio di verificare sul reference i parametri opzionali che possiamo usare per ciascuna funzione. Per questo esempio il comportamento di default è quello che fa al caso nostro ma in futuro potrebbero tornarvi utili anche le altre possibilità.

Dovendo creare due grafici a barre, dovremo utilizzare due cicli for:


int x1 = 68;

beginShape();
for (int i = 0; i < tempMin.length; i++) {
  vertex(x1, height - 50 + tempMin[i] * - 20);
  ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
  x1 += 40;
}
endShape();

x1 = 68;
beginShape();
for (int i = 0; i < tempMax.length; i++) {
  vertex(x1, height - 50 + tempMax[i] * - 20);
  ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
  x1 += 40;
}
endShape();

Grafico a linee beginShape, vertex, endShape

A questo punto possiamo eliminare i grafici a barre che avevamo tenuto come riferimento e andare a sistemare i colori per ottenere un risultato finale soddisfacente:

Grafico a linee finale in Processing

Codice completo

Guardando il codice vi sembrerà un po’ verboso, in particolare per quanto riguarda le assegnazioni dei colori. Facendo qualche esperimento vi renderete conto di quanto sia importante inserire le istruzioni giuste per evitare problemi di visualizzazione.


/*
 * Grafico a linee
 * Federico Pepe, 20.05.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[], tempMax[];

void setup() {
  size(500, 500);
  background(255);
  noStroke();
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());

  tempMin = new float[0];
  tempMax = new float[0];

  for (int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
    tempMax = append(tempMax, csv.getFloat(3, i));
  }

  printArray(tempMin);

  println("Il valore minimo è: " + min(tempMin));
  println("Il valore massimo è: " + max(tempMin));

  noLoop();

  // Scritte relative all'anno
  int x = 50;
  int year = 2008;
  
  for (int i = 0; i < tempMin.length; i++) {
    fill(0, 127);
    text(year, x + 2, height - 30);
    x += 40;
    year++;
  }
  
  // Temperature minime
  int x1 = 68;
  beginShape();
  for (int i = 0; i < tempMin.length; i++) {
    fill(100, 190, 255);
    vertex(x1, height - 50 + tempMin[i] * - 20);
    noStroke();
    ellipse(x1, height - 50 + tempMin[i] * - 20, 10, 10);
    x1 += 40;
    stroke(100, 190, 255);
    noFill();
  }
  endShape();
  
  // Temperature massime
  x1 = 68;
  beginShape();
  for (int i = 0; i < tempMax.length; i++) {
    fill(255, 100, 100);
    vertex(x1, height - 50 + tempMax[i] * - 20);
    noStroke();
    ellipse(x1, height - 50 + tempMax[i] * - 20, 10, 10);
    x1 += 40;
    stroke(255, 100, 100);
    noFill();
  }
  endShape();

  // Griglia di riferimento
  textAlign(RIGHT, CENTER);
  for (int j = 0; j <= 20; j++) {
    if (j % 5 == 0) {
      fill(0, 127);
      text(j + "°", 25, height - 52 + (j * - 20));
      stroke(0, 30);
    } else {
      stroke(0, 15);
    }
    line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
  }
}

void draw() {
}

Grafico a barre II

Nell’articolo precedente abbiamo cominciato a lavorare al nostro primo grafico a barre utilizzando come fonte i dati presenti all’interno di un file CSV.

In questo articolo andremo a completare la visualizzazione inserendo anche le temperature massime e dei riferimenti.

Array delle temperature massime

I dati relativi alle temperature massime sono già presenti dentro al file CSV. Come abbiamo già visto, per comodità inseriamo questi dati in un array.

Dove abbiamo dichiarato le variabili, aggiungiamo:

float tempMin[], tempMax[];

Inizializziamo l’array:

tempMax = new float[0];

Inseriamo i dati utilizzando lo stesso ciclo for per non appesantire troppo il programma

for (int i = 1; i < csv.getColumnCount(); i++) {
  tempMin = append(tempMin, csv.getFloat(0, i));
  tempMax = append(tempMax, csv.getFloat(3, i));
}

Grafico a barre delle temperature massime

Utilizzando sempre il codice che abbiamo già scritto, aggiungiamo nel ciclo for in cui andavamo a disegnare i rettangoli delle temperature minime, quelli relativi alle massime. Per differenziarle a livello visivo utilizzeremo il colore rosso.

fill(255, 100, 100);
rect(x, height - 50, 36, tempMax[i] * - 20);

Siccome i rettangoli rossi saranno più alti rispetto a quelli azzurri, assicuriamoci di disegnarli per primi.

Nel mio codice ho aggiunto anche un noStroke() per eliminare il bordo.

Grafico a barre con temperature massime

Aggiungiamo dei riferimenti

Quando si creano delle visualizzazioni di dati può essere utile dare dei riferimenti a chi sta osservando il nostro lavoro per aiutarli nella comprensione. L’immagine qui sopra presa singolarmente potrebbe rappresentare migliaia di cose differenti.

Aggiungere delle linee di riferimento è questione di un semplice ciclo for:

for (int j = 0; j <= 20; j++) {
  stroke(0, 30);
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Grafico a barre con riferimenti in Processing

Ora non ci resta che aggiungere il testo (aggiungiamo un paio di linee di codice al precedente ciclo for):

textAlign(RIGHT, CENTER);
for (int j = 0; j <= 20; j++) {
  fill(0, 127);
  text(j + "°", 25, height - 52 + (j * - 20));
  stroke(0, 30);
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Grafico a barre in Processing

Per rendere il grafico più leggibile facciamo dei piccoli miglioramenti: scriviamo il valore numerico ogni cinque gradi e schiariamo leggermente le altre linee:

textAlign(RIGHT, CENTER);

for (int j = 0; j <= 20; j++) {
  if (j % 5 == 0) {
    fill(0, 127);
    text(j + "°", 25, height - 52 + (j * - 20));
    stroke(0, 30);
  } else {
    stroke(0, 15);
  }
  line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
}

Realizzare un grafico a barre in Processing

Non ci resta che aggiungere l'anno di riferimento in fondo al grafico: avendo aggiunto l'opzione header nella lettura del file CSV viene saltata completamente la prima riga che include proprio questo dato. Potremmo riscrivere il codice ma, per questa volta, adotteremo una soluzione più semplice anche se non proprio elegante. Per completezza includo tutto il codice del programma:

/*
 * Grafico a barre, 2
 * Federico Pepe, 29.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[], tempMax[];

void setup() {
  size(500, 500);
  background(255);
  noStroke();
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());

  tempMin = new float[0];
  tempMax = new float[0];

  for (int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
    tempMax = append(tempMax, csv.getFloat(3, i));
  }

  printArray(tempMin);

  println("Il valore minimo è: " + min(tempMin));
  println("Il valore massimo è: " + max(tempMin));

  noLoop();

  int x = 50;
  int year = 2008;

  for (int i = 0; i < tempMin.length; i++) {
    fill(255, 100, 100);
    rect(x, height - 50, 36, tempMax[i] * - 20);
    fill(100, 190, 255);
    rect(x, height - 50, 36, tempMin[i] * - 20);
    fill(0, 127);
    text(year, x + 2, height - 30);
    x += 40;
    year++;
  }

  textAlign(RIGHT, CENTER);

  for (int j = 0; j <= 20; j++) {
    if (j % 5 == 0) {
      fill(0, 127);
      text(j + "°", 25, height - 52 + (j * - 20));
      stroke(0, 30);
    } else {
      stroke(0, 15);
    }
    line(30, height - 50 + (j * - 20), 470, height - 50 + (j * - 20));
  }
}

void draw() {
}

Bar Graph in Processing

Creiamo il nostro primo grafico a barre

Abbiamo imparato come leggere un file CSV in Processing e come cominciare a lavorare sui dati passando da una tabella a un array. Ora è arrivato il momento di creare il nostro primo grafico a barre.

Per fare un velocissimo recap: stiamo lavorando in Processing con un file CSV che rappresenta le statistiche metoclimatiche degli ultimi 10 anni (2008-2017) della regione Veneto, prese dal sito del Mipaaf.

Il file contiene diverse informazioni interessanti ma, per il momento, abbiamo creato un array in cui abbiamo salvato solo i valori delle temperature minime:

[0] 6.9
[1] 7.3
[2] 6.7
[3] 7.4
[4] 7.2
[5] 7.7
[6] 8.7
[7] 8.0
[8] 7.6
[9] 7.1

Il primo valore si riferisce all’anno 2008 mentre quello inserito alla posizione [9] è il dato del 2017.

Disegniamo il grafico a barre

Disegnare un grafico a barre non è difficile: devo creare dei rettangoli della stessa larghezza e la cui altezza sia legata al dato che voglio rappresentare.

Creiamo una finestra di 500×500 pixel e, siccome i valori sono 10 divido, ciascun rettangolo avrà una larghezza pari a 50 pixel.

Utilizzo un ciclo for per leggere dall’array i singoli valori e li assegno direttamente all’altezza dei rettangoli

/*
 * Creiamo il nostro primo grafico a barre
 * Federico Pepe, 22.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  // Dimensione della finestra
  size(500, 500);
  
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  // Creazione dell'array
  tempMin = new float[0];
  // Inserimento dei dati nell'array
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
  }
  printArray(tempMin);
  // Disegno il grafico
  for(int j = 0; j < tempMin.length; j++) {
    rect(j * 50, 0, 50, tempMin[j]);
  }
  
  noLoop();
}

void draw() {
}

La riga di codice più importante è: rect(j * 50, 0, 50, tempMin[j]);. Ricordo che la funzione rect() accetta quattro parametri: posizione x, posizione y, larghezza e altezza del rettangolo. Il codice, quindi, dovrebbe essere chiaro e non dovrebbe necessitare di ulteriori spiegazioni.

Grafico a barre in Processing

Il risultato, come possiamo vedere nell’immagine, non è, però, molto soddisfacente: i grafici a barre generalmente vengono disegnati dal basso verso l’alto e, in questo caso, il valore dell’altezza dei rettangoli è troppo basso per essere comprensibile.

È sufficiente modificare una sola riga di codice:

rect(j * 50, height, 50, tempMin[j] * - 20);

per ottenere un risultato completamente differente:

Bar Chart in Processing

Aggiungiamo un po’ di margine dai bordi della finestra e tra le varie barre del nostro grafico:

// Disegno il grafico
  int x = 50;
  for(int j = 0; j < tempMin.length; j++) {
    rect(x, height - 50, 36, tempMin[j] * - 20);
    x += 40;
  }

e un po’ di colore fill(100, 190, 255);

Visualizzazione di dati in Processing (grafico a barre)

Ecco il codice completo:

/*
 * Creiamo il nostro primo grafico a barre
 * Federico Pepe, 22.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  // Dimensione della finestra
  size(500, 500);
  background(255);
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  // Creazione dell'array
  tempMin = new float[0];
  // Inserimento dei dati nell'array
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
  }
  printArray(tempMin);
  // Disegno il grafico
  int x = 50;
  fill(100, 190, 255);
  for(int j = 0; j < tempMin.length; j++) {
    rect(x, height - 50, 36, tempMin[j] * - 20);
    x += 40;
  }
  
  noLoop();
}

void draw() {
}

Per il momento possiamo fermarci qui. Per chi volesse spingersi un po’ oltre, un buon esercizio potrebbe essere, partendo dal codice qui sopra, aggiungere al grafico anche le temperature massime con dei rettangoli colorati di rosso.

Alice Merton – No Roots

Track Deconstruction è una nuova rubrica che, attraverso l’analisi di brani più o meno famosi, ha l’obiettivo di spiegare come vengono scritte canzoni di successo e di farci capire che, per nostra fortuna, non ci sono particolari trucchi o segreti. Oggi analizziamo insieme il brano No Roots di Alice Merton.

Premessa

Se ascoltate la radio è molto probabile che conosciate già questa canzone: negli ultimi due mesi è andata in rotazione nelle principali stazioni italiane. Il motivo per cui l’ho scelta è molto semplice: fin dalla prima volta che l’ho sentito l’ho trovato orecchiabile e interessante da analizzare per la sua apparente semplicità.

Facendo una veloce ricerca si scopre che Alice Merton, classe 1993, è una cantante tedesco-canadese che, con Paul Grauwinkel, ha fondato un’etichetta discografica, la Paper Plane Records international con la quale ha pubblicato il suo singolo No Roots e l’omonimo EP.

Nessun talent show né major discografica o, addirittura, etichetta indipendente di successo alle spalle per supportare il suo singolo; con questo brano è riuscita ad arrivare al successo e le 79 milioni di visualizzazioni del video su YouTube – nel momento in cui scrivo – ne sono la prova.

Metro, BPM e struttura del brano

Quando analizzo un brano mi piace partire subito dalle cose più semplici: il metro è un classico 4/4, la velocità del brano è 116 bpm. Di norma preferisco non concentrarmi subito su note, accordi e arrangiamento perché richiedono un minimo di competenze musicali. Al contrario scrivere la struttura di un brano, ovvero al suddivisione in verso, ritornello, ecc… è una cosa piuttosto semplice che chiunque sappia contare fino a 10 e sia armato di carta e penna può fare senza troppa difficoltà.

Ho importato il brano in Ableton Live 10, impostato il metro e il BPM e mi sono assicurato che l’allineamento del punto di inizio coincidesse con 1.1.1.

Alice Merton - No Roots - Song Sctructure

La struttura del brano è la seguente:

Intro (6 bars)
|      |      |      |       |
|      |      |
Verse I (8)
|      |      |      |       |
|      |      |      |       |
Verse II (8)
|      |      |      |       |
|      |      |      |       |
Pre-Chorus (4)
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
Verse I (8)
|      |      |      |       |
|      |      |      |       |
Verse II (8)
|      |      |      |       |
|      |      |      |       |
Pre-Chorus (4)
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
Bridge (9)
|      |      |      |       |
|      |      |      |       |
|      |
Solo (8)
|      |      |      |       |
|      |      |      |       |
Chorus (16)
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |
|      |      |      |       |


Noterete subito che, pur non trattandosi della classica struttura dei brani pop, non si discosta troppo da questo modello. Interessante notare l’intro lungo 6 battute (molto spesso,viene tagliato completamente dalla radio) e il bridge di 9 se consideriamo 8 battute effettive più una che serve per il lancio dell’assolo di sintetizzatore.

Riff principale

Tutto il brano è giocato su un riff suonato, all’inizio della canzone, dal basso. Il riff parte dalla nota B e si sviluppa come segue:

No Roots - Bass Intro Il riff suonato dal basso nell’intro.

 

Siccome, come dicevamo prima, l’intro è stranamente lungo sei battute e non otto come accade di solito, ecco che nel verso è necessario allungare di due battute il riff. Nell’immagine sottostante ho utilizzato il pulsante Fold per mostrare solo le note suonate ed eliminare dalla vista le altre.

Bass (Verse)

 

Questo riff è così importante che si ripeterà in modo pressoché identico per tutta la durata del brano, chorus compreso. Ad un ascolto attento ci si rende conto, però, che c’è una piccola variazione nel secondo verso:

Armonia

Trattandosi di un brano pop, l’armonia è dell’intera canzone è piuttosto semplice: la tonalità è Bm e l’ho dedotta partendo dalle note suonate dal basso. Considerando le otto battute le verso, nelle prime quattro l’accordo è sempre Bm, nella quinta passa a Em per poi salire a G e A, entrambi maggiori, nella sesta battuta e ricadere su Bm nelle ultime due.

La progressione è, dunque:

I - IV - VI - VII

Inseriamo gli accordi nella struttura del verso:

Verse
|Bm    |%     |%     |%      |
|Em    |G  A  |Bm    |%      |

No Roots - Chords Accordi fatti con il pianoforte (in blu) e riff di basso (giallo)

 

 

L’unica variazione a livello armonico, se così la possiamo definire, avviene nel pre-chorus dove vengono suonati gli accordi di EmGA per poi lanciare il ritornello che presenta la stessa progressione armonica del verso.

Ritmica

La parte ritmica di batteria è davvero semplice: cassa praticamente sempre sui quarti, il rullante, che entra nel ritornello e suona sul secondo e sul quarto quarto produce la classica ritmica chiamata backbeat. Le percussioni (shaker) sono in levare.

 

Da segnalare l’utilizzo di percussioni, in particolare floor tom per sottolineare i passaggi da una sezione all’altra del brano.

Melodia

Trascrivere la melodia di un brano è sempre la parte che trovo più difficile. Per mia fortuna in questo caso è composta da alcune cellule che si ripetono sempre uguali:

Alice Merton - Melody La melodia del primo verso

Lascio a voi il divertimento di trascrivere la melodia del pre-chorus e del chorus.

Conclusione

Mancano ancora alcuni dettagli del brano ma il grosso del lavoro è stato fatto. Ora si tratta solo di rimettere insieme le idee, ordinarle e farle suonare insieme. Ecco il risultato finale che ho ottenuto dall’intro fino alla conclusione del primo ritornello:

Da una tabella CSV agli array

Una volta capito come leggere con Processing i dati contenuti in un file CSV, il passaggio successivo è rendere questi dati leggibili e modificabili facilmente all’interno del programma convertendoli in variabili e array.

Ripartiamo dal nostro esempio precedente utilizzando sempre lo stesso data set:

/*
 * Leggere file CSV
 * Federico Pepe, 25.03.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  for(int i = 0; i < csv.getRowCount(); i++) {
    println(csv.getFloat(i, 2));
  }
  
  noLoop();
}

void draw() {
}

Prima di procedere, assicuriamoci che il programma funzioni cliccando su run: nella console dovrebbero comparire i dati contenuti nella colonna relativa al 2009.

Utilizzare i metodi corretti per leggere i dati

Un aspetto importante da tenere presente è di utilizzare i metodi corretti per accedere ai dati. Spulciando nel reference di Table noterete che esistono diverse funzioni come .getFloat().getInt().getString().

Se sostituiamo nell’esempio precedente la riga println(csv.getFloat(i, 2)); con println(csv.getString(i, 2)); Processing non genererà nessun errore e continuerà a far girare il nostro programma ma, ora, quei valori sono considerati stringhe (quindi testo) e non più numeri.

Tutti questi metodi accettano due parametri: il primo indica la riga della tabella, il secondo la colonna. Per quest’ultimo possiamo usare sia un numero, partendo, come sempre a contare da 0, oppure una stringa contenente il nome della colonna.

Il codice, può essere sostituito con: println(csv.getFloat(i, "2009"));

Se sostituite sempre la stessa riga con println(csv.getFloat(i, "Descrizione")); la console vi restituirà tutti valori NaN ovvero Not a Number. Come dicevo, il programma continuerà a funzionare ma non in modo corretto.

Dal tabella CSV all’array

Passare tutti i dati in un array può essere molto comodo per utilizzare alcune funzioni specifiche di calcolo, come, ad esempio, min()max() che restituiscono, rispettivamente, il valore minimo e massimo di un array.

Anche se in questo momento siamo ancora lontani dall’idea di creare una visualizzazione di dati, dovremmo comunque cominciare a pensare a come utilizzeremo questi numeri.

Per come è stato strutturato il file CSV, ciascuna colonna rappresenta un anno con valori di vario tipo: temperatura minima, temperatura massima, eccetera; ma se noi volessimo rappresentare la variazione di uno stesso valore nel tempo dovremmo lavorare orizzontalmente e non verticalmente.

Sfruttiamo questo esempio per capire come passare i dati dal CSV a un array:

Creiamo un array di tipo float chiamato tempMin nel quale inseriremo tutti i valori di temperatura minima e modifichiamo il nostro ciclo for per girare non più sul numero di righe ma su quello delle colonne. Impostiamo l’inizio del ciclo for a 1 per saltare la prima colonna.

/*
 * Da una tabella CSV agli array
 * Federico Pepe, 01.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  tempMin = new float[csv.getColumnCount()];
    
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin[i] = csv.getFloat(0, i);
  }
  
  printArray(tempMin);
  
  noLoop();
}

void draw() {
}

La dimensione dell’array è uguale al numero di colonne all’interno del file tempMin = new float[csv.getColumnCount()]; e inseriamo all’interno dell’array i valori float provenienti dalla riga 0, perché stiamo ignorando l’header, e di ciascuna colonna: tempMin[i] = csv.getFloat(0, i);

Dal risultato in console notiamo subito un problema: il primo valore dell’array è 0.0 perché, effettivamente, l’array contiene un valore in più, quello della colonna Descrizione.

Abbiamo due possibilità per risolvere il problema:

Modificare la grandezza dell’array sottraendo 1: tempMin = new float[csv.getColumnCount()-1]; e modificando l’inserimento dei valori nell’array sempre spostando l’indice indietro di 1 tempMin[i-1] = csv.getFloat(0, i);.

Questa soluzione funziona ma non è molto elegante, meglio cambiare il codice come segue: inizializziamo l’array con grandezza pari a 0: tempMin = new float[0]; e poi utilizziamo la funzione append() che espande l’array di un elemento e aggiunge il dato nella nuova posizione tempMin = append(tempMin, csv.getFloat(0, i));

Il codice completo

/*
 * Da una tabella CSV agli array
 * Federico Pepe, 01.04.2018
 * http://blog.federicopepe.com/processing
 */

Table csv;

float tempMin[];

void setup() {
  csv = loadTable("data.csv", "header");

  println("Numero righe: " + csv.getRowCount());
  println("Numero colonne: " + csv.getColumnCount());
  
  tempMin = new float[0];
    
  for(int i = 1; i < csv.getColumnCount(); i++) {
    tempMin = append(tempMin, csv.getFloat(0, i));
  }
  
  printArray(tempMin);
  
  noLoop();
}

void draw() {
}

Ora l’array è corretto e contiene esattamente tutti i valori previsti. Come dicevo, ora possiamo sfruttare l’array per ottenere il valore minimo e quello massimo molto semplicemente:

println("Il valore minimo è: " + min(tempMin));
println("Il valore massimo è: " + max(tempMin));