J'essaye d'écrire une fonction pour accepter un data.frame ( x
) et un column
de celui-ci. La fonction effectue quelques calculs sur x et retourne plus tard un autre data.frame. Je suis bloqué sur la méthode des meilleures pratiques pour passer le nom de la colonne à la fonction.
Les deux exemples minimaux fun1
et fun2
ci - dessous produisent le résultat souhaité, étant capable d'effectuer des opérations sur x$column
, en utilisant max()
comme exemple. Cependant, les deux s'appuient sur l'apparence (du moins pour moi) inélégante
- appeler
substitute()
et éventuellementeval()
- la nécessité de passer le nom de la colonne comme vecteur de caractères.
fun1 <- function(x, column){
do.call("max", list(substitute(x[a], list(a = column))))
}
fun2 <- function(x, column){
max(eval((substitute(x[a], list(a = column)))))
}
df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")
Je voudrais pouvoir appeler la fonction comme fun(df, B)
, par exemple. Autres options que j'ai envisagées mais que je n'ai pas essayées:
- Passez
column
comme un entier du numéro de colonne. Je pense que cela éviteraitsubstitute()
. Idéalement, la fonction pourrait accepter l'un ou l'autre. with(x, get(column))
, mais, même si cela fonctionne, je pense que cela nécessiterait toujourssubstitute
- Utilisez
formula()
etmatch.call()
, avec lesquels je n'ai pas beaucoup d'expérience.
Sous-question : est-il do.call()
préférable à eval()
?
B
supposera que B est un objet lui-même.[[
solution était la seule qui fonctionnait pour moi.Cette réponse couvrira bon nombre des mêmes éléments que les réponses existantes, mais ce problème (passer les noms de colonnes aux fonctions) revient assez souvent pour que je souhaite qu'il y ait une réponse qui couvre les choses de manière un peu plus complète.
Supposons que nous ayons une trame de données très simple:
et nous aimerions écrire une fonction qui crée une nouvelle colonne
z
qui est la somme des colonnesx
ety
.Une pierre d'achoppement très courante ici est qu'une tentative naturelle (mais incorrecte) ressemble souvent à ceci:
Le problème ici est que cela
df$col1
n'évalue pas l'expressioncol1
. Il recherche simplement une colonnedf
littéralement appeléecol1
. Ce comportement est décrit dans?Extract
la section "Objets récursifs (de type liste)".La solution la plus simple et la plus souvent recommandée consiste simplement à passer de
$
à[[
et à transmettre les arguments de la fonction sous forme de chaînes:Ceci est souvent considéré comme une «meilleure pratique» car c'est la méthode la plus difficile à bousiller. Passer les noms de colonnes sous forme de chaînes est à peu près aussi clair que possible.
Les deux options suivantes sont plus avancées. De nombreux forfaits les plus populaires utilisent ce genre de techniques, mais leur utilisation bien nécessite plus de soins et de compétences, car ils peuvent introduire des complexités subtiles et des points imprévus d'échec. Cette section du livre Advanced R de Hadley est une excellente référence pour certains de ces problèmes.
Si vous voulez vraiment éviter à l'utilisateur de taper tous ces guillemets, une option peut être de convertir les noms de colonnes nus et sans guillemets en chaînes en utilisant
deparse(substitute())
:C'est, franchement, probablement un peu idiot, car nous faisons vraiment la même chose que dans
new_column1
, juste avec un tas de travail supplémentaire pour convertir les noms nus en chaînes.Enfin, si nous voulons vraiment avoir de la fantaisie, nous pourrions décider qu'au lieu de passer les noms de deux colonnes à ajouter, nous aimerions être plus flexibles et permettre d'autres combinaisons de deux variables. Dans ce cas, nous aurions probablement recours à l'utilisation
eval()
d'une expression impliquant les deux colonnes:Juste pour le plaisir, j'utilise toujours
deparse(substitute())
le nom de la nouvelle colonne. Ici, tous les éléments suivants fonctionneront:La réponse courte est donc essentiellement: passez les noms de colonnes data.frame sous forme de chaînes et utilisez
[[
pour sélectionner des colonnes uniques. Commencer à ne se plonger danseval
,substitute
etc. si vous savez vraiment ce que vous faites.la source
Personnellement, je pense que passer la colonne sous forme de chaîne est assez moche. J'aime faire quelque chose comme:
ce qui donnera:
Notez que la spécification d'un data.frame est facultative. vous pouvez même travailler avec les fonctions de vos colonnes:
la source
Une autre façon est d'utiliser l'
tidy evaluation
approche. Il est assez simple de passer des colonnes d'un bloc de données sous forme de chaînes ou de noms de colonnes nues. En savoir plustidyeval
ici .Utiliser les noms de colonnes comme chaînes
Utiliser des noms de colonnes nus
Créé le 01/03/2019 par le package reprex (v0.2.1.9000)
la source
En guise de réflexion supplémentaire, s'il est nécessaire de passer le nom de la colonne sans guillemets à la fonction personnalisée, cela
match.call()
pourrait peut -être être également utile dans ce cas, comme alternative àdeparse(substitute())
:S'il y a une faute de frappe dans le nom de la colonne, il serait plus sûr de s'arrêter avec une erreur:
Créé le 11/01/2019 par le package reprex reprex (v0.2.1)
Je ne pense pas que j'utiliserais cette approche car il y a plus de typage et de complexité que de simplement passer le nom de colonne cité comme indiqué dans les réponses ci-dessus, mais bien, c'est une approche.
la source