2011-06-28 15 views
7

Soy un usuario R inexperto y he tenido problemas con la función By() y agradecería su ayuda. La tarea es simple, tengo un conjunto de datos longitudinal (¿cómo declaro uno?) Y necesito calcular diferentes métricas por ID. Uno de los las métricas es un simple cambio de por ciento (que requiere un retraso, el ejemplo siguiente):Cálculo de% de cambios con By()

ID   Date   Temp  %Change 
AAA 1/1/2003 0.749881714 NA 
AAA 1/2/2003 0.666661576 -0.110977687 
AAA 1/3/2003 0.773079935 0.159628759 
AAA 1/4/2003 0.62902364 -0.186340751 
AAA 1/5/2003 0.733312374 0.165794619 
BBB 1/1/2003 0.707339766 NA 
BBB 1/2/2003 0.764986529 0.081497982 
BBB 1/3/2003 0.662201467 -0.134361925 
BBB 1/4/2003 0.774451765 0.169510798 
BBB 1/5/2003 0.50829093 -0.343676453 
CCC 1/1/2003 0.836836215 NA 
CCC 1/2/2003 0.837136823 0.00035922 
CCC 1/3/2003 0.809016624 -0.033590924 
CCC 1/4/2003 0.690277509 -0.146769685 
CCC 1/5/2003 0.796357069 0.153676686 

Intuitivamente I comprender el uso de por(), pero no han sido capaces de producir el resultado correcto (% Cambiar) usando un data.frame que contiene $ ID, $ Date y $ Temp. Cualquier sugerencia sobre cómo lograr el% de cambio deseado sería muy apreciada.

+0

Bienvenido a SO, Mateo. Esta es una pregunta realmente buena con buenos datos de muestra. – Andrie

+0

Supongo que fue para preguntar sobre la función 'by'. Las mayúsculas correctas (o "no" en este caso) son cruciales en R. –

Respuesta

8

Puede usar una combinación de plyr para manejar el grupo por operación en ID y quantmod tiene una función para el cambio de porcentaje llamado Delt.

require(plyr) 
require(quantmod) 

> ddply(dat, "ID", transform, DeltaCol = Delt(Temp)) 
    ID  Date  Temp X.Change Delt.1.arithmetic 
1 AAA 1/1/2003 0.7498817   NA    NA 
2 AAA 1/2/2003 0.6666616 -0.11097769  -0.1109776868 
3 AAA 1/3/2003 0.7730799 0.15962876  0.1596287574 
4 AAA 1/4/2003 0.6290236 -0.18634075  -0.1863407501 
5 AAA 1/5/2003 0.7333124 0.16579462  0.1657946178 
6 BBB 1/1/2003 0.7073398   NA    NA 
7 BBB 1/2/2003 0.7649865 0.08149798  0.0814979813 
8 BBB 1/3/2003 0.6622015 -0.13436192  -0.1343619242 
9 BBB 1/4/2003 0.7744518 0.16951080  0.1695107963 
10 BBB 1/5/2003 0.5082909 -0.34367645  -0.3436764522 
11 CCC 1/1/2003 0.8368362   NA    NA 
12 CCC 1/2/2003 0.8371368 0.00035922  0.0003592196 
13 CCC 1/3/2003 0.8090166 -0.03359092  -0.0335909235 
14 CCC 1/4/2003 0.6902775 -0.14676969  -0.1467696849 
15 CCC 1/5/2003 0.7963571 0.15367669  0.1536766860 

Alternativamente, puede omitir el bit plyr, calcular el delta para toda la hoja.de.datos y luego actualizar la primera fila para cada ID. Hay muchas buenas ideas para seleccionar la primera fila de un data.frame basado en un identificador here. Algo como esto probablemente funcionaría:

dat$Delta <- Delt(dat$Temp) 
dat[ diff(c(0,dat$ID)) != 0, 5] <- NA 

En una nota relacionada, si alguien puede explicar por qué no parece Delta a aceptar mi petición para darle un nombre de columna razonable, lo agradecería.

+0

+1 para el uso de quantmod – diliop

+1

Combo interesante. Pensé que uno tiene que tener una estructura xts para que funcione Quantmode.Además, me he estado preguntando cómo marcar el punto primero/último obs de un data.frame, el enlace es extremadamente útil. – Mateo

6

Desde el PO preguntó específicamente sobre el uso de by() que pensé en dar una respuesta del ilustra su uso.

En primer lugar, escribir una función que actúa sobre cada 'pieza' de la trama de datos:

myFun <- function(x){ 
n <- nrow(x) 
x$Change <- c(NA,diff(x$Temp)/head(x$Temp,n-1)) 
x 
} 

He utilizado las funciones de base diff para calcular las diferencias secuenciales en Temp y luego puesto que el vector resultante tiene longitud n-1, usamos head para dividir las diferencias por todos menos el último valor de temperatura. (Hice esto solo para trabajar head ya que es práctico, hay muchas otras maneras de hacerlo).

Entonces el by llamada:

by(dat,dat$ID,FUN=myFun) 

Si usted quiere poner todas las piezas juntas de nuevo, podemos utilizar do.call y rbind:

do.call(rbind,by(dat,dat$ID,FUN=myFun)) 
+0

+1, buena respuesta. Cada vez que intento usar las funciones de agregación en la base R, termino sintiendo que estoy jugando con un spork oxidado y aburrido. – Chase

+0

Gracias por la respuesta. No pensé en la función diff ... solo me estoy acostumbrando a do.call()! – Mateo

1

Su salida sugerida no es "cambio% "(pero en lugar de diferencia fraccionaria) mientras que esto ilustra un método que obtiene" porcentaje del original "utilizando el valor inicial como base para el cambio:

> dat$pctTemp <- unlist(
      tapply(dat$Temp, dat$ID, function(x) c(NA, 100*x[-1]/x[1])) 
         ) 
> dat 
    ID  Date  Temp pctTemp 
1 AAA 1/1/2003 0.7498817  NA 
2 AAA 1/2/2003 0.6666616 88.90223 
3 AAA 1/3/2003 0.7730799 103.09358 
4 AAA 1/4/2003 0.6290236 83.88305 
5 AAA 1/5/2003 0.7333124 97.79041 
6 BBB 1/1/2003 0.7073398  NA 
7 BBB 1/2/2003 0.7649865 108.14980 
8 BBB 1/3/2003 0.6622015 93.61858 
snipped 

Si desea cambiar el intervalo, se puede dividir diff (x) por el prceding valores

> dat$pctTemp <- unlist(tapply(dat$Temp, dat$ID, function(x) c(NA, 100*diff(x)/x[-length(x)])) ) 
> dat 
    ID  Date  Temp  pctTemp 
1 AAA 1/1/2003 0.7498817   NA 
2 AAA 1/2/2003 0.6666616 -11.09776868 
3 AAA 1/3/2003 0.7730799 15.96287574 
4 AAA 1/4/2003 0.6290236 -18.63407501 
5 AAA 1/5/2003 0.7333124 16.57946178 
6 BBB 1/1/2003 0.7073398   NA 
7 BBB 1/2/2003 0.7649865 8.14979813 
snipped 
+0

Comentarios y extensiones interesantes y útiles. ¡Muchas gracias Chase, Joran y DWin! – Mateo