language - Aplicar mĂșltiples funciones a cada fila de un marco de datos
sapply() (4)
Creo que estás pensando demasiado complejo aquí. ¿Qué hay de malo con dos llamadas a apply()
separadas? Sin embargo, hay una manera mucho mejor de hacer lo que está haciendo aquí que no involucra llamadas en bucle / aplicar. Trataré estos por separado, pero la segunda solución es preferible ya que está verdaderamente vectorizada.
Versión de dos llamadas aplicadas.
Las primeras dos llamadas de aplicación separadas usan todas las funciones de Base R:
df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))
df1 <- transform(df1, MFE = apply(df1, 1, FindMFE), MAE = apply(df1, 1, FindMAE))
df1
Lo que da:
> df1
Bar1 Bar2 MFE MAE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
Bien, df1
dos veces las filas de df1
es quizás un poco ineficiente, pero incluso para los grandes problemas, ya ha pasado más tiempo pensando en hacer esto inteligentemente en una sola pasada de lo que ahorrará al hacerlo de esa manera.
Usando funciones vectorizadas pmax()
y pmin()
Entonces, una mejor manera de hacer esto es observar las funciones pmax()
y pmin()
y darse cuenta de que pueden hacer lo que cada una de las llamadas de apply(df1, 1, FindFOO()
estaban haciendo. Por ejemplo:
> (tmp <- with(df1, pmax(0, Bar1, Bar2, na.rm = TRUE)))
[1] 3 2 3 0 0 0
sería MFE de su pregunta. Es muy fácil trabajar con esto si tiene dos columnas y son Bar1
y Bar2
o las primeras 2 columnas de df1
, siempre. Pero no es muy general; ¿Qué sucede si tiene varias columnas sobre las que desea calcular esto, etc.? pmax(df1[, 1:2], na.rm = TRUE)
no hará lo que queremos:
> pmax(df1[, 1:2], na.rm = TRUE)
Bar1 Bar2
1 1 3
2 2 1
3 3 3
4 -3 -2
5 -2 -3
6 -1 -1
El truco para obtener una solución general utilizando pmax()
y pmin()
es usar do.call()
para organizar las llamadas a esas dos funciones para nosotros. Actualizando tus funciones para utilizar esta idea tenemos:
FindMFE2 <- function(x) {
MFE <- do.call(pmax, c(as.list(x), 0, na.rm = TRUE))
MFE[is.infinite(MFE)] <- 0
MFE
}
FindMAE2 <- function(x) {
MAE <- do.call(pmin, c(as.list(x), 0, na.rm = TRUE))
MAE[is.infinite(MAE)] <- 0
MAE
}
que dan:
> transform(df1, MFE = FindMFE2(df1), MAE = FindMAE2(df1))
Bar1 Bar2 MFE MAE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
y no una apply()
a la vista. Si desea hacer esto en un solo paso, ahora es mucho más fácil de ajustar:
FindMAEandMFE2 <- function(x){
cbind(MFE = FindMFE2(x), MAE = FindMAE2(x))
}
que se puede utilizar como:
> cbind(df1, FindMAEandMFE2(df1))
Bar1 Bar2 MFE MAE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
Cada vez que creo que entiendo sobre el trabajo con vectores, lo que parece ser un problema simple me da la vuelta. Mucho de leer y probar diferentes ejemplos no ha ayudado en esta ocasión. Por favor, dame de comer aquí ...
Quiero aplicar dos funciones personalizadas a cada fila de un marco de datos y agregar los resultados como dos nuevas columnas. Aquí está mi código de muestra:
# Required packages:
library(plyr)
FindMFE <- function(x) {
MFE <- max(x, na.rm = TRUE)
MFE <- ifelse(is.infinite(MFE ) | (MFE < 0), 0, MFE)
return(MFE)
}
FindMAE <- function(x) {
MAE <- min(x, na.rm = TRUE)
MAE <- ifelse(is.infinite(MAE) | (MAE> 0), 0, MAE)
return(MAE)
}
FindMAEandMFE <- function(x){
# I know this next line is wrong...
z <- apply(x, 1, FindMFE, FindMFE)
return(z)
}
df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))
df1 = transform(df1,
FindMAEandMFE(df1)
)
#DF1 should end up with the following data...
#Bar1 Bar2 MFE MAE
#1 3 3 0
#2 1 2 0
#3 3 3 0
#-3 -2 0 -3
#-2 -3 0 -3
#-1 -1 0 -1
Sería genial obtener una respuesta utilizando la biblioteca plyr y un enfoque más básico. Ambos me ayudarán en mi comprensión. Por supuesto, por favor, señale dónde me voy mal si es obvio. ;-)
Ahora volvamos a los archivos de ayuda para mí!
Edición: me gustaría una solución multivariable ya que los nombres de las columnas pueden cambiar y expandirse con el tiempo. También permite la reutilización del código en el futuro.
Hay muchas buenas respuestas aquí. Comencé esto mientras Gavin Simpson estaba editando, por lo que cubrimos un terreno similar. Lo que hacen los paralelos min y max (pmin y pmax) es exactamente para lo que estás escribiendo tus funciones. Puede ser un poco opaco lo que hace el 0 en pmax (0, Bar1, Bar2) pero esencialmente el 0 se recicla, así que es como hacerlo
pmax(c(0,0,0,0,0,0), Bar1, Bar2)
Eso tomará cada elemento de las tres cosas pasadas y encontrará el máximo de ellas. Por lo tanto, el máximo será 0 si fue negativo y logra gran parte de lo que hizo su declaración ifelse. Podría volver a escribir para obtener vectores y combinar cosas con funciones similares a las que estaba haciendo y eso podría hacerlo un poco más transparente. En este caso, solo pasaríamos el marco de datos a una nueva función paralela y rápida findMFE que funcionará con cualquier marco de datos numérico y obtendrá un vector.
findMFE <- function(dataf){
MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
}
MFE <- findMFE(df1)
Lo que hace esta función es agregar una columna adicional de 0s al marco de datos pasado y luego llamar a pmax pasando cada columna separada de df1 como si fuera una lista (los marcos de datos son listas, por lo que es fácil).
Ahora, observo que realmente desea corregir los valores de Inf en sus datos que no están en su ejemplo ... podríamos agregar una línea adicional a su función ...
findMFE <- function(dataf){
MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
ifelse(is.infinite(MFE), 0, MFE)
}
Ahora, ese es el uso adecuado de la función ifelse () en un vector. Lo hice de esa manera como ejemplo para ti, pero el uso de MFE [is.infinite (MFE)] por Gavin Simpson es <- 0 más eficiente. Tenga en cuenta que esta función findMFE no se utiliza en un bucle, simplemente pasa todo el marco de datos.
El findMAE comparable es ...
findMAE <- function(dataf){
MAE <- do.call( pmin, c(dataf, 0, na.rm = TRUE))
ifelse(is.infinite(MAE), 0, MAE)
}
y la función combinada es simplemente ...
findMFEandMAE <- function(dataf){
MFE <- findMFE(dataf)
MAE <- findMAE(dataf)
return(data.frame(MFE, MAE))
}
MFEandMAE <- encuentraMFEandMAE (df1) df1 <- cbind (df1, MFEandMAE)
Algunos consejos
Si tiene un escalar si la sentencia no usa ifelse (), use if () else. Es mucho más rápido en situaciones escalares. Y, tus funciones son escalares y estás tratando de vectorizarlas. ifelse () ya está vectorizado y se ejecuta muy rápido cuando se usa de esa manera pero mucho más lento que if () si no se usa escalar.
Además, si va a poner cosas en un bucle o aplicar una declaración, ponga tan poco como sea posible. Por ejemplo, en su caso, el ifelse () realmente tenía que sacarse del bucle y aplicarse después a todo el resultado de MFE.
Muestro tres alternativas de una sola línea:
- Usando
each
función deplyr
- Usando el
plyr
each
función con base R - Usando las funciones
pmin
ypmax
que son vectorizadas.
Solución 1: plyr y cada uno
El paquete plyr
define each
función que hace lo que usted quiere. De ?each
: Agregue múltiples funciones en una sola función. Esto significa que puede resolver su problema con una sola línea:
library(plyr)
adply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))
Bar1 Bar2 MAE MFE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
Solución 2: cada uno y base R
Puedes, por supuesto, usar each
con funciones básicas. A continuación le indicamos cómo puede usarlo con apply
: solo tenga en cuenta que debe transponer los resultados antes de agregarlos a su data.frame original.
library(plyr)
data.frame(df1,
t(apply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))))
Bar1 Bar2 MAE MFE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
Solución 3: utilizando funciones vectorizadas
Usando las funciones pmin
y pmax
, puede usar esta sola línea:
transform(df1, MFE=pmax(0, Bar1, Bar2), MAE=pmin(0, Bar1, Bar2))
Bar1 Bar2 MFE MAE
1 1 3 3 0
2 2 1 2 0
3 3 3 3 0
4 -3 -2 0 -3
5 -2 -3 0 -3
6 -1 -1 0 -1
Si realmente lo quieres, puedes:
FindMAEandMFE <- function(x){
t(apply(x, 1, function(currow){c(MAE=FindMAE(currow), MFE=FindMFE(currow))}))
}
(no probado, debería devolver una matriz con dos columnas (nombradas, creo) y tantas filas como tenía el data.frame). Ahora puedes hacer:
df1<-cbind(df1, FindMAEandMFE(df1))
Muy icky Por favor, presta atención al consejo de Gavin.