Estrarre intervalli di tempo da un data frame

Se abbiamo un data frame con una colonna contenente data e ora (esempio una misurazione fatta ogni 10 minuti, quindi il formato sarà del tipo 2013-07-18 02:03:40), come facciamo a estrarre un intervallo preciso di valori?
L’inghippo è usare la funzione subset combinata con la funzione strftime.
Prima di tutto però è necessario che la colonna da cui estrarre i valori sia formattata e riconosciuta come data e ora (per vederlo basta eseguire str(nome_df), Vedi qui .
Questa riga di codice:
xxx <- subset(df, strftime(Date.time, "%H:%M", tz="") %in% c('00:10','06:10','12:10','18:10'))
estrae dalla colonna Date.time del dataframe df, tutti i valori specificati nella seconda parte del codice.

Opzioni per subset

Come già visto in vari articoli (vedi il tag subset) la funzione subset è davvero una manna dal cielo. Volevo esplorare un attimino alcuni passaggi che mi hanno creato dei problemini. Immaginiamo sempre di aver il nostro data frame fatto da n colonne di cui una identifica l’ID. Quello che vogliamo fare è estrarre pezzi del nostro data frame in funzione di vari ID. Ci sono vari modi:

1) Estrazione in funzione di un solo ID. Il primo metodo è più sbrigativo.

new_df<-subset(df, ID=="P1")

oppure

new_df<-subset(df, ID %in% c("P1"))

2) estrazione in funzione di diversi ID. Si vede che il secondo metodo adesso è più pratico,

new_df<-subset(df, ID=="P1" | ID=="P2" | ID=="P3")

new_df<-subset(df, ID %in% c("P1", "P2", "P3"))

3) tutti gli ID TRANNE uno in particolare. Si aggiunge il !

new_df<-subset(df, ID!="P1")

new_df<-subset(df, !ID %in% c("P1"))

4) Estrazione in funzione di diversi criteri

new_df<-subset(df, ID=="P1" & Value>10)

new_df<-subset(df, ID %in% c("P1") & Value > 10)

 

Scrivere una funzione in R

L’enorme utilità di programmi come R è la possibilità di scriversi le proprie funzioni e richiamarle quando ci servono. Bisogna imparare un pochino di sintassi: più la funzione è complessa e maggiori saranno le difficoltà, ma ne vale la pena.
La sintassi base per scrivere una funzione è:
nome funzione <- function(x,y,…) {statements}
Un rapido e banale esempio. Proviamo a scrivere una funzione che calcoli la media. Apriamo un file R script e iniziamo:

media <- function (x) {
somma=sum(x)
n=length(x)
media=somma/n
print(media)
}

Tutto qui. Tre cose importanti:
– attenzione alle parentesi: tutto ciò che riguarda la funzione deve essere inserito all’interno delle parentesi graffe. È possibile anche incorporare funzioni nelle funzioni
– la riga print(media) permette di visualizzare il risultato. R infatti ha una “politica al risparmio”, ovvero se non specifichiamo che vogliamo vedere il risultato questo non viene mostrato
– una volta scritta la funzione, questa va salvata e “richiamata”. In altre parole bisogna effettuare il source di quella funzione semplicemente con
source(percorso_della_funzione)

Manipolazione avanzata con ggplot2

Voglio offrire un piccolo esempio di manipolazione dati per la produzione di grafici ad alto livello con ggplot2. Consideriamo il data frame (chiamato df):

Date.time P T EC ID
2013-07-03 12:05:00 1180 9.51 3.32 K0904
2013-07-03 12:10:00 1180 9.51 3.32 K0904
2013-07-03 12:15:00 1179 9.50 3.32 K0904
2013-07-03 12:20:00 1179 9.49 3.32 K1136
2013-07-03 12:25:00 1180 9.49 3.32 K1136
2013-07-03 12:30:00 1180 9.48 3.31 K1136
.......
2013-07-03 12:05:00 1180 9.51 3.32 K1142
2013-07-03 12:10:00 1180 9.51 3.32 K1142
2013-07-03 12:15:00 1179 9.50 3.32 K1142
2013-07-03 12:20:00 1179 9.49 3.32 K1148
2013-07-03 12:25:00 1180 9.49 3.32 K1148
2013-07-03 12:30:00 1180 9.48 3.31 K1148
.........

Fatto da 100.000 righe e 5 colonne.  Si può iniziare a riprodurre semplicemente un grafico della T (sta per temperatura) in funzione di Date.time diviso per ID. Con il seguente codice:

ggplot(df, aes(x=Date.time, y=P, group=ID, color=ID)) +
geom_line() +
facet_grid( . ~ID)

otteniamo

2

L’opzione facet_grid permette di creare un grafico accanto all’altro in base al criterio di selezione (in questo caso ID). Attenzione alla sintassi dentro la parentesi: (. ~ ID) significa che la suddivisione in colonne. Invertendo i comandi si ottiene:

ggplot(df, aes(x=Date.time, y=P, group=ID, color=ID)) +
geom_line() +
facet_grid(ID~ .)

3

Il risultato è buono, ma se volete sovrapporre gli ultimi 3 grafici ai primi 3? In modo da avere 3 grafici totali ognuno fatto da una coppia di ID.  ggplot non offre una scappatoia veloce, ma si può agire velocemente sul data frame in modo da assegnare un nuovo ID per ogni coppia che si vuole sovrapporre. Cosi facendo possiamo usare il primo ID per distinguere i colori e il secondo ID diventa il criterio di differenziazione di facet_grid.  Ci sono vari modi per farlo, uno può essere:

#usare subset per estrarre dei data frame con gli ID che vogliamo#
dff1<-subset(df, ID %in% c("K0904", "K0955"))
dff2<-subset(df, ID %in% c("K1136", "K1148"))
dff3<-subset(df, ID %in% c("K1142", "K8651"))
#Assegnare il nuovo nome dell'ID#
dff1$new_ID<-c("K0904 & K0955")
dff2$new_ID<-c("K1136 & K1148")
dff3$new_ID<-c("K1142 & K8651")
#unire tutto in un unico nuovo data frame#
new_df<-rbind(dff1, dff2, dff3)
Adesso si può lanciare ggplot:
ggplot(new_df, aes(x=Date.time, y=P, group=ID, color=ID)) +
geom_line() +
facet_grid( . ~newID)

4

scrivere uno script

L’enorme vantaggio degli utenti Linux è quello di poter scrivere degli script che automatizzano alcune procedure. Gli script sono semplici file di testo con una lista di comandi che il sistema operativo legge ed esegue.

La prima cosa da fare è creare una cartella in cui andremo a mettere tutti i nostri script. Prima “nota”: la cartella deve far parte del PATH del sistema, ovvero deve essere in una posizione in cui Linux sa che ci avete posizionato degli script.

Per scoprire quali sono le cartelle che fanno già parte del PATH aprite il terminale e digitate:

$PATH

dovrebbe comparire una cosa simile:

bash: /home/matteo/.script:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games: File o directory non esistente

Nel mio caso ho deciso di creare la cartella .script in /home/matteo e di piazzare li tutti i miei script. Fatta la cartellla ho dovuto aggiungerla al PATH. Per farlo è necessario aggiungere una stringa di testo al file .bashrc (nel caso di UBUNTU): aprite il terminale (o se è già aperto andate nella home) e aprite il file .bashrc con un editor di testo:

gedit .bashrc

Nel file che vi si è aperto aggiunte in fondo la riga

export PATH=/home/matteo/.script:"$PATH"

dove ovviamente /home/matte/.script dovrà essere sostituito con il percorso della cartella che avete scelto.

Bene, ora non vi resta che aggiungere degli script! Andate nella cartella .script appena creata e create un documento nuovo con un editor di testo. La prima riga del documento deve essere

#!/bin/bash

Questa sequenza fa capire a Linux che quello che viene dopo è uno script. Aggiungete quello che vi pare. Qui un esempio:

#!/bin/bash
#script per fare pulizia di pacchetti inutilizzati

sudo apt-get autoremove
sudo apt-get autoclean
sudo apt-get clean
sudo apt-get purge

E salvate il documento. Ultimo passo è quello di rendere lo script eseguibile. Per farlo, aprite il terminale e andate nella cartella dove avete creato lo script. Se avete chiamato pulizia il documento creato, allora digitate:

chmod +x pulizia

Adesso siete pronti per eseguire il vostro script! Basta aprire il  terminale e digitare il nome del vostro script (esempio di sopra pulizia).

 

 

Estrarre valori in funzione della data (e ora)

Sapendo già che la funzione subset fa magie, ne aggiungo un’altra. Se vogliamo estrarre una sotto matrice di valori superiori a una certa data (e ora)?

Fondamentale è sapere come abbiamo formattato la colonna Data del date frame originale (con la funzione as.Date oppure as.POSIXct). Per vederlo diamo un str(df) e leggiamo:

data.frame':    92669 obs. of  5 variables:
$ Date.time: POSIXct, format: "2013-07-03 12:10:00" "2013-07-03 12:20:00"
$ P        : num  1114 1114 1114 1114 1114 ...
$ T        : num  11.9 11.9 11.9 11.9 11.9 ...

Quindi nel mio caso bisogna considerare la funzione POSIXct. Per l’estrazione, esempio solo i valori dopo il 10 luglio:

xxx<-subset(df, Date.time > as.POSIXct("2013-07-10"))

In questo modo xxx diventerà un sotto-data frame dell’originale df mantenendo le colonne e le righe, ma solo dopo il 10 luglio.

Grafico di una porzione di matrice con ggplot2

Ho già spiegato qui come estrarre delle parti di un data frame in base a un criterio specifico. Mi sono trovato però a lavorare con un data frame molto grande (quasi 100.000 righe) in cui sono stati raccolte delle misurazioni di 12 diversi strumenti. Una colonna di questo data frame si riferisce all’identificativo degli strumenti, mentre le altre contengono vari valori di alcuni parametri. Il data frame ha quindi questo aspetto:

Date.time P T EC ID
2013-07-03 12:05:00 1180 9.51 3.32 AAA
2013-07-03 12:10:00 1180 9.51 3.32 AAA
2013-07-03 12:15:00 1179 9.50 3.32 AAA
2013-07-03 12:20:00 1179 9.49 3.32 BBB
2013-07-03 12:25:00 1180 9.49 3.32 BBB
2013-07-03 12:30:00 1180 9.48 3.31 BBB
.......
2013-07-03 12:05:00 1180 9.51 3.32 CCC
2013-07-03 12:10:00 1180 9.51 3.32 CCC
2013-07-03 12:15:00 1179 9.50 3.32 CCC
2013-07-03 12:20:00 1179 9.49 3.32 DDD
2013-07-03 12:25:00 1180 9.49 3.32 DDD
2013-07-03 12:30:00 1180 9.48 3.31 DDD
.........

ggplot2 è un ottimo pacchetto che permette di ottenere grafici. Può spaventare all’inizio, ma ha una sua logica in quanto è stato creato in modo da offrire una grammatica per i grafici.

Di base bisogna indicare il nome del data frame di partenza e poi si personalizza ogni cosa aggiungendo un +. Per esempio, se volessimo plottare i valori di P di senza distinguerli per ID, il comando diventa:

ggplot(nome_data_frame) + geom_line(aes(Date.time, P)

geom_line unisce i vari valori con delle linee, aes si riferisce all'”estetica” del grafico (quali sono gli assi x e y, eventualmente i gruppi in cui suddivideri…..).
Ma se volessimo mettere in grafico i valori di P in funzione di Date.time solamente per alcuni ID, diciamo AAA e DDD. Come possiamo farlo rapidamente con ggplot2? La funzione che ci aiuta è subset.

ggplot(subset(logger, ID=="AAA" |  ID=="DDD")) + geom_line(aes(Date.time, P, group=ID, colour=ID))

La funzione subset permette di definire la colonna di ricerca (nel nostro caso ID) e scegliere il criterio di scelta (nel nostro caso AAA e DDD). Attenzione sia alle doppie virgolette in cui è necessario richiudere il nome degli ID, il doppio uguale e soprattutto la pipe | che permette di scegliere fra nomi multipli in quella colonna. Le aggiunte group=ID e colour=ID permettono di aggiungere un ulteriore criterio di selezione, ovvero le linee che verranno visualizzate saranno anche distinte e colorate in base all’ID.

Ultima chicca: se abbiamo 100 diversi ID e vogliamo plottarli tutti tranne uno? Il trucchetto sta nel cambiare il doppio uguale con != che appunto significa diverso.

Grafici di serie temporali

Potrebbe capitare di dover plottare un valore in funzione del tempo per vedere se ci sono variazioni su scala temporale.

Per avere un risultato ottimale è necessario formattare la colonna che s riferisce al tempo in un formato adeguato. In altre parole dobbiamo far capire a R che i valori di quella colonna non sono numeri, bensì date.

Infatti se saltiamo questo passaggio il risultato fa schifo:
library(ggplot2)
qplot(Date.time, P, data = ccc, geom = "line")

11

Se invece facciamo capire a R che la colonna Date.time è una data (con tanto di tempo in ore, minuti e secondi) il risultato è decisamente migliore:
matrice$Date.time<-as.POSIXct(matrice$Date.time, "%Y/%m/%d %H:%M:%S", tz = "")
e rimandiamo
qplot(Date.time, P, data = ccc, geom = "line")
2
as.POSIXct infatti fa capire a R che abbiamo a che fare con delle date e non con dei valori. Sta a noi inserire i valori giusti nella funzione in modo che rispecchino il tipo di codifica della data. Infatti "%Y/%m/%d %H:%M:%S" significa che la colonna delle date è del tipo “2013/09/25 13:45:00”.

 

Modificare le etichette di un layer in QGIS

QGIS offre un modo molto efficace per etichettare un layer (punti, linee o poligoni che sia). Spostare a proprio piacimento le etichette e ruotarle richiede un piccolo passo ulteriore, ma il controllo sul layer sarà davvero totale!

Nell’immagine si vedono alcune città della Toscana come punti etichettate con il loro nome (per etichettare un vettore, basta cliccare sull’icona con le lettere abc e scegliere il campo con cui si desidera etichettare il layer). Entrando in modalità editing (cliccando sull’icona a forma di matita) si vede che le icone che permettono di interagire con le etichette sono grige, quindi spente.

1

Per poter spostare e ruotare le etichette dobbiamo creare delle nuove colonne che rispecchino le proprietà delle etichette. Praticamente creiamo 3 colonne: una coordinata X, una coordinata Y e una “coordinata” di rotazione.

Aprendo la tabella degli attributi del layer, attiviamo la modalità editing e clicchiamo sull’icona per creare una nuova colonna. Creiamo 3 colonne con i nomi xlab, ylab e rot_lab e scegliamo come tipo numerico decimale (come lunghezza e precisione stiamo larghi, vedi figura). Definire la colonna con valori reali e non interi è fondamentale: quando sposteremo le etichette, QGIS riempirà i campi delle colonne create in funzione delle coordinate dell’etichetta. Se obblighiamo QGIS a troncare il valore numerico rischiamo che l’etichetta sparisca dalla mappa. Salviamo e usciamo dalla modalità editing.

2

Fatto questo dobbiamo semplicemente entrare nella finestra di dialogo delle etichette e impostare come coordinata X la colonna xlab, come coordinata Y la colonna ylab e come rotazione la colonna rot_lab. Per farlo clicchiamo sull’icona accanto alla X, e scegliamo l’opzione field type: dall’elenco spuntiamo xlab. La stessa cosa va fatta anche per la coordinata Y e la rotazione.

3

Adesso siamo pronti per fare quello che vogliamo con ogni etichetta di ogni singolo punto! Selezionando il layer desiderato ed entrando in modalità editing (sempre cliccando sull’icona a forma di matita), si vede che le icone delle etichette che prima erano grige, ora sono attive. Basta cliccare su una di queste e spostare e ruotare l’etichetta che vogliamo.

4

 

 

 

Un modo artigianale per visualizzare direzioni di flusso

In una mappa che tratta di idrologia potrebbe essere necessario visualizzare la direzione del flusso, ovvero le linee che corrono perpendicolarmente alle isopieze. L’acqua tende infatti a spostarsi da un gradiente maggiore a uno inferiore con il minimo sforzo.

Il metodo è artigianale ma il risultato è gradevole.

1

Per prima cosa è necessario essere in possesso di un layer di linee (scaricandolo o estraendo le curve di livello da un raster DTM in QGIS). In ogni caso il metodo funziona con qualsiasi tipo di linea. Come vedremo infatti è solamente una questione di simbologia.

Bisogna semplicemente assegnare una doppia etichettatura al layer delle linee: una che rappresenti la linea in sè (ovvero la linea blu nella figura sopra) e l’altra che invece rappresenta le frecce (ovvero le direzioni di flusso).

Apriamo la finestra delle proprietà del layer (doppio click oppure click destro -> Proprietà) e andiamo nella scheda Stile. A questo punto è sufficiente aggiungere un simbolo al layer cliccando sul pulsante verde +.

11

Scegliamo come tipo di simbologia Linea di evidenziazione

22

Possiamo cambiare il tipo di marker scegliendo fra alcuni contenuti nel database di QGIS oppure possiamo anche aggiungere un’immagine a nostro piacimento.

33

Nei due menu appena elencati sono possibili infinite personalizzazioni: quanto distanti devono essere le frecce, il colore, il bordo, la dimensione e chi più ne ha più ne metta…

Unica nota “negativa” non è possibile scegliere l’orientamento delle frecce per ogni linea. In linea generale se volessimo ruotare le frecce nella direzione opposta basta modificare l’opzione angolo, ma questa agisce su tutte le frecce contemporaneamente. Quindi se ci sono alcune linee dove le frecce hanno la direzione sbagliata, selezionate solo quelle linee e salvatele come un altro shapefile. In questo modo, una volta che lo avete ricaricato, potete agire sull’orientamento delle frecce solamente di questo layer.