Gibt es eine Möglichkeit, den Listenindexnamen in meiner Funktion lapply () abzurufen?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
Ich habe zuvor gefragt, ob es möglich ist, die Indexnamen in der von lapply () zurückgegebenen Liste beizubehalten , aber ich weiß immer noch nicht, ob es eine einfache Möglichkeit gibt, jeden Elementnamen innerhalb der benutzerdefinierten Funktion abzurufen. Ich möchte vermeiden, lapply für die Namen selbst aufzurufen, ich möchte den Namen lieber in den Funktionsparametern erhalten.
Antworten:
Leider erhalten Sie
lapply
nur die Elemente des Vektors, den Sie übergeben. Die übliche Umgehung besteht darin, die Namen oder Indizes des Vektors anstelle des Vektors selbst zu übergeben.Beachten Sie jedoch, dass Sie der Funktion immer zusätzliche Argumente übergeben können, sodass Folgendes funktioniert:
Hier verwende ich
lapply
über die Indizes vonx
, übergebe aber auchx
und die Namen vonx
. Wie Sie sehen können, kann die Reihenfolge der Funktionsargumente beliebig sein -lapply
wird im "Element" (hier der Index) an das erste Argument übergeben, das nicht unter den zusätzlichen Argumenten angegeben ist. In diesem Fall gebe ichy
und ann
, also gibt es nuri
noch ...Welches erzeugt das folgende:
UPDATE Einfacheres Beispiel, gleiches Ergebnis:
Hier verwendet die Funktion die Variable "global"
x
und extrahiert die Namen in jedem Aufruf.quelle
y
statt ,x
so dass es (hoffentlich) deutlicher , dass die Funktionsargumente es ist etwas aufrufen können. Auch die Vektorwerte wurden in geändert11,12,13
.Dies verwendet im Grunde die gleiche Problemumgehung wie Tommy, aber mit
Map()
müssen Sie nicht auf globale Variablen zugreifen, in denen die Namen von Listenkomponenten gespeichert sind.Oder wenn Sie es vorziehen
mapply()
quelle
mapply()
dieSIMPLIFY
Option, die standardmäßig true ist. In meinem Fall machte das Ganze zu einer großen Matrix, als ich nur eine einfache Liste anwenden wollte. Wenn Sie es aufF
(innerhalb vonmapply()
) einstellen, wird es wie beabsichtigt ausgeführt.UPDATE für R Version 3.2
Haftungsausschluss: Dies ist ein hackiger Trick und funktioniert möglicherweise in den nächsten Versionen nicht mehr.
Sie können den Index folgendermaßen abrufen:
Hinweis:
[]
Dies ist erforderlich, damit dies funktioniert, da R zu der Annahme verleitet wird, dass das Symboli
(das sich im Bewertungsrahmen von befindetlapply
) möglicherweise mehr Referenzen enthält, wodurch die verzögerte Vervielfältigung aktiviert wird. Ohne sie wird R keine getrennten Kopien voni
:Andere exotische Tricks können verwendet werden, wie
function(x){parent.frame()$i+0}
oderfunction(x){--parent.frame()$i}
.Auswirkungen auf die Leistung
Wird die erzwungene Vervielfältigung zu Leistungseinbußen führen? Ja! Hier sind die Benchmarks:
Fazit
Diese Antwort zeigt nur, dass Sie dies NICHT verwenden sollten ... Nicht nur Ihr Code ist besser lesbar, wenn Sie eine andere Lösung wie die oben beschriebene von Tommy finden und mit zukünftigen Versionen kompatibler sind. Sie riskieren auch, die Optimierungen zu verlieren, an denen das Kernteam hart gearbeitet hat sich entwickeln!
Tricks alter Versionen, die nicht mehr funktionieren:
Ergebnis:
Erläuterung:
lapply
schafft Anrufe von der FormFUN(X[[1L]], ...)
,FUN(X[[2L]], ...)
etc. Also das Argument es passiert ist ,X[[i]]
woi
der aktuelle Index in der Schleife ist. Wenn wir dies erhalten, bevor es ausgewertet wird (dh wenn wir es verwendensubstitute
), erhalten wir den nicht bewerteten AusdruckX[[i]]
. Dies ist ein[[
Funktionsaufruf mit ArgumentenX
(ein Symbol) undi
(eine Ganzzahl). Sosubstitute(x)[[3]]
kehrt gerade diese ganze Zahl ist .Mit dem Index können Sie trivial auf die Namen zugreifen, wenn Sie ihn zuerst wie folgt speichern:
Ergebnis:
Oder mit diesem zweiten Trick ::-)
(Ergebnis ist das gleiche).
Erläuterung 2:
sys.call(1)
Gibt zurücklapply(...)
, sodass diessys.call(1)[[2]]
der Ausdruck ist, für den das Listenargument verwendet wirdlapply
. Wenn Sie dies an übergeben,eval
wird ein legitimes Objekt erstellt, dasnames
kann. Tricky, aber es funktioniert.Bonus: ein zweiter Weg, um die Namen zu bekommen:
Beachten Sie, dass dies
X
ein gültiges Objekt im übergeordneten Frame vonFUN
ist und auf das Listenargument von verweistlapply
, damit wir mit darauf zugreifen könneneval.parent
.quelle
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
gibt alle auf 3 zurück. Würden Sie erklären, wie diese 3 ausgewählt wurde? und Grund für die Diskrepanz? Entspricht es in diesem Fall der Länge der Liste? 3. Entschuldigung, wenn dies eine grundlegende Frage ist, Sie aber gerne wissen möchten, wie Sie dies in einem allgemeinen Fall anwenden können.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
Werke ... Ich werde überprüfen, was los ist.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
funktioniert nicht mehr und gibt einen Fehler aus,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
gibt es eine einfache Möglichkeit, dies zu beheben?Ich hatte oft das gleiche Problem ... Ich habe angefangen, einen anderen Weg zu verwenden ... Anstatt zu benutzen
lapply
, habe ich angefangen zu benutzenmapply
quelle
Sie könnten versuchen,
imap()
auspurrr
Paket zu verwenden.Aus der Dokumentation:
Sie können es also folgendermaßen verwenden:
Welches gibt Ihnen das folgende Ergebnis:
quelle
Schleifen Sie einfach die Namen ein.
quelle
mylist
innerhalb der Funktion fest zu codieren . Besser noch zu tunfunction(mylist, nm) ...
Tommys Antwort gilt für benannte Vektoren, aber ich hatte die Idee, dass Sie an Listen interessiert waren. Und es scheint, als würde er ein End-Around machen, weil er "x" aus der aufrufenden Umgebung referenzierte. Diese Funktion verwendet nur die Parameter, die an die Funktion übergeben wurden, und macht daher keine Annahmen über den Namen der übergebenen Objekte:
quelle
NULL
?! Alsolapply(x, function(x) NULL)
gibt die gleiche Antwort ...lapply
immer die Namen vonx
zum Ergebnis hinzugefügt werden .Meine Antwort geht in die gleiche Richtung wie die von Tommy und Caracals, aber ich muss die Liste nicht als zusätzliches Objekt speichern.
Ergebnis:
Dies gibt die Liste als benanntes Argument an FUN (statt an lapply). lapply muss nur über die Elemente der Liste iterieren (achten Sie darauf, dieses erste Argument in lapply zu ändern, wenn Sie die Länge der Liste ändern).
Hinweis: Wenn Sie die Liste als zusätzliches Argument direkt an lapply übergeben, funktioniert dies auch:
quelle
Sowohl @caracals als auch @Tommy sind gute Lösungen und dies ist ein Beispiel, einschließlich
list
´s unddata.frame
´s.r
ist einlist
vonlist
´s unddata.frame
´s (dput(r[[1]]
am Ende).Das Ziel besteht darin,
unlist
alle Listen zu erstellen und die Reihenfolge derlist
Namen als Spalten zu setzen, um den Fall zu identifizieren.Liste auflisten, aber nicht die
data.frame
.Map
Setzt die Namensfolge als Spalte.Reduce
mach mit bei allendata.frame
.PS
r[[1]]
:quelle
Angenommen, wir möchten die Länge jedes Elements berechnen.
Wenn das Ziel darin besteht, nur die resultierenden Elemente zu
lapply(mylist,length)
kennzeichnen, funktioniert dies oder darunter.Wenn das Ziel darin besteht, die Beschriftung innerhalb der Funktion zu verwenden,
mapply()
ist es nützlich, zwei Objekte zu durchlaufen. die Listenelemente und Listennamen.quelle
@ ferdinand-kraft hat uns einen tollen Trick gegeben und uns dann gesagt, wir sollten ihn nicht verwenden, weil er nicht dokumentiert ist und wegen des Leistungsaufwands.
Ich kann mit dem ersten Punkt nicht viel streiten, aber ich möchte darauf hinweisen, dass der Overhead selten ein Problem sein sollte.
Definieren wir aktive Funktionen, damit wir den komplexen Ausdruck nicht aufrufen müssen,
parent.frame()$i[]
sondern nur.i()
. Wir erstellen auch,.n()
um auf den Namen zuzugreifen, der sowohl für Basis- als auch für Purrr- Funktionen (und wahrscheinlich auch für die meisten anderen) funktionieren sollte.Lassen Sie uns nun eine einfache Funktion vergleichen, die die Elemente eines Vektors mit verschiedenen Ansätzen in ihren Index einfügt (diese Operationen können natürlich mit vektorisiert werden
paste(vec, seq_along(vec))
aber das ist hier nicht der Punkt).Wir definieren eine Benchmarking-Funktion und eine Plotfunktion und zeichnen die folgenden Ergebnisse auf:
Erstellt am 15.11.2019 durch das reprex-Paket (v0.3.0)
Der Abfall am Anfang des ersten Diagramms ist ein Zufall, bitte ignorieren Sie ihn.
Wir sehen, dass die gewählte Antwort tatsächlich schneller ist, und für eine anständige Anzahl von Iterationen sind unsere
.i()
Lösungen tatsächlich langsamer. Der Overhead im Vergleich zur gewählten Antwort beträgt etwa das Dreifache des Overheads für die Verwendung vonpurrr::imap()
und beträgt ungefähr 25 ms für 30.000 Iterationen. Ich verliere also ungefähr 1 ms pro 1000 Iterationen, 1 Sekunde pro Million. Das ist meiner Meinung nach ein kleiner Kostenfaktor.quelle
Schreiben Sie einfach Ihre eigene benutzerdefinierte
lapply
FunktionDann verwenden Sie wie folgt:
quelle