Czy istnieje sposób, aby uzyskać nazwę indeksu listy w mojej funkcji lapply ()?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
Pytałem wcześniej , jeśli to możliwe, aby zachować nazwy indeksu w lapply () zwróconej listy, ale nadal nie wiem, czy jest to prosty sposób, aby pobrać nazwę każdego elementu wewnątrz funkcji niestandardowej. Chciałbym uniknąć wywoływania lapply na samych nazwach, wolałbym raczej uzyskać nazwę w parametrach funkcji.
Odpowiedzi:
Niestety,
lapply
podaje tylko elementy wektora, przez który go przekazujesz. Zwykle można obejść ten problem, aby przekazać mu nazwy lub indeksy wektora zamiast samego wektora.Pamiętaj jednak, że zawsze możesz przekazać dodatkowe argumenty do funkcji, więc działa to:
Tutaj używam
lapply
indeksówx
, ale także przekazujęx
i nazwx
. Jak widać, kolejność argumentów funkcja może być cokolwiek -lapply
przejdzie w „element” (tu index) do pierwszego argumentu nie określony spośród tych dodatkowych. W tym przypadku określamy
in
tak zostało tylkoi
...Który daje następujące wyniki:
UPDATE Prostszy przykład, ten sam wynik:
Tutaj funkcja używa zmiennej „globalnej”
x
i wyodrębnia nazwy w każdym wywołaniu.źródło
y
zamiastx
, aby było (miejmy nadzieję) jaśniejsze, że funkcja może nazwać swoje argumenty dowolnie. Zmieniono także wartości wektora na11,12,13
.Zasadniczo wykorzystuje to to samo obejście, co Tommy, ale
Map()
nie ma potrzeby uzyskiwania dostępu do zmiennych globalnych, które przechowują nazwy składników listy.Lub, jeśli wolisz
mapply()
źródło
mapply()
zwróć uwagę naSIMPLIFY
opcję, która domyślnie ma wartość true. W moim przypadku sprawiło to, że całość stała się dużą matrycą, gdy chciałem zastosować tylko prostą listę. Ustawienie go naF
(wewnątrzmapply()
) sprawiło, że działał zgodnie z przeznaczeniem.UPDATE dla wersji R 3.2
Zastrzeżenie: to hacky trik i może przestać działać w następnych wydaniach.
Możesz uzyskać indeks za pomocą tego:
Uwaga:
[]
aby to zadziałało, wymagane jest, aby R pomyślał, że symboli
(znajdujący się w ramce ocenylapply
) może mieć więcej odniesień, aktywując w ten sposób leniwe jego powielanie. Bez tego R nie zachowa oddzielnych kopiii
:Można użyć innych egzotycznych sztuczek, takich jak
function(x){parent.frame()$i+0}
lubfunction(x){--parent.frame()$i}
.Wpływ na wydajność
Czy wymuszona duplikacja spowoduje utratę wydajności? Tak! oto wzorce:
Wniosek
Ta odpowiedź pokazuje tylko, że NIE powinieneś tego używać ... Nie tylko twój kod będzie bardziej czytelny, jeśli znajdziesz inne rozwiązanie, takie jak Tommy powyżej, i bardziej kompatybilne z przyszłymi wersjami, możesz również stracić optymalizacje, nad którymi główny zespół ciężko pracował rozwijać się!
Sztuczki starszych wersji, już nie działają:
Wynik:
Objaśnienie:
lapply
tworzy połączenia z postaciFUN(X[[1L]], ...)
,FUN(X[[2L]], ...)
itp Więc argument, że przechodzi toX[[i]]
gdziei
jest obecny indeks w pętli. Jeśli otrzymamy to przed oszacowaniem (tj. Jeśli używamysubstitute
), otrzymamy niezocenione wyrażenieX[[i]]
. To jest wywołanie[[
funkcji z argumentamiX
(symbol) ii
(liczbą całkowitą). Więcsubstitute(x)[[3]]
zwraca dokładnie tę liczbę całkowitą.Mając indeks, możesz uzyskać dostęp do nazw w trywialny sposób, jeśli zapiszesz go najpierw w ten sposób:
Wynik:
Lub używając tej drugiej sztuczki: :-)
(wynik jest taki sam).
Wyjaśnienie 2:
sys.call(1)
zwracalapply(...)
, więcsys.call(1)[[2]]
jest to wyrażenie używane jako argument listylapply
. Przekazanie tego doeval
tworzy legalny obiekt, do któregonames
można uzyskać dostęp. Podstępne, ale działa.Bonus: drugi sposób na zdobycie nazw:
Zauważ, że
X
jest to prawidłowy obiekt w ramce nadrzędnej programuFUN
i odwołuje się do argumentu listlapply
, więc możemy do niego dotrzeć za pomocąeval.parent
.źródło
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
zwraca wszystko jako 3. Czy mógłbyś wyjaśnić, jak wybrano tę 3? i powód rozbieżności? Czy jest równa długości listy, w tym przypadku 3. Przepraszam, jeśli jest to podstawowe pytanie, ale chciałbym wiedzieć, jak zastosować to w ogólnym przypadku.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
działa ... Sprawdzę, co się dzieje.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
już nie działa i wyświetla błąd,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
czy istnieje łatwy sposób na naprawienie tego?Wiele razy miałem ten sam problem ... Zacząłem używać innego sposobu ... Zamiast używać
lapply
, zacząłem używaćmapply
źródło
Możesz spróbować użyć
imap()
zpurrr
pakietu.Z dokumentacji:
Możesz więc używać tego w ten sposób:
Który da następujący wynik:
źródło
Po prostu zapętl nazwy.
źródło
mylist
w funkcji jest złą praktyką . Jeszcze lepiej zrobićfunction(mylist, nm) ...
Odpowiedź Tommy'ego dotyczy wektorów nazwanych, ale doszedłem do wniosku, że interesowały Cię listy. I wygląda na to, że robił koniec, ponieważ odnosił się do „x” ze środowiska wywołującego. Ta funkcja używa tylko parametrów, które zostały przekazane do funkcji, więc nie przyjmuje żadnych założeń dotyczących nazw przekazanych obiektów:
źródło
NULL
?! Więclapply(x, function(x) NULL)
daje tę samą odpowiedź ...lapply
zawsze dodaje nazwy odx
wynikowi później .Moja odpowiedź idzie w tym samym kierunku, co Tommy i karakale, ale unika konieczności zapisywania listy jako dodatkowego obiektu.
Wynik:
Daje to listę jako nazwany argument FUN (zamiast lapply). lapply musi tylko iterować po elementach listy (uważaj, aby zmienić ten pierwszy argument na lapply podczas zmiany długości listy).
Uwaga: Podanie listy bezpośrednio do lapply jako dodatkowego argumentu również działa:
źródło
Zarówno @caracals, jak i @Tommy są dobrymi rozwiązaniami, a to jest przykład obejmujący znaki
list
„idata.frame
”.r
jestlist
zlist
´s idata.frame
´s (dput(r[[1]]
na końcu).Celem jest
unlist
sprawdzenie wszystkich list, umieszczenie sekwencjilist
nazwisk jako kolumn w celu zidentyfikowania przypadku.Ukryj listy, ale nie
data.frame
´s.Map
umieszcza sekwencję nazw jako kolumnę.Reduce
dołącz do wszystkichdata.frame
.PS
r[[1]]
:źródło
Powiedzmy, że chcemy obliczyć długość każdego elementu.
Jeśli celem jest po prostu etykietowanie powstałych elementów, wtedy
lapply(mylist,length)
lub poniżej działa.Jeśli celem jest użycie etykiety wewnątrz funkcji,
mapply()
przydatne jest zapętlenie dwóch obiektów; elementy listy i nazwy list.źródło
@ ferdinand-kraft dał nam świetną sztuczkę, a następnie mówi nam, że nie powinniśmy go używać, ponieważ nie jest to udokumentowane i ze względu na narzut wydajności.
Nie mogę zbytnio dyskutować z pierwszą kwestią, ale chciałbym zauważyć, że koszty ogólne rzadko powinny stanowić problem.
zdefiniujmy aktywne funkcje, abyśmy nie musieli wywoływać złożonego wyrażenia,
parent.frame()$i[]
ale tylko.i()
, stworzymy również.n()
dostęp do nazwy, która powinna działać zarówno dla funkcjonałów podstawowych, jak i mruczących (i prawdopodobnie większości innych).Teraz przeanalizujmy prostą funkcję, która wkleja elementy wektora do ich indeksu, używając różnych podejść (te operacje można oczywiście wektoryzować przy użyciu,
paste(vec, seq_along(vec))
ale nie o to tutaj chodzi).Definiujemy funkcję porównawczą i funkcję kreślenia i wykreślamy wyniki poniżej:
Utworzono 15.11.2019 przez pakiet reprex (v0.3.0)
Spadek na początku pierwszego wykresu to fuks, zignoruj go.
Widzimy, że wybrana odpowiedź jest rzeczywiście szybsza, a dla przyzwoitej liczby iteracji nasze
.i()
rozwiązania są rzeczywiście wolniejsze, narzut w porównaniu z wybraną odpowiedzią jest około 3 razy większy niż narzut używaniapurrr::imap()
i wynosi około 25 ms dla 30k iteracji, więc tracę około 1 ms na 1000 iteracji, 1 sekundę na milion. Moim zdaniem to niewielki koszt dla wygody.źródło
Po prostu napisz własną
lapply
funkcję niestandardowąNastępnie użyj w ten sposób:
źródło