Existe-t-il un moyen d'obtenir le nom de l'index de la liste dans ma fonction lapply ()?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
J'ai demandé avant s'il est possible de conserver les noms d'index dans le lapply () retour liste, mais je ne sais toujours pas s'il y a un moyen facile de chercher le nom de chaque élément dans la fonction personnalisée. Je voudrais éviter d'appeler lapply sur les noms eux-mêmes, je préfère obtenir le nom dans les paramètres de la fonction.
Réponses:
Malheureusement,
lapply
ne vous donne que les éléments du vecteur que vous lui transmettez. La solution habituelle consiste à lui transmettre les noms ou les indices du vecteur au lieu du vecteur lui-même.Mais notez que vous pouvez toujours passer des arguments supplémentaires à la fonction, donc ce qui suit fonctionne:
Ici, j'utilise
lapply
sur les indices dex
, mais aussi je passex
et les noms dex
. Comme vous pouvez le voir, l'ordre des arguments de la fonction peut être n'importe quoi -lapply
passera dans l '"élément" (ici l'index) au premier argument non spécifié parmi les supplémentaires. Dans ce cas, je précisey
etn
, donc il nei
reste plus que ...Ce qui produit ce qui suit:
UPDATE Exemple plus simple, même résultat:
Ici, la fonction utilise la variable "globale"
x
et extrait les noms dans chaque appel.la source
y
au lieu dex
afin qu'il soit (espérons-le) plus clair que la fonction puisse appeler ses arguments n'importe quoi. Également changé les valeurs vectorielles en11,12,13
.Cela utilise essentiellement la même solution de contournement que Tommy, mais avec
Map()
, il n'est pas nécessaire d'accéder aux variables globales qui stockent les noms des composants de la liste.Ou, si vous préférez
mapply()
la source
mapply()
, notez l'SIMPLIFY
option, qui par défaut est true. Dans mon cas, cela a transformé le tout en une grande matrice alors que je voulais seulement appliquer une simple liste. Le définir surF
(à l'intérieur demapply()
) l'a fait fonctionner comme prévu.MISE À JOUR pour la version R 3.2
Clause de non-responsabilité: c'est un truc de piratage, et peut cesser de fonctionner dans les prochaines versions.
Vous pouvez obtenir l'index en utilisant ceci:
Remarque: le
[]
est nécessaire pour que cela fonctionne, car il incite R à penser que le symbolei
(résidant dans le cadre d'évaluation delapply
) peut avoir plus de références, activant ainsi la duplication paresseuse de celui-ci. Sans cela, R ne conservera pas de copies séparées dei
:D'autres astuces exotiques peuvent être utilisées, comme
function(x){parent.frame()$i+0}
oufunction(x){--parent.frame()$i}
.Impact sur la performance
La duplication forcée entraînera-t-elle une perte de performances? Oui! voici les benchmarks:
Conclusion
Cette réponse montre simplement que vous ne devriez PAS utiliser ceci ... Non seulement votre code sera plus lisible si vous trouvez une autre solution comme celle de Tommy ci-dessus, et plus compatible avec les versions futures, vous risquez également de perdre les optimisations pour lesquelles l'équipe principale a travaillé dur. développer!
Astuces des anciennes versions, ne fonctionnant plus:
Résultat:
Explication:
lapply
crée des appels du formulaireFUN(X[[1L]], ...)
,FUN(X[[2L]], ...)
etc. Donc l'argument qu'il passe estX[[i]]
oùi
est l'index courant dans la boucle. Si nous obtenons cela avant qu'il ne soit évalué (c'est-à-dire si nous utilisonssubstitute
), nous obtenons l'expression non évaluéeX[[i]]
. C'est un appel à la[[
fonction, avec des argumentsX
(un symbole) eti
(un entier). Doncsubstitute(x)[[3]]
renvoie précisément cet entier.Ayant l'index, vous pouvez accéder aux noms de manière triviale, si vous l'enregistrez d'abord comme ceci:
Résultat:
Ou en utilisant cette deuxième astuce: :-)
(le résultat est le même).
Explication 2:
sys.call(1)
retournelapply(...)
, donc c'estsys.call(1)[[2]]
l'expression utilisée comme argument de liste pourlapply
. Passer ceci àeval
crée un objet légitime quinames
peut accéder. Tricky, mais ça marche.Bonus: une deuxième façon d'obtenir les noms:
Notez qu'il
X
s'agit d'un objet valide dans le cadre parent deFUN
, et fait référence à l'argument de liste delapply
, afin que nous puissions y accéder aveceval.parent
.la source
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
revient à 3. Pouvez-vous expliquer comment ce 3 a été choisi? et la raison de l'écart? Est-ce égal à la longueur de la liste, dans ce cas, 3. Désolé si c'est une question basique mais j'aimerais savoir comment l'appliquer dans un cas général.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
travaux ... Je vais vérifier ce qui se passe.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
ne fonctionne plus et donne une erreur,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
y a-t-il un moyen simple de résoudre ce problème?J'ai eu le même problème plusieurs fois ... J'ai commencé à utiliser une autre façon ... Au lieu d'utiliser
lapply
, j'ai commencé à utilisermapply
la source
Vous pouvez essayer d'utiliser
imap()
frompurrr
package.De la documentation:
Donc, vous pouvez l'utiliser de cette façon:
Ce qui vous donnera le résultat suivant:
la source
Bouclez simplement les noms.
la source
mylist
à l'intérieur de la fonction. Mieux encore à fairefunction(mylist, nm) ...
La réponse de Tommy s'applique aux vecteurs nommés mais j'ai eu l'idée que vous étiez intéressé par les listes. Et il semble qu'il faisait une fin autour parce qu'il faisait référence à "x" depuis l'environnement d'appel. Cette fonction utilise uniquement les paramètres qui ont été passés à la fonction et ne fait donc aucune hypothèse sur le nom des objets qui ont été passés:
la source
NULL
?! Donclapply(x, function(x) NULL)
donne la même réponse ...lapply
ajoute toujours les noms dex
au résultat par la suite .Ma réponse va dans le même sens que Tommy et caracals, mais évite d'avoir à sauvegarder la liste comme objet supplémentaire.
Résultat:
Cela donne la liste comme argument nommé à FUN (au lieu de lapply). lapply n'a qu'à itérer sur les éléments de la liste (attention à changer ce premier argument en lapply lors de la modification de la longueur de la liste).
Remarque: donner la liste directement à lapply comme argument supplémentaire fonctionne également:
la source
Les deux @caracals et @Tommy sont de bonnes solutions et ceci est un exemple incluant les
list
´s etdata.frame
´s.r
est unlist
delist
´s etdata.frame
´s (dput(r[[1]]
à la fin).L'objectif est de
unlist
toutes les listes, en mettant la séquence deslist
noms de ´s comme colonnes pour identifier le cas.Désélectionnez les listes mais pas les
data.frame
´s.Map
met la séquence de noms sous forme de colonne.Reduce
rejoindre tous lesdata.frame
´s.PS
r[[1]]
:la source
Disons que nous voulons calculer la longueur de chaque élément.
Si le but est simplement d'étiqueter les éléments résultants, alors
lapply(mylist,length)
ou ci-dessous fonctionne.Si le but est d'utiliser l'étiquette à l'intérieur de la fonction, alors
mapply()
est utile en bouclant sur deux objets; les éléments de liste et les noms de liste.la source
@ ferdinand-kraft nous a donné un excellent truc et nous dit ensuite que nous ne devrions pas l'utiliser parce qu'il n'est pas documenté et à cause de la surcharge de performance.
Je ne peux pas discuter beaucoup avec le premier point, mais j'aimerais noter que les frais généraux devraient rarement être un problème.
définissons les fonctions actives afin que nous n'ayons pas besoin d'appeler l'expression complexe,
parent.frame()$i[]
mais seulement.i()
, nous allons également créer.n()
pour accéder au nom, qui devrait fonctionner à la fois pour les fonctions de base et purrr (et probablement pour la plupart des autres également).Benchons maintenant une fonction simple qui colle les items d'un vecteur à leur index, en utilisant différentes approches (ces opérations peuvent bien sûr être vectorisées en utilisant
paste(vec, seq_along(vec))
mais ce n'est pas le but ici).Nous définissons une fonction de benchmarking et une fonction de traçage et représentons les résultats ci-dessous:
Créé le 15/11/2019 par le package reprex (v0.3.0)
La chute au début du premier graphique est un hasard, veuillez l'ignorer.
Nous voyons que la réponse choisie est en effet plus rapide, et pour un nombre décent d'itérations nos
.i()
solutions sont en effet plus lentes, la surcharge par rapport à la réponse choisie est environ 3 fois la surcharge d'utilisationpurrr::imap()
, et s'élève à environ, 25 ms pour 30k itérations, donc je perds environ 1 ms pour 1000 itérations, 1 seconde par million. C'est un petit coût pour la commodité à mon avis.la source
Écrivez simplement votre propre
lapply
fonction personnaliséeEnsuite, utilisez comme ceci:
la source