Cómo utilizar la función apply() o una función equivalente para realizar operaciones matemáticas en filas actuales y adyacentes de un marco de datos?
Estoy realizando operaciones matemáticas simples por columna en filas de un marco de datos que también implican acceder a filas de marcos de datos adyacentes y anteriores. Aunque el código a continuación funciona, es engorroso (al menos en lo que respecta a mi uso liberal de las funciones cbind()
y subset()
) y me pregunto si hay una forma más limpia de obtener los mismos resultados usando apply()
u otra súper función de R. En R base si es posible.
Estoy sumando y restando valores de columnas en cada fila del marco de datos (refiriéndome a las siguientes columnas, “plus1” + “plus2” – “minus” = “total”), y si el número de identificación es el mismo a medida que se avanza de una fila a la siguiente, se añade el plus1 de la fila anterior. Vea la siguiente ilustración:
id plus1 plus2 minus total [total explicado]
1 1 3 5 10 -2
2 2 4 5 9 0
3 3 8 5 8 5 [8 + 5 - 8 = 5, se ignora "plus1" en la fila 2 ya que "id" cambió entre las filas 2 y 3]
4 3 1 4 7 6 [1 + 4 - 7, + 8 de la columna "plus1" en la fila 3 ya que "id" es el mismo en las filas 3 y 4, = 6]
5 3 2 5 6 2 [2 + 5 - 6, + 1 de la columna "plus1" en la fila 4 ya que "id" es el mismo en las filas 4 y 5, = 2]
6 5 3 6 5 4 [3 + 6 - 5 = 4, se ignora "plus1" en la fila 5 ya que "id" cambió entre las filas 5 y 6]
Aquí está el código que usé para generar lo anterior:
data <- data.frame(id=c(1,2,3,3,3,5),
plus1=c(3,4,8,1,2,3),
plus2=c(5,5,5,4,5,6),
minus = c(10,9,8,7,6,5))
data <- cbind(data,
tmp1=(data[ ,"plus1"] +
data[ ,"plus2"] -
data[ ,"minus"]
)
)
grp <- with(rle(data$id), rep(seq_along(values), lengths))
data$tmp2 <- with(data,ave(plus1, grp, FUN = function(x) c(0, x[-length(x)])))
data <- cbind(data, total = round((data[ ,"tmp1"] + data[ ,"tmp2"]),2))
data <- subset(data, select = -c(tmp1,tmp2) )
data
Estoy buscando simplicidad en el mundo de apply()
porque estaré usando muchas derivaciones de este tipo en mi proyecto actual. Parece que estoy imitando a Excel en R, y así es.
davy.ai
Una solución potencial para simplificar el código utilizando la función
apply()
sería dividir el marco de datos por la columna “id” y aplicar una función a cada grupo. La función calcularía la columna “total” para cada fila dentro del grupo, teniendo en cuenta el valor “plus1” de la fila anterior si los valores “id” coinciden.Aquí hay un código de ejemplo que utiliza las funciones
lapply()
yReduce()
para dividir el marco de datos por “id” y aplicar la función de cálculo a cada grupo:“`{r}
Definir una función para calcular la columna “total” para cada fila dentro de un grupo
calc_total <- function(df) {
# Inicializar una variable para almacenar los valores de “total”
total <- numeric(nrow(df))
# Calcular “total” para la primera fila del grupo
total[1] <- df[1, “plus1”] + df[1, “plus2”] – df[1, “minus”]
# Calcular “total” para cada fila consecutiva, teniendo en cuenta “plus1” de la fila anterior si los valores “id” coinciden
for (i in 2:nrow(df)) {
if (df[i, “id”] == df[i-1, “id”]) {
total[i] <- df[i, “plus1”] + df[i, “plus2”] – df[i, “minus”] + df[i-1, “plus1”]
} else {
total[i] <- df[i, “plus1”] + df[i, “plus2”] – df[i, “minus”]
}
}
# Agregar la columna “total” al marco de datos y devolverlo
cbind(df, total = total)
}
Dividir el marco de datos por “id” y aplicar la función de cálculo a cada grupo usando lapply()
grouped_data <- lapply(split(data, data$id), calc_total)
Combinar los marcos de datos resultantes en un único marco de datos usando Reduce() y rbind()
final_data <- Reduce(rbind, grouped_data)
final_data
“`
Este código debería producir el mismo resultado que el código original utilizando
cbind()
ysubset()
, pero de una manera más concisa y legible.