Utilice un valor de la fila anterior en un cálculo de tabla de datos R.


81

Quiero crear una nueva columna en un data.table calculado a partir del valor actual de una columna y el anterior de otra. ¿Es posible acceder a filas anteriores?

P.ej:

> DT <- data.table(A=1:5, B=1:5*10, C=1:5*100)
> DT
   A  B   C
1: 1 10 100
2: 2 20 200
3: 3 30 300
4: 4 40 400
5: 5 50 500
> DT[, D := C + BPreviousRow] # What is the correct code here?

La respuesta correcta debe ser

> DT
   A  B   C   D
1: 1 10 100  NA
2: 2 20 200 210
3: 3 30 300 320
4: 4 40 400 430
5: 5 50 500 540

Por lo general, establezco una clave para mis tablas de datos:DT <- data.table(A=..., key = "A")
— PatrickT

Respuestas:


103

Con shift()implementado en v1.9.6 , esto es bastante sencillo.

DT[ , D := C + shift(B, 1L, type="lag")]
# or equivalently, in this case,
DT[ , D := C + shift(B)]

De NEWS :

  1. La nueva función shift()implementa rápido lead/lagde vector , lista , data.frames o data.tables . Toma un typeargumento que puede ser "lag" (predeterminado) o "lead" . Permite un uso muy conveniente junto con :=o set(). Por ejemplo: DT[, (cols) := shift(.SD, 1L), by=id]. Échale un vistazo ?shiftpara obtener más información.

Consulte el historial para obtener respuestas anteriores.


¿Tiene .Nel número de fila actual o algo así? Siento preguntar aquí, pero parece que no puedo encontrarlo en los archivos de ayuda ...
— SlowLearner

7
@SlowLearner: también puede resultarle .Iútil, que contiene los índices de fila para las filas en el grupo actual.
— Steve Lianoglou

7
Utilice seq_len (.N - 1) en lugar de 1 :(. N-1). Esto evita problemas asociados con 1: 0.
— mnel

1
+1 para el .SDejemplo: estaba tratando de usar a lapplyy obteniendo resultados extravagantes. esto es mucho más sencillo.
— MichaelChirico

¿Dónde puedo encontrar un pdf actualizado con toda esta nueva información? Las viñetas oficiales 1.9.4 y los webminars no lo incluyen. Y las viñetas Rmd 1.9.5 no son cómodas y tampoco las incluyen.
— skan

43

Usando dplyrusted podría hacer:

mutate(DT, D = lag(B) + C)

Lo que da:

#   A  B   C   D
#1: 1 10 100  NA
#2: 2 20 200 210
#3: 3 30 300 320
#4: 4 40 400 430
#5: 5 50 500 540

22

Varias personas han respondido a la pregunta específica. Consulte el código a continuación para ver una función de propósito general que uso en situaciones como esta y que puede ser útil. En lugar de simplemente obtener la fila anterior, puede ir tantas filas en el "pasado" o en el "futuro" como desee.

rowShift <- function(x, shiftLen = 1L) {
  r <- (1L + shiftLen):(length(x) + shiftLen)
  r[r<1] <- NA
  return(x[r])
}

# Create column D by adding column C and the value from the previous row of column B:
DT[, D := C + rowShift(B,-1)]

# Get the Old Faithul eruption length from two events ago, and three events in the future:
as.data.table(faithful)[1:5,list(eruptLengthCurrent=eruptions,
                                 eruptLengthTwoPrior=rowShift(eruptions,-2), 
                                 eruptLengthThreeFuture=rowShift(eruptions,3))]
##   eruptLengthCurrent eruptLengthTwoPrior eruptLengthThreeFuture
##1:              3.600                  NA                  2.283
##2:              1.800                  NA                  4.533
##3:              3.333               3.600                     NA
##4:              2.283               1.800                     NA
##5:              4.533               3.333                     NA

Esta es una respuesta brillante, me molesta que ya haya votado a favor de las otras respuestas porque esta es una respuesta mucho más general. De hecho, lo usaré en mi paquete geneorama (si no le importa).
— geneorama

Claro, adelante. Tenía la esperanza de conseguir un poco de tiempo libre y presentarlo como una solicitud de extracción al data.tablepaquete, pero por desgracia ...
— dnlbrky

Se shiftha agregado una función similar llamada a data.tablepartir de la versión 1.9.5. Vea la respuesta actualizada de @Arun.
— dnlbrky

12

Basado en el comentario de @Steve Lianoglou anterior, ¿por qué no solo:

DT[, D:= C + c(NA, B[.I - 1]) ]
#    A  B   C   D
# 1: 1 10 100  NA
# 2: 2 20 200 210
# 3: 3 30 300 320
# 4: 4 40 400 430
# 5: 5 50 500 540

Y evitar el uso seq_leno heado cualquier otra función.


2
Agradable, sin embargo, esto no funcionaría si quisiera encontrar el anterior dentro de un grupo.
— Mateo

1
@Matthew, tienes razón. Si estuviera subconjunto por grupo, reemplazaría .Iconseq_len(.N)
— Gary Weissman

9

Siguiendo la solución de Arun, se pueden obtener resultados similares sin hacer referencia a .N

> DT[, D := C + c(NA, head(B, -1))][]
   A  B   C   D
1: 1 10 100  NA
2: 2 20 200 210
3: 3 30 300 320
4: 4 40 400 430
5: 5 50 500 540

¿Hay alguna razón para preferir un método a otro? ¿O es simplemente una diferencia estética?
— Korone

Creo que en este escenario (es decir, donde .Nestá disponible) es principalmente una elección estética. No soy consciente de ninguna diferencia importante.
— Ryogi


1

Aquí está mi solución intuitiva:

#create data frame
df <- data.frame(A=1:5, B=seq(10,50,10), C=seq(100,500, 100))`
#subtract the shift from num rows
shift  <- 1 #in this case the shift is 1
invshift <- nrow(df) - shift
#Now create the new column
df$D <- c(NA, head(df$B, invshift)+tail(df$C, invshift))`

Aquí invshift, el número de filas menos 1 es 4. le nrow(df)proporciona el número de filas en un marco de datos o en un vector. Del mismo modo, si desea tomar valores aún anteriores, reste de nrow 2, 3, ... etc, y también coloque NA en consecuencia al principio.


-2

se puede hacer en bucle.

# Create the column D
DT$D <- 0
# for every row in DT
for (i in 1:length(DT$A)) {
  if(i==1) {
    #using NA at first line
    DT[i,4] <- NA
  } else {
    #D = C + BPreviousRow
    DT[i,4] <- DT[i,3] + DT[(i-1), 2]   
  }
}

Usando un for, incluso puede usar el valor anterior de la fila de esta nueva columna DT[(i-1), 4]

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.