J'ai un dataframe avec plusieurs colonnes. Pour chaque ligne de la trame de données, je souhaite appeler une fonction sur la ligne et l'entrée de la fonction utilise plusieurs colonnes de cette ligne. Par exemple, disons que j'ai ces données et ce testFunc qui accepte deux arguments:
> 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
Disons que je veux appliquer ce testFunc aux colonnes x et z. Donc, pour la ligne 1, je veux 1 + 5, et pour la ligne 2, je veux 2 + 6. Y a-t-il un moyen de faire cela sans écrire une boucle for, peut-être avec la famille de fonctions apply?
J'ai essayé ceci:
> 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
Mais vous avez une erreur, des idées?
EDIT: la fonction réelle que je veux appeler n'est pas une simple somme, mais c'est power.t.test. J'ai utilisé a + b juste à titre d'exemple. Le but final est de pouvoir faire quelque chose comme ça (écrit en pseudocode):
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
))
où le résultat est un vecteur de sorties pour power.t.test pour chaque ligne de df.
dplyr
chemin.Réponses:
Vous pouvez postuler
apply
à un sous-ensemble des données d'origine.ou si votre fonction est juste une somme, utilisez la version vectorisée:
Si vous souhaitez utiliser
testFunc
EDIT Pour accéder aux colonnes par nom et non par index, vous pouvez faire quelque chose comme ceci:
la source
apply
sur les big data.frames, cela copiera l'objet entier (pour le convertir en matrice). Cela posera également des problèmes si vous avez différents objets de classe dans le data.frame.A
data.frame
est unlist
, donc ...Pour les fonctions vectorisées,
do.call
c'est généralement un bon pari. Mais les noms des arguments entrent en jeu. Ici, votretestFunc
est appelé avec args x et y à la place de a et b. Le...
permet de passer des arguments non pertinents sans provoquer d'erreur:Pour les fonctions non vectorisées ,
mapply
cela fonctionnera, mais vous devez faire correspondre l'ordre des arguments ou les nommer explicitement:Cela
apply
fonctionnera parfois - comme lorsque tous les arguments sont du même type, ce qui contraint ledata.frame
à une matrice ne pose pas de problèmes en modifiant les types de données. Votre exemple était de ce genre.Si votre fonction doit être appelée dans une autre fonction dans laquelle tous les arguments sont passés, il existe une méthode beaucoup plus astucieuse que celles-ci. Étudiez les premières lignes du corps pour savoir
lm()
si vous souhaitez emprunter cette voie.la source
Vectorize
comme wrappermapply
pour vectoriser les fonctionsUtilisation
mapply
la source
Nouvelle réponse avec
dplyr
packageSi la fonction que vous souhaitez appliquer est vectorisée, vous pouvez utiliser la
mutate
fonction dudplyr
package:Ancienne réponse avec
plyr
packageÀ mon humble avis, l'outil le mieux adapté à la tâche est celui
mdply
duplyr
package.Exemple:
Malheureusement, comme l'a souligné Bertjan Broeksema , cette approche échoue si vous n'utilisez pas toutes les colonnes de la trame de données dans l'
mdply
appel. Par exemple,la source
dplyr::mutate_each
. Par exemple:iris %>% mutate_each(funs(half = . / 2),-Species)
.D'autres ont correctement souligné que
mapply
c'est fait à cet effet, mais (par souci d'exhaustivité) une méthode conceptuellement plus simple consiste simplement à utiliser unefor
boucle.la source
De nombreuses fonctions sont déjà vectorisées, et il n'y a donc pas besoin d'itérations (ni
for
boucles ni*pply
fonctions). Votre entestFunc
est un exemple. Vous pouvez simplement appeler:En général, je recommanderais d'essayer d'abord de telles approches de vectorisation et de voir si elles vous donnent les résultats escomptés.
Alternativement, si vous devez passer plusieurs arguments à une fonction qui n'est pas vectorisée,
mapply
peut-être ce que vous recherchez:la source
Voici une autre approche. C'est plus intuitif.
Un aspect clé que je pense que certaines des réponses n'ont pas pris en compte, que je souligne pour la postérité, est apply () vous permet de faire des calculs de lignes facilement, mais uniquement pour les données matricielles (toutes numériques)
les opérations sur les colonnes sont encore possibles pour les dataframes:
Pour opérer sur les lignes, nous faisons d'abord la transposition.
L'inconvénient est que je pense que R fera une copie de votre tableau de données. Ce qui pourrait être un problème de mémoire. (C'est vraiment triste, car il est simple par programme pour tdf d'être simplement un itérateur du df d'origine, économisant ainsi de la mémoire, mais R ne permet pas le référencement de pointeur ou d'itérateur.)
En outre, une question connexe est de savoir comment opérer sur chaque cellule individuelle dans une trame de données.
la source
Je suis venu ici à la recherche du nom de la fonction tidyverse - dont je savais qu'il existait. En ajoutant ceci pour (ma) future référence et pour les
tidyverse
passionnés:purrrlyr:invoke_rows
(purrr:invoke_rows
dans les anciennes versions).Avec la connexion aux méthodes de statistiques standard comme dans la question originale, le paquet balai aiderait probablement.
la source
La réponse de @ user20877984 est excellente. Puisqu'ils l'ont résumé bien mieux que ma réponse précédente, voici ma tentative (peut-être encore de mauvaise qualité) d'application du concept:
Utilisation
do.call
de base:Travailler sur un ensemble de données complet:
lapply
lapower.t.test
fonction à chacune des lignes de valeurs spécifiées:la source
2
, pourquoi ne pas simplement postuler1
?data.table
a également une façon très intuitive de le faire:L'
:=
opérateur peut être appelé entre parenthèses pour ajouter une nouvelle colonne à l'aide d'une fonctionIl est également facile d'accepter des constantes comme arguments en utilisant cette méthode:
la source
Si les colonnes data.frame sont de types différents,
apply()
a un problème. Une subtilité à propos de l'itération de ligne est de savoir commentapply(a.data.frame, 1, ...)
la conversion de type implicite en types de caractères lorsque les colonnes sont de types différents; par exemple. un facteur et une colonne numérique. Voici un exemple, en utilisant un facteur dans une colonne pour modifier une colonne numérique:La soustraction échoue car les colonnes sont converties en types de caractères.
Un correctif consiste à convertir en arrière la deuxième colonne en un nombre:
Mais les conversions peuvent être évitées en gardant les colonnes séparées et en utilisant
mapply()
:mapply()
est nécessaire car[[ ]]
n'accepte pas un argument vectoriel. Ainsi, l'itération de colonne pourrait être faite avant la soustraction en passant un vecteur à[]
, par un code un peu plus laid:la source
Une fonction vraiment intéressante pour cela est
adply
deplyr
, surtout si vous souhaitez ajouter le résultat au dataframe d'origine. Cette fonction et sa cousineddply
m'ont sauvé beaucoup de maux de tête et de lignes de code!Vous pouvez également appeler la fonction souhaitée.
la source