Tengo un marco de datos con varias 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 xyz. Entonces, para la fila 1 quiero 1 + 5, y para la fila 2 quiero 2 + 6. ¿Hay alguna manera de hacer esto 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 tiene error, alguna idea?
EDITAR: la función real 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.
dplyr
camino.Respuestas:
Puede aplicar
apply
a un subconjunto de los datos originales.o si su función es solo suma use la versión vectorizada:
Si quieres usar
testFunc
EDITAR Para acceder a las columnas por nombre y no por índice, puede hacer algo como esto:
fuente
apply
en big data.frames, copiará todo el objeto (para convertirlo en una matriz). Esto también causará problemas si tiene diferentes objetos de clase dentro del data.frame.A
data.frame
es unlist
, entonces ...Para las funciones vectorizadas
do.call
suele ser una buena apuesta. Pero los nombres de los argumentos entran en juego. AquítestFunc
se llama a tu con args x e y en lugar de a y b. El...
permite args irrelevantes que se pasarán sin causar un error:Para las funciones no vectorizadas ,
mapply
funcionará, pero debe coincidir con el orden de los argumentos o nombrarlos explícitamente:A veces
apply
funcionará, como cuando todos los argumentos son del mismo tipo, por lo que coaccionardata.frame
a una matriz no causa problemas al cambiar los tipos de datos. Su ejemplo fue de este tipo.Si su función se llamará dentro de otra función en la que se pasan todos los argumentos, hay un método mucho más ingenioso que estos. Estudie las primeras líneas del cuerpo de
lm()
si desea seguir esa ruta.fuente
Vectorize
como un contenedormapply
para vectorizar funcionesUtilizar
mapply
fuente
Nueva respuesta con
dplyr
paqueteSi la función que desea aplicar está vectorizada, puede usar la
mutate
función deldplyr
paquete:Antigua respuesta con
plyr
paqueteEn mi humilde opinión, la herramienta más adecuada para la tarea es
mdply
proviene delplyr
paquete.Ejemplo:
Desafortunadamente, como señaló Bertjan Broeksema , este enfoque falla si no utiliza todas las columnas del marco de datos en la
mdply
llamada. Por ejemplo,fuente
dplyr::mutate_each
. Por ejemplo:iris %>% mutate_each(funs(half = . / 2),-Species)
.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 usar unfor
bucle.fuente
Muchas funciones ya son vectorización, por lo que no hay necesidad de iteraciones (ni
for
bucles ni*pply
funciones). TutestFunc
es uno de esos ejemplos. Simplemente puedes llamar:En general, recomendaría probar primero dichos enfoques de vectorización y ver si le dan los resultados deseados.
Alternativamente, si necesita pasar varios argumentos a una función que no está vectorizada,
mapply
podría ser lo que está buscando:fuente
Aquí hay un enfoque alternativo. Es mas intuitivo.
Un aspecto clave que creo que algunas de las respuestas no tuvieron en cuenta, que señalo para la posteridad, es apply () le permite hacer cálculos de fila fácilmente, pero solo para datos de matriz (todos numéricos)
las operaciones en columnas aún son posibles para marcos de datos:
Para operar en filas, hacemos primero la transposición.
La desventaja es que creo que R hará una copia de su tabla de datos. Lo que podría ser un problema de memoria. (Esto es realmente triste, porque es programáticamente simple que tdf sea solo un iterador del df original, ahorrando así memoria, pero R no permite referencias de puntero o iterador).
Además, una pregunta relacionada es cómo operar en cada celda individual en un marco de datos.
fuente
Vine aquí buscando el nombre de la función tidyverse , que sabía que existía. Agregando esto para (mi) referencia futura y para
tidyverse
entusiastas: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 escoba probablemente ayudaría.
fuente
La respuesta de @ user20877984 es excelente. Como lo resumieron mucho mejor que mi respuesta anterior, aquí está mi intento (posiblemente aún de mala calidad) de aplicar el concepto:
Utilizando
do.call
de manera básica:Trabajando en un conjunto de datos completo:
lapply
lapower.t.test
función para cada una de las filas de valores especificados:fuente
2
, por qué no solo aplicar sobre1
?data.table
tiene una forma realmente intuitiva de hacer esto también:Se
:=
puede llamar al operador entre paréntesis para agregar una nueva columna usando una funciónTambién es fácil aceptar constantes como argumentos usando este método:
fuente
Si las columnas data.frame son de diferentes tipos,
apply()
tiene un problema. Una sutileza acerca de la iteración de fila es cómo laapply(a.data.frame, 1, ...)
conversión de tipo implícito 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:La resta falla porque las columnas se convierten en tipos de caracteres.
Una solución es volver a convertir la segunda columna a un número:
Pero las conversiones se pueden evitar manteniendo las columnas separadas y utilizando
mapply()
:mapply()
es necesario porque[[ ]]
no acepta un argumento vectorial. Entonces, la iteración de la columna podría hacerse antes de la resta pasando un vector a[]
, por un código un poco más feo:fuente
Una función muy bonito de esto es
adply
a partirplyr
, sobre todo si desea añadir el resultado a la trama de datos originales. ¡Esta función y su primoddply
me han ahorrado muchos dolores de cabeza y líneas de código!Alternativamente, puede llamar a la función que desee.
fuente