studio - superponer graficas en r
Llame a la función similar a la aplicación en cada fila de marco de datos con múltiples argumentos de cada fila (10)
Nueva respuesta con el paquete dplyr
Si la función que desea aplicar está vectorizada, puede usar la función mutate
del paquete dplyr
:
> library(dplyr)
> myf <- function(tens, ones) { 10 * tens + ones }
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mutate(x, value = myf(tens, ones))
hundreds tens ones value
1 7 1 4 14
2 8 2 5 25
3 9 3 6 36
Respuesta anterior con paquete plyr
En mi humilde opinión, la herramienta más adecuada para la tarea es mdply
del paquete plyr
.
Ejemplo:
> library(plyr)
> x <- data.frame(tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
tens ones V1
1 1 4 14
2 2 5 25
3 3 6 36
Desafortunadamente, como señaló Bertjan Broeksema , este enfoque falla si no utiliza todas las columnas del marco de datos en la llamada mdply
. Por ejemplo,
> library(plyr)
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
Error in (function (tens, ones) : unused argument (hundreds = 7)
Tengo un marco de datos con múltiples columnas. Para cada fila en el marco de datos, quiero llamar a una función en la fila, y la entrada de la función está usando múltiples columnas de esa fila. Por ejemplo, digamos que tengo estos datos y este testFunc que acepta dos argumentos:
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b
Digamos que quiero aplicar este testFunc a las columnas x y z. Entonces, para la fila 1, quiero 1 + 5, y para la fila 2, quiero 2 + 6. ¿Hay alguna forma de hacerlo sin escribir un bucle for, tal vez con la familia de funciones apply?
Intenté esto:
> df[,c(''x'',''z'')]
x z
1 1 5
2 2 6
> lapply(df[,c(''x'',''z'')], testFunc)
Error in a + b : ''b'' is missing
Pero obtuve un error, alguna idea?
EDITAR: la función real a la que quiero llamar no es una suma simple, sino que es power.t.test. Usé a + b solo por ejemplo. El objetivo final es poder hacer algo como esto (escrito en pseudocódigo):
df = data.frame(
delta=c(delta_values),
power=c(power_values),
sig.level=c(sig.level_values)
)
lapply(df, power.t.test(delta_from_each_row_of_df,
power_from_each_row_of_df,
sig.level_from_each_row_of_df
))
donde el resultado es un vector de salidas para power.t.test para cada fila de df.
Aquí hay un enfoque alternativo. Es mas intuitivo
Un aspecto clave que creo que algunas de las respuestas no tuvieron en cuenta, lo que señalo para la posteridad, es aplicar () le permite hacer cálculos de filas fácilmente, pero solo para datos de matriz (todos los numéricos)
las operaciones en columnas son posibles aún para marcos de datos:
as.data.frame(lapply(df, myFunctionForColumn()))
Para operar en filas, hacemos la transposición primero.
tdf<-as.data.frame(t(df))
as.data.frame(lapply(tdf, myFunctionForRow()))
La desventaja es que creo que R hará una copia de su tabla de datos. Lo cual podría ser un problema de memoria. (Esto es realmente triste, porque programáticamente es simple que tdf sea solo un iterador del df original, lo que ahorra memoria, pero R no permite la referencia del puntero o del iterador).
Además, una pregunta relacionada es cómo operar en cada celda individual en un marco de datos.
newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()}))
La respuesta de @ user20877984 es excelente. Dado que lo resumieron mucho mejor que mi respuesta anterior, aquí está mi intento (posiblemente de mala calidad) de aplicar el concepto:
Usando do.call
de manera básica:
powvalues <- list(power=0.9,delta=2)
do.call(power.t.test,powvalues)
Trabajando en un conjunto completo de datos:
# get the example data
df <- data.frame(delta=c(1,1,2,2), power=c(.90,.85,.75,.45))
#> df
# delta power
#1 1 0.90
#2 1 0.85
#3 2 0.75
#4 2 0.45
lapply
la función power.t.test
a cada una de las filas de valores especificados:
result <- lapply(
split(df,1:nrow(df)),
function(x) do.call(power.t.test,x)
)
> str(result)
List of 4
$ 1:List of 8
..$ n : num 22
..$ delta : num 1
..$ sd : num 1
..$ sig.level : num 0.05
..$ power : num 0.9
..$ alternative: chr "two.sided"
..$ note : chr "n is number in *each* group"
..$ method : chr "Two-sample t test power calculation"
..- attr(*, "class")= chr "power.htest"
$ 2:List of 8
..$ n : num 19
..$ delta : num 1
..$ sd : num 1
..$ sig.level : num 0.05
..$ power : num 0.85
... ...
Muchas funciones ya son vectorización, por lo que no es necesario realizar iteraciones (ni for
bucles ni for
funciones *pply
). Su testFunc
es uno de esos ejemplos. Simplemente puede llamar:
testFunc(df[, "x"], df[, "z"])
En general, recomendaría probar primero estos enfoques de vectorización y ver si obtienen los resultados deseados.
Alternativamente, si necesita pasar múltiples argumentos a una función que no está vectorizada, mapply
puede ser lo que está buscando:
mapply(power.t.test, df[, "x"], df[, "z"])
Otros han señalado correctamente que mapply
está hecho para este propósito, pero (en aras de la exhaustividad) un método conceptualmente más simple es simplemente usar un bucle for
.
for (row in 1:nrow(df)) {
df$newvar[row] <- testFunc(df$x[row], df$z[row])
}
Puede aplicar apply
a un subconjunto de los datos originales.
dat <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
apply(dat[,c(''x'',''z'')], 1, function(x) sum(x) )
o si su función es solo suma use la versión vectorizada:
rowSums(dat[,c(''x'',''z'')])
[1] 6 8
Si quieres usar testFunc
testFunc <- function(a, b) a + b
apply(dat[,c(''x'',''z'')], 1, function(x) testFunc(x[1],x[2]))
EDITAR Para acceder a las columnas por nombre y no por índice, puede hacer algo como esto:
testFunc <- function(a, b) a + b
apply(dat[,c(''x'',''z'')], 1, function(y) testFunc(y[''z''],y[''x'']))
Si las columnas data.frame son de tipos diferentes, apply()
tiene un problema. Una sutileza sobre la iteración de filas es cómo apply(a.data.frame, 1, ...)
realiza la conversión de tipos implícitos a tipos de caracteres cuando las columnas son tipos diferentes; p.ej. un factor y una columna numérica. Aquí hay un ejemplo, usando un factor en una columna para modificar una columna numérica:
mean.height = list(BOY=69.5, GIRL=64.0)
subjects = data.frame(gender = factor(c("BOY", "GIRL", "GIRL", "BOY"))
, height = c(71.0, 59.3, 62.1, 62.1))
apply(height, 1, function(x) x[2] - mean.height[[x[1]]])
La resta falla porque las columnas se convierten a tipos de caracteres.
Una solución es convertir de nuevo la segunda columna a un número:
apply(subjects, 1, function(x) as.numeric(x[2]) - mean.height[[x[1]]])
Pero las conversiones se pueden evitar manteniendo separadas las columnas y usando mapply()
:
mapply(function(x,y) y - mean.height[[x]], subjects$gender, subjects$height)
mapply()
es necesario porque [[ ]]
no acepta un argumento vectorial. Entonces la iteración de la columna se puede hacer antes de la resta pasando un vector a []
, por un código un poco más feo:
subjects$height - unlist(mean.height[subjects$gender])
Un data.frame
es una list
, entonces ...
Para funciones vectorizadas, do.call
suele ser una buena apuesta. Pero los nombres de los argumentos entran en juego. Aquí se llama a su testFunc
con args xey en lugar de a y b. El ...
permite que args irrelevantes pasen sin causar un error:
do.call( function(x,z,...) testFunc(x,z), df )
Para funciones no vectorizadas , mapply
funcionará, pero necesita hacer coincidir el orden de los argumentos o nombrarlos explícitamente:
mapply(testFunc, df$x, df$z)
A veces, la apply
funcionará, como cuando todos los argumentos son del mismo tipo, por lo que coaccionan los datos. El data.frame
a una matriz no causa problemas al cambiar los tipos de datos. Tu ejemplo fue de este tipo.
Si su función debe ser llamada dentro de otra función en la cual todos los argumentos son pasados, hay un método mucho más resbaladizo que estos. Estudia las primeras líneas del cuerpo de lm()
si quieres seguir esa ruta.
Utilice mapply
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> mapply(function(x,y) x+y, df$x, df$z)
[1] 6 8
> cbind(df,f = mapply(function(x,y) x+y, df$x, df$z) )
x y z f
1 1 3 5 6
2 2 4 6 8
Vine aquí buscando el nombre de la función tidyverse , que sabía que existía. Agregando esto para (mi) futura referencia y entusiastas de purrrlyr:invoke_rows
( purrr:invoke_rows
en versiones anteriores).
Con la conexión a los métodos de estadísticas estándar como en la pregunta original, el paquete de broom probablemente sería útil.