Dans une réponse à une autre question, @Marek a publié la solution suivante: https://stackoverflow.com/a/10432263/636656
dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L,
7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA, -20L), class = "data.frame")
`levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Qui produit en sortie:
[1] Generic Generic Bayer Bayer Advil Tylenol Generic Advil Bayer Generic Advil Generic Advil Tylenol
[15] Generic Bayer Generic Advil Bayer Bayer
Ce n'est que l'impression d'un vecteur, donc pour le stocker, vous pouvez faire ce qui est encore plus déroutant:
res <- `levels<-`(
factor(dat$product),
list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
)
Il s'agit clairement d'une sorte d'appel à la fonction des niveaux, mais je n'ai aucune idée de ce qui se fait ici. Quel est le terme pour ce genre de sorcellerie, et comment augmenter ma capacité magique dans ce domaine?
names<-
et[<-
.structure(...)
construction au lieu de simplementdata.frame(product = c(11L, 11L, ..., 8L))
? (S'il y a de la magie là-bas, j'aimerais aussi la manier!)"levels<-"
fonctionfunction (x, value) .Primitive("levels<-")
:, un peu commeX %in% Y
est une abréviation pour"%in%"(X, Y)
.Réponses:
Les réponses ici sont bonnes, mais il leur manque un point important. Laissez-moi essayer de le décrire.
R est un langage fonctionnel et n'aime pas muter ses objets. Mais il autorise les instructions d'affectation, en utilisant des fonctions de remplacement:
est équivalent à
L'astuce est que cette réécriture se fait par
<-
; ce n'est pas fait parlevels<-
.levels<-
est juste une fonction régulière qui prend une entrée et donne une sortie; il ne mute rien.Une conséquence de cela est que, selon la règle ci-dessus,
<-
doit être récursive:est
est
C'est assez beau que cette transformation purement fonctionnelle (jusqu'à la toute fin, là où se déroule la mission) équivaut à ce que serait une mission dans un langage impératif. Si je me souviens bien, cette construction dans les langages fonctionnels s'appelle une lentille.
Mais ensuite, une fois que vous avez défini des fonctions de remplacement comme
levels<-
, vous obtenez une autre aubaine inattendue: vous n'avez pas seulement la possibilité de faire des affectations, vous avez une fonction pratique qui prend en compte un facteur et donne un autre facteur avec différents niveaux. Il n'y a vraiment rien de "cession" à ce sujet!Donc, le code que vous décrivez utilise simplement cette autre interprétation de
levels<-
. J'avoue que le nomlevels<-
est un peu déroutant car il suggère une mission, mais ce n'est pas ce qui se passe. Le code met simplement en place une sorte de pipeline:Commencer avec
dat$product
Convertissez-le en facteur
Changer les niveaux
Conservez ça dans
res
Personnellement, je trouve que cette ligne de code est magnifique;)
la source
Pas de sorcellerie, c'est juste comment les fonctions de (sous) affectation sont définies.
levels<-
est un peu différent car il s'agit d'une primitive pour (sous) attribuer les attributs d'un facteur, pas les éléments eux-mêmes. Il existe de nombreux exemples de ce type de fonction:D'autres opérateurs binaires peuvent également être appelés comme ça:
Maintenant que vous le savez, quelque chose comme ça devrait vraiment vous épater:
la source
`levels<-`(foo,bar)
c'est la même chose quelevels(foo) <- bar
. En utilisant l'exemple de @ Marek:`levels<-`(as.factor(foo),bar)
est le même quefoo <- as.factor(foo); levels(foo) <- bar
.levels<-
c'est vraiment juste un raccourci pourattr<-(x, "levels") <- value
, ou du moins c'était probablement jusqu'à ce qu'il soit transformé en primitif et remis au code C.La raison de cette "magie" est que le formulaire "affectation" doit avoir une vraie variable sur laquelle travailler. Et le
factor(dat$product)
n'était assigné à rien.la source
within()
et d'transform()
appeler où l'objet ainsi modifié est renvoyé et attribué.Pour le code utilisateur, je me demande pourquoi de telles manipulations de langage sont utilisées ainsi? Vous demandez ce qu'est la magie et d'autres ont souligné que vous appelez la fonction de remplacement qui porte le nom
levels<-
. Pour la plupart des gens, c'est magique et c'est vraiment l'usage prévulevels(foo) <- bar
.Le cas d’utilisation que vous montrez est différent car il
product
n’existe pas dans l’environnement global, il n’existe donc que dans l’environnement local de l’appel.levels<-
Ainsi, le changement que vous souhaitez apporter ne persiste pas - il n’y a pas eu de réaffectation dedat
.Dans ces circonstances,
within()
c'est la fonction idéale à utiliser. Vous souhaiteriez naturellement écrireen R mais
product
n'existe bien sûr pas en tant qu'objet.within()
contourne ce problème car il configure l'environnement dans lequel vous souhaitez exécuter votre code R et évalue votre expression dans cet environnement. L'affectation de l'objet de retour de l'appel àwithin()
aboutit ainsi à la trame de données correctement modifiée.Voici un exemple (vous n'avez pas besoin de créer de nouveau
datX
- je le fais juste pour que les étapes intermédiaires restent à la fin)Qui donne:
J'ai du mal à voir à quel point des constructions comme celle que vous montrez sont utiles dans la majorité des cas - si vous voulez changer les données, changer les données, ne créez pas une autre copie et changez cela (ce que l'
levels<-
appel fait après tout ).la source