Come tracciare la banda quantil (in r)
Domanda
Ho un file CSV che contiene linee per ogni evento (Java GC), sono interessato. L'oggetto è composto da un timestamp sottomissione (non equidistante) e alcune variabili. L'oggetto sembra questo:
gcdata <- read.table("http://bernd.eckenfels.net/view/gc1001.ygc.csv",header=TRUE,sep=",", dec=".")
start = as.POSIXct(strptime("2012-01-01 00:00:00", format="%Y-%m-%d %H:%M:%S"))
gcdata.date = gcdata$Timestamp + start
gcdata = gcdata[,2:7] # remove old date col
gcdata=data.frame(date=gcdata.date,gcdata)
str(gcdata)
.
Risultati in
'data.frame': 2997 obs. of 7 variables:
$ date : POSIXct, format: "2012-01-01 00:00:06" "2012-01-01 00:00:06" "2012-01-01 00:00:18" ...
$ Distance.s. : num 0 0.165 11.289 9.029 11.161 ...
$ YGUsedBefore.K.: int 1610619 20140726 20148325 20213304 20310849 20404772 20561918 21115577 21479211 21544930 ...
$ YGUsedAfter.K. : int 7990 15589 80568 178113 272036 429182 982841 1346475 1412181 1355412 ...
$ Promoted.K. : int 0 0 0 0 8226 937 65429 71166 62548 143638 ...
$ YGCapacity.K. : int 22649280 22649280 22649280 22649280 22649280 22649280 22649280 22649280 22649280 22649280 ...
$ Pause.s. : num 0.0379 0.022 0.0287 0.0509 0.109 ...
.
In questo caso mi interessa il tempo di pausa (in secondi). Voglio tracciare un diagramma, che mi mostrerà per ciascun (orologio da parete) un'ora fondamentalmente la media come una linea, il 2% e il 98% come corridoio grigio e il valore massimo (entro ogni ora) come una linea rossa. < / P >.
Ho fatto del lavoro, ma usare le funzioni del Q98 è brutto, dover usare più linee le istruzioni sembrano essere più sprechi, e non so come ottenere un'area grigia tra Q02 e Q98:
q02 <- function(x, ...) { x <- quantile(x,probs=c(0.2)) }
q98 <- function(x, ...) { x <- quantile(x,probs=c(0.98)) }
hours = droplevels(cut(gcdata$date, breaks="hours")) # can I have 2 hours?
plot(aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=max),ylim=c(0,2), col="red", ylab="Pause(s)", xlab="Days") # Is always black?
lines(aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=q98),ylim=c(0,2), col="green")
lines(aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=q02),ylim=c(0,2), col="green")
lines(aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=mean),ylim=c(0,2), col="blue")
.
Ora questo risulta in un grafico che ha punti neri come massimo, una linea blu come media oraria e una linea verde inferiore e superiore 0,2 + 0,98. Penso che sarebbe meglio leggibile avere un corridoio grigio, forse una linea massima (rossa) tratteggiata e in qualche modo fissa le etichette dell'asse. Eventuali suggerimenti? (Il file è disponibile sopra)
Soluzione
È bello vedere i compagni di Debian Old-Timer qui :) La tua risposta è già carina.Come mi capita di lavorare molto con la serie temporale, pensavo che avrei buttato in una variante usando l'eccellente Zoo e xts pacchetti.Quest'ultimo si basa sulla parte superiore del primo e ha, tra le altre cose, la funzione period.apply()
che possiamo utilizzare qui insieme alla funzione endpoints()
per ottenere gli aggregati di due ore su 24.
Quindi in cima avrei usato
library(zoo) # for zoo objects
library(xts) # for period.apply
gcdata <- read.table("http://bernd.eckenfels.net/view/gc1001.ygc.csv",
header=TRUE, sep=",", dec=".")
timestamps <- gcdata$Timestamp +
as.POSIXct(strptime("2012-01-01 00:00:00",
format="%Y-%m-%d %H:%M:%S"))
gcdatazoo <- zoo(gcdata[-1], order.by=timestamps) # as zoo object
.
per creare un oggetto zoo
.La tua funzione rimane:
plotAreaCorridor <- function(x, y, col.poly1="lightgray", col.poly2="gray",...) {
x.pol <- c(x, rev(x), x[1])
y.pol <- c(y[,1], rev(y[,5]),y[,1][1])
plot(x, y[,6]+1, type="n", ...)
polygon(x.pol, y.pol, col=col.poly1, lty=0)
x.pol <- c(x, rev(x), x[1])
y.pol <- c(y[,2], rev(y[,4]), y[,1][1])
polygon(x.pol, y.pol, col=col.poly2, lty=0)
lines(x, y[,3], col="blue") # median
lines(x, y[,6], col="red") # max
invisible(NULL)
}
.
E possiamo quindi semplificare un po ':
agg <- period.apply(gcdatazoo[,"Pause.s."], # to which data
INDEX=endpoints(gcdatazoo, "hours", k=2), # every 2 hours
FUN=function(x) quantile(x, # what fun.
probs=c(5,20,50,80,95,100)/100))
#v99 = q99(gcdata$Pause.s.) # what is q99 ?
v99 <- mean(agg[,5]) # mean of 95-th percentile?
plotAreaCorridor(index(agg), # use time index as x axis
coredata(agg), # and matrix part of zoo object as data
ylim=c(0,max(agg[,5])*1.5),
ylab="Quantiles of GC events",
main="NewPar Collection Activity")
abline(h=median(gcdatazoo[,"Pause.s."]), col="lightblue")
abline(h=v99, col="grey")
labeltxt <- paste("99%=",round(v99,digits=3),"s n=", nrow(gcdatazoo),sep="")
text(x=index(agg)[20], y=1.5*v99, labeltxt, col="grey", pos=3) # or legend()
.
che dà
L'asse è ora automatico e mostra i giorni feriali solo mentre l'estensione è inferiore alla settimana;Questo può essere sostituito se necessario.
Altri suggerimenti
Devi provare polygon
.Questo codice può essere utile:
y98 = aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=q98)
y02 = aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=q02)
ymax = aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=max)
ymin = aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=min)
ymean = aggregate(gcdata$Pause.s. ~ hours, data=gcdata, FUN=mean)
x = ymean[,1]
y1 = cbind(y02[,2], ymean[,2], y98[,2])
y2 = cbind(ymin[,2], ymean[,2], ymax[,2])
plotAreaCI(x,y2, ylim=c(0,2), xlab="time", ylab="variable")
plotAreaCI(x,y1, ylim=c(0,2), poly.col="blue", add=TRUE)
.
o
plotAreaCI(x,y2, ylim=c(0,2), xlab="time", ylab="variable", nice.x = TRUE)
plotAreaCI(x,y1, ylim=c(0,2), mean.lwd=2, poly.col="blue", add=TRUE)
.
Dove la funzione plotAreaCI
è definita da:
plotAreaCI = function(x, y, add=FALSE, nice.x = FALSE,
xlim=NULL, ylim=NULL,
mean.col="black", mean.lwd=1.5,
poly.col="gray", poly.lty=3,
xlab=NULL, ylab=NULL, main="",
...) {
isFactorX = isClass("factor", x)
if(isFactorX) {
x.label = x
x = as.numeric(x)
}
if(is.null(xlim)) xlim=range(x, na.rm=TRUE)
if(is.null(ylim)) ylim=range(y, na.rm=TRUE)
x.pol = c(x, rev(x), x[1])
y.pol = c(y[,1], rev(y[,3]), y[,1][3])
if(!add) {
plot.new()
plot.window(xlim=xlim, ylim=ylim, ...)
if(!nice.x & isFactorX) {
axis(1, at=x, labels=x.label)
} else {
xticks = axTicks(1)
if(isFactorX) {
xticks = xticks[xticks>=1]
axis(1, at=xticks, labels=x.label[xticks])
} else {
axis(1)
}
}
axis(2, las=1)
box()
title(xlab=xlab, ylab=ylab, main=main)
}
polygon(x.pol, y.pol, col=poly.col, lty=poly.lty)
lines(x, y[,2], col=mean.col, lwd=mean.lwd)
return(invisible())
}
. Questo è il codice che uso per tracciare la variazione temporale in analiti di laboratorio (pressione sanguigna sistolica in questa istanza):
SBP.qtr.mat <- aggregate(set1HLI$SBP,
list( year(set1HLI$Drawdt)+0.25* quarter(set1HLI$Drawdt)),
quantile, prob=c(0.1,0.25,0.5,0.75, 0.9,0.95, 0.975), na.rm=TRUE)
matplot(SBP.qtr.mat[,1], SBP.qtr.mat$x, type="pl")
.
Non dovrebbe essere troppo difficile adattarlo al tuo problema .... o puoi postare un esempio riproducibile con cui lavorare. Questo dà il 10 °, il 25 °, il 50 °, il 75 °, il 90 °, il 95 ° e il 97.5th percentili in un singolo dati. Frame e Matplot gestisce il tracciamento di un tale oggetto.
L'area grigia?, ... L'approccio abituale è quello di tracciare un poligono che esce sui limiti inferiori, "girando" a destra estremo e tornare indietro sul lato alto, e collegando indietro sul lato sinistro . Gli argomenti polygon
sono configurati come x, y
. C'è un argomento col
che si imposta su "Grey".
Per effettuare sequenze di "2 ore" a cui è possibile unire il tuo dataFrame o utilizzare con cut.POSIXt" as a breaks argument , there is the option of using multiples of time units with
seq.posixt`:
> seq(ISOdate(1910,1,1), ISOdate(1999,1,1), "10 years")
[1] "1910-01-01 12:00:00 GMT" "1920-01-01 12:00:00 GMT" "1930-01-01 12:00:00 GMT" "1940-01-01 12:00:00 GMT"
[5] "1950-01-01 12:00:00 GMT" "1960-01-01 12:00:00 GMT" "1970-01-01 12:00:00 GMT" "1980-01-01 12:00:00 GMT"
[9] "1990-01-01 12:00:00 GMT"
.
Non l'ho visto documentato ma puoi usare multipli di intervalli con cut.POSIXt
:
> str( cut( seq(ISOdate(1910,1,1), ISOdate(1999,1,1), "years"), "10 years") )
Factor w/ 9 levels "1910-01-01","1920-01-01",..: 1 1 1 1 1 1 1 1 1 1 ...
> str( cut( seq(ISOdate(1910,1,1), ISOdate(1999,1,1), "years"), "5 years") )
Factor w/ 18 levels "1910-01-01","1915-01-01",..: 1 1 1 1 1 2 2 2 2 2 ...
. Non sono attualmente arrivato al seguente script (è ancora necessario guardare la risposta più avanzata da DWIN).Ora sembra in qualche modo che stavo cercando, ma il codice è ancora piuttosto brutto (ad esempio non so come allineare l'etichetta e come ottenere corretti etichette Xlab):
plotAreaCorridor = function(x, y, col.poly1="lightgray", col.poly2="gray",...) {
x.pol = c(x, rev(x), x[1])
y.pol = c(y[,1], rev(y[,5]),y[,1][1])
plot(x, y[,6]+1, type="n", ...) # ugly since type="n" does not work for factor
polygon(x.pol, y.pol, col=col.poly1, lty=0)
x.pol = c(x, rev(x), x[1])
y.pol = c(y[,2], rev(y[,4]), y[,1][1])
polygon(x.pol, y.pol, col=col.poly2, lty=0)
lines(x, y[,3], col="blue") # median
lines(x, y[,6], col="red") # max
return(invisible())
}
pause = gcdata$Pause.s.
hours = droplevels(cut(gcdata$date, breaks="hours")) # can I have 2 hours?
agg = aggregate(pause ~ hours, FUN=quantile, probs=c(5,20,50,80,95,100)/100)
x = agg$hours
ys = agg$pause
q99 <- function(x, ...) { x <- quantile(x,probs=c(0.99)) }
v99 = q99(gcdata$Pause.s.)
vmed = median(gcdata$Pause.s.)
plotAreaCorridor(x, ys,ylim=c(0,v99*1.5))
abline(h=vmed, col="lightblue")
abline(h=v99, col="grey")
label=paste("99%=",round(v99,digits=3),"s n=", length(gcdata$date),sep="")
text(x=30, y=v99, label, col="grey", pos=3)
title("NewPar Collection Activity")
.