Dodać obiekt do listy w R w czasie stałym, O(1)?
Jeśli mam jakąś listę R mylist
, możesz do niej dodać element obj
w następujący sposób:
mylist[[length(mylist)+1]] <- obj
Ale na pewno jest jakiś bardziej zwarty sposób. Kiedy byłem nowy w R, próbowałem napisać lappend()
tak:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
Ale oczywiście to nie działa ze względu na semantykę r wywołanie po nazwie (lst
jest skutecznie kopiowane podczas wywołania, więc zmiany na lst
nie są widoczne poza zakresem lappend()
. Wiem, że można zrobić hacking środowiska w funkcji R, aby dotrzeć poza zakres swojej funkcji i zmutować środowisko wywołujące, ale to wydaje się dużym młotkiem do napisania prostej funkcji dołączania.
Czy ktoś może zasugerować piękniejszy sposób na zrobienie tego? Punkty bonusowe, jeśli działa zarówno dla wektorów, jak i list.
16 answers
Jeśli jest to lista łańcuchów, wystarczy użyć funkcji c()
:
R> LL <- list(a="tom", b="dick")
R> c(LL, c="harry")
$a
[1] "tom"
$b
[1] "dick"
$c
[1] "harry"
R> class(LL)
[1] "list"
R>
To też działa na wektorach, więc czy dostanę punkty bonusowe?
Edit (2015-Feb-01): Ten post zbliża się w swoje piąte urodziny. Niektórzy czytelnicy powtarzają z nim wszelkie niedociągnięcia, więc za wszelką cenę Zobacz też niektóre z poniższych komentarzy. Jedna sugestia dla typów list
:
newlist <- list(oldlist, list(someobj))
Ogólnie rzecz biorąc, typy R mogą utrudniać posiadanie jednego i tylko jednego idiomu dla wszystkich typów i zastosowań.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-28 18:24:55
OP (w zaktualizowanej wersji pytania z kwietnia 2012) jest zainteresowany tym, czy istnieje sposób na dodanie do listy w stałym czasie, np. za pomocą kontenera C++ vector<>
. Najlepsza odpowiedź?) tutaj do tej pory pokazują tylko względne czasy wykonania dla różnych rozwiązań, biorąc pod uwagę problem o stałej wielkości, ale nie odnoszą się bezpośrednio do żadnego z różnych rozwiązań " algorytmicznej wydajności. Komentarze poniżej wiele odpowiedzi omawiają algorytmiczne efektywność niektórych rozwiązań, ale w każdym przypadku do tej pory (od kwietnia 2015) dochodzą do błędnych wniosków.
Algorytmiczna wydajność rejestruje charakterystykę wzrostu w czasie (czas wykonania) lub przestrzeni (ilość zużywanej pamięci) wraz ze wzrostem rozmiaru problemu. Przeprowadzenie testu wydajności dla różnych rozwiązań, biorąc pod uwagę problem o stałej wielkości, nie uwzględnia tempa wzrostu różnych rozwiązań. OP jest interesuje się tym, czy istnieje sposób dodawania obiektów do listy R w "zamortyzowanym stałym czasie". Co to znaczy? Aby wyjaśnić, najpierw opiszę "stały czas": {]}
-
Stała lub O(1) wzrost:
Jeśli czas potrzebny do wykonania danego zadania pozostaje taki sam jak rozmiar problemu podwaja się, to mówimy, że algorytm wykazuje stały CZAS wzrost, lub podany w notacji "Big O", wykazuje O (1) wzrost czasu. Kiedy OP mówi "amortyzowany" stały czas, on po prostu oznacza "na dłuższą metę"... np. jeśli wykonanie pojedynczej operacji czasami trwa znacznie dłużej niż zwykle(np. jeśli wstępnie przydzielony bufor jest wyczerpany i czasami wymaga zmiany rozmiaru do większego bufora), tak długo, jak długo długoterminowa średnia wydajność jest stała, nadal będziemy ją nazywać O (1).
Dla porównania opiszę również "czas liniowy" i " kwadrat czas": {]}
-
Liniowy lub O (n) wzrost:
Jeśli czas potrzebny do wykonania danego zadania podwaja się jako rozmiar problemu podwaja się , to mówimy, że algorytm wykazuje czas liniowy , lub O(n) wzrost.
-
Kwadratowy lub O (n2) wzrost:
Jeśli czas potrzebny na wykonanie danego zadania zwiększy się o kwadrat wielkości problemu , mówimy, że algorytm wykazuje Czas kwadratowy , lub O (n2) wzrost.
Istnieje wiele innych klas wydajności algorytmów; odraczam artykuł Wikipedii do dalszej dyskusji.
Dziękuję @CronAcronis za odpowiedź, ponieważ jestem nowy w R i miło było mieć w pełni skonstruowany blok kodu do wykonywania analizy wydajności różnych rozwiązań przedstawionych na tej stronie. Pożyczam jego kod do mojego analiza, którą powielam (zawiniętą w funkcję) poniżej:
library(microbenchmark)
### Using environment as a container
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(substitute(lab))]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}
runBenchmark <- function(n) {
microbenchmark(times = 5,
env_with_list_ = {
listptr <- new.env(parent=globalenv())
listptr$list <- NULL
for(i in 1:n) {envAppendList(listptr, i)}
listptr$list
},
c_ = {
a <- list(0)
for(i in 1:n) {a = c(a, list(i))}
},
list_ = {
a <- list(0)
for(i in 1:n) {a <- list(a, list(i))}
},
by_index = {
a <- list(0)
for(i in 1:n) {a[length(a) + 1] <- i}
a
},
append_ = {
a <- list(0)
for(i in 1:n) {a <- append(a, i)}
a
},
env_as_container_ = {
listptr <- new.env(parent=globalenv())
for(i in 1:n) {lPtrAppend(listptr, i, i)}
listptr
}
)
}
Wyniki opublikowane przez @ CronAcronis zdecydowanie wydają się sugerować, że metoda a <- list(a, list(i))
jest najszybsza, przynajmniej dla rozmiaru problemu 10000, ale wyniki dla pojedynczego rozmiaru problemu nie dotyczą wzrostu rozwiązania. W tym celu musimy przeprowadzić co najmniej dwa testy profilowania, o różnych rozmiarach problemów: [23]}
> runBenchmark(2e+3)
Unit: microseconds
expr min lq mean median uq max neval
env_with_list_ 8712.146 9138.250 10185.533 10257.678 10761.33 12058.264 5
c_ 13407.657 13413.739 13620.976 13605.696 13790.05 13887.738 5
list_ 854.110 913.407 1064.463 914.167 1301.50 1339.132 5
by_index 11656.866 11705.140 12182.104 11997.446 12741.70 12809.363 5
append_ 15986.712 16817.635 17409.391 17458.502 17480.55 19303.560 5
env_as_container_ 19777.559 20401.702 20589.856 20606.961 20939.56 21223.502 5
> runBenchmark(2e+4)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 534.955014 550.57150 550.329366 553.5288 553.955246 558.636313 5
c_ 1448.014870 1536.78905 1527.104276 1545.6449 1546.462877 1558.609706 5
list_ 8.746356 8.79615 9.162577 8.8315 9.601226 9.837655 5
by_index 953.989076 1038.47864 1037.859367 1064.3942 1065.291678 1067.143200 5
append_ 1634.151839 1682.94746 1681.948374 1689.7598 1696.198890 1706.683874 5
env_as_container_ 204.134468 205.35348 208.011525 206.4490 208.279580 215.841129 5
>
Przede wszystkim słowo o wartościach min/LQ/mean/median / UQ / max: ponieważ wykonujemy dokładnie to samo zadanie dla każdego z 5 biegów, w idealnym świecie możemy się spodziewać, że zajmie to dokładnie tyle samo czasu dla każdego biegu. Ale pierwsze uruchomienie jest zwykle tendencyjne w kierunku dłuższych czasów ze względu na fakt, że kod, który testujemy, nie jest jeszcze załadowany do pamięci podręcznej procesora. Po pierwszym uruchomieniu spodziewamy się, że czasy będą dość spójne, ale czasami nasz kod może zostać eksmitowany z pamięci podręcznej z powodu przerwań timer tick lub innych przerwań sprzętowych, które nie są związane z kod, który testujemy. Testując urywki kodu 5 razy, pozwalamy na załadowanie kodu do pamięci podręcznej podczas pierwszego uruchomienia, a następnie dajemy każdemu urywkowi 4 szanse na ukończenie bez ingerencji ze strony zdarzeń zewnętrznych. Z tego powodu i dlatego, że tak naprawdę uruchamiamy dokładnie ten sam kod w dokładnie tych samych warunkach wejściowych za każdym razem, rozważymy tylko czasy " min " jako wystarczające dla najlepszego porównania różnych opcji kodu.
Zauważ, że wybrałem do pierwszego uruchomienia z problemem wielkości 2000, a następnie 20000, więc mój rozmiar problemu wzrosła o współczynnik 10 od pierwszego uruchomienia do drugiego.
Wydajność Rozwiązania list
: O (1) (stały CZAS)
Spójrzmy najpierw na wzrost rozwiązania list
, ponieważ od razu możemy stwierdzić, że jest to najszybsze rozwiązanie w obu przebiegach profilowania: w pierwszym biegu wykonanie 2000 zadań "dołączania" zajęło 854 mikro sekund (0,854 milli sekund. W drugi bieg, zajęło 8.746 milisekund do wykonania 20000" Dołącz " zadań. Naiwny obserwator powiedziałby: "Ah, rozwiązanie list
wykazuje wzrost O (n), ponieważ gdy rozmiar problemu wzrósł o współczynnik dziesięciu, tak samo czas potrzebny na wykonanie testu." problem z tą analizą polega na tym, że OP chce szybkości wzrostu pojedynczego obiektu wstawiania, a nie szybkości wzrostu ogólnego problemu. Wiedząc o tym, jest jasne, że rozwiązanie list
zapewnia dokładnie to, co OP wants: metoda dodawania obiektów do listy w czasie O(1).
Wydajność innych rozwiązań
Żadne inne rozwiązania nie zbliżają się nawet do prędkości rozwiązania list
, ale i tak warto je zbadać: {]}
Większość innych rozwiązań wydaje się być O (n) w wydajności. Na przykład rozwiązanie by_index
, bardzo popularne rozwiązanie oparte na częstotliwości, z jaką znajduję je w innych postach SO, zajęło 11.6 milisekund, aby dołączyć 2000 obiektów i 953 milisekundy, aby dołączyć dziesięć razy więcej obiektów. Całkowity czas problemu wzrósł o współczynnik 100, więc naiwny obserwator może powiedzieć "Ah, rozwiązanie by_index
wykazuje O(n2) wzrost, ponieważ gdy rozmiar problemu wzrósł o współczynnik dziesięciu, czas potrzebny do wykonania testu wzrósł o współczynnik 100." jak poprzednio, analiza ta jest błędna, ponieważ OP jest zainteresowany wzrostem pojedynczego obiektu wstawiania. Jeśli podzielimy ogólny wzrost czasu przez problem przyrost wielkości, stwierdzamy, że przyrost czasu dodawania obiektów zwiększył się tylko o współczynnik 10, a nie o współczynnik 100, co odpowiada wzrostowi wielkości problemu, więc rozwiązaniem by_index
Jest O (n). Nie ma na liście rozwiązań, które wykazują O (n2) wzrost do dodawania jednego obiektu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 21:22:00
W innych odpowiedziach, tylko podejście list
powoduje dodanie O(1), ale skutkuje głęboko zagnieżdżoną strukturą listy, a nie zwykłą pojedynczą listą. Użyłem poniższych datastructures, obsługują one dodawania O(1) (amortized) i pozwalają na konwersję wyniku z powrotem do zwykłej listy.
expandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
capacity <<- capacity * 2
}
methods$add <- function(val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
}
methods$as.list <- function() {
b <- buffer[0:length]
return(b)
}
methods
}
I
linkedList <- function() {
head <- list(0)
length <- 0
methods <- list()
methods$add <- function(val) {
length <<- length + 1
head <<- list(head, val)
}
methods$as.list <- function() {
b <- vector('list', length)
h <- head
for(i in length:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
return(b)
}
methods
}
Użyj ich w następujący sposób:
> l <- expandingList()
> l$add("hello")
> l$add("world")
> l$add(101)
> l$as.list()
[[1]]
[1] "hello"
[[2]]
[1] "world"
[[3]]
[1] 101
Te rozwiązania można rozszerzyć na pełne obiekty, które same obsługują operacje związane z listą al, ale że pozostanie jako ćwiczenie dla czytelnika.
Inny wariant dla nazwanej listy:
namedExpandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
names <- character(capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
names <<- c(names, character(capacity))
capacity <<- capacity * 2
}
methods$add <- function(name, val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
names[length] <<- name
}
methods$as.list <- function() {
b <- buffer[0:length]
names(b) <- names[0:length]
return(b)
}
methods
}
Benchmarki
Porównanie wydajności przy użyciu kodu @ phonetagger (który jest oparty na kodzie @ Cron Arconis). Dodałem również better_env_as_container
i trochę zmieniłem env_as_container_
. Oryginał env_as_container_
został uszkodzony i nie przechowuje wszystkich numerów.
library(microbenchmark)
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(lab)]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}
env2list <- function(env, len) {
l <- vector('list', len)
for (i in 1:len) {
l[[i]] <- env[[as.character(i)]]
}
l
}
envl2list <- function(env, len) {
l <- vector('list', len)
for (i in 1:len) {
l[[i]] <- env[[paste(as.character(i), 'L', sep='')]]
}
l
}
runBenchmark <- function(n) {
microbenchmark(times = 5,
env_with_list_ = {
listptr <- new.env(parent=globalenv())
listptr$list <- NULL
for(i in 1:n) {envAppendList(listptr, i)}
listptr$list
},
c_ = {
a <- list(0)
for(i in 1:n) {a = c(a, list(i))}
},
list_ = {
a <- list(0)
for(i in 1:n) {a <- list(a, list(i))}
},
by_index = {
a <- list(0)
for(i in 1:n) {a[length(a) + 1] <- i}
a
},
append_ = {
a <- list(0)
for(i in 1:n) {a <- append(a, i)}
a
},
env_as_container_ = {
listptr <- new.env(hash=TRUE, parent=globalenv())
for(i in 1:n) {lPtrAppend(listptr, i, i)}
envl2list(listptr, n)
},
better_env_as_container = {
env <- new.env(hash=TRUE, parent=globalenv())
for(i in 1:n) env[[as.character(i)]] <- i
env2list(env, n)
},
linkedList = {
a <- linkedList()
for(i in 1:n) { a$add(i) }
a$as.list()
},
inlineLinkedList = {
a <- list()
for(i in 1:n) { a <- list(a, i) }
b <- vector('list', n)
head <- a
for(i in n:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
},
expandingList = {
a <- expandingList()
for(i in 1:n) { a$add(i) }
a$as.list()
},
inlineExpandingList = {
l <- vector('list', 10)
cap <- 10
len <- 0
for(i in 1:n) {
if(len == cap) {
l <- c(l, vector('list', cap))
cap <- cap*2
}
len <- len + 1
l[[len]] <- i
}
l[1:len]
}
)
}
# We need to repeatedly add an element to a list. With normal list concatenation
# or element setting this would lead to a large number of memory copies and a
# quadratic runtime. To prevent that, this function implements a bare bones
# expanding array, in which list appends are (amortized) constant time.
expandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
capacity <<- capacity * 2
}
methods$add <- function(val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
}
methods$as.list <- function() {
b <- buffer[0:length]
return(b)
}
methods
}
linkedList <- function() {
head <- list(0)
length <- 0
methods <- list()
methods$add <- function(val) {
length <<- length + 1
head <<- list(head, val)
}
methods$as.list <- function() {
b <- vector('list', length)
h <- head
for(i in length:1) {
b[[i]] <- head[[2]]
head <- head[[1]]
}
return(b)
}
methods
}
# We need to repeatedly add an element to a list. With normal list concatenation
# or element setting this would lead to a large number of memory copies and a
# quadratic runtime. To prevent that, this function implements a bare bones
# expanding array, in which list appends are (amortized) constant time.
namedExpandingList <- function(capacity = 10) {
buffer <- vector('list', capacity)
names <- character(capacity)
length <- 0
methods <- list()
methods$double.size <- function() {
buffer <<- c(buffer, vector('list', capacity))
names <<- c(names, character(capacity))
capacity <<- capacity * 2
}
methods$add <- function(name, val) {
if(length == capacity) {
methods$double.size()
}
length <<- length + 1
buffer[[length]] <<- val
names[length] <<- name
}
methods$as.list <- function() {
b <- buffer[0:length]
names(b) <- names[0:length]
return(b)
}
methods
}
Wynik:
> runBenchmark(1000)
Unit: microseconds
expr min lq mean median uq max neval
env_with_list_ 3128.291 3161.675 4466.726 3361.837 3362.885 9318.943 5
c_ 3308.130 3465.830 6687.985 8578.913 8627.802 9459.252 5
list_ 329.508 343.615 389.724 370.504 449.494 455.499 5
by_index 3076.679 3256.588 5480.571 3395.919 8209.738 9463.931 5
append_ 4292.321 4562.184 7911.882 10156.957 10202.773 10345.177 5
env_as_container_ 24471.511 24795.849 25541.103 25486.362 26440.591 26511.200 5
better_env_as_container 7671.338 7986.597 8118.163 8153.726 8335.659 8443.493 5
linkedList 1700.754 1755.439 1829.442 1804.746 1898.752 1987.518 5
inlineLinkedList 1109.764 1115.352 1163.751 1115.631 1206.843 1271.166 5
expandingList 1422.440 1439.970 1486.288 1519.728 1524.268 1525.036 5
inlineExpandingList 942.916 973.366 1002.461 1012.197 1017.784 1066.044 5
> runBenchmark(10000)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 357.760419 360.277117 433.810432 411.144799 479.090688 560.779139 5
c_ 685.477809 734.055635 761.689936 745.957553 778.330873 864.627811 5
list_ 3.257356 3.454166 3.505653 3.524216 3.551454 3.741071 5
by_index 445.977967 454.321797 515.453906 483.313516 560.374763 633.281485 5
append_ 610.777866 629.547539 681.145751 640.936898 760.570326 763.896124 5
env_as_container_ 281.025606 290.028380 303.885130 308.594676 314.972570 324.804419 5
better_env_as_container 83.944855 86.927458 90.098644 91.335853 92.459026 95.826030 5
linkedList 19.612576 24.032285 24.229808 25.461429 25.819151 26.223597 5
inlineLinkedList 11.126970 11.768524 12.216284 12.063529 12.392199 13.730200 5
expandingList 14.735483 15.854536 15.764204 16.073485 16.075789 16.081726 5
inlineExpandingList 10.618393 11.179351 13.275107 12.391780 14.747914 17.438096 5
> runBenchmark(20000)
Unit: milliseconds
expr min lq mean median uq max neval
env_with_list_ 1723.899913 1915.003237 1921.23955 1938.734718 1951.649113 2076.910767 5
c_ 2759.769353 2768.992334 2810.40023 2820.129738 2832.350269 2870.759474 5
list_ 6.112919 6.399964 6.63974 6.453252 6.910916 7.321647 5
by_index 2163.585192 2194.892470 2292.61011 2209.889015 2436.620081 2458.063801 5
append_ 2832.504964 2872.559609 2983.17666 2992.634568 3004.625953 3213.558197 5
env_as_container_ 573.386166 588.448990 602.48829 597.645221 610.048314 642.912752 5
better_env_as_container 154.180531 175.254307 180.26689 177.027204 188.642219 206.230191 5
linkedList 38.401105 47.514506 46.61419 47.525192 48.677209 50.952958 5
inlineLinkedList 25.172429 26.326681 32.33312 34.403442 34.469930 41.293126 5
expandingList 30.776072 30.970438 34.45491 31.752790 38.062728 40.712542 5
inlineExpandingList 21.309278 22.709159 24.64656 24.290694 25.764816 29.158849 5
Dodałem linkedList
i expandingList
oraz inlined wersję obu. inlinedLinkedList
jest zasadniczo jest to Kopia list_
, ale konwertuje również zagnieżdżoną strukturę z powrotem do zwykłej listy. Poza tym różnica między wersjami inlined i non-inlined wynika z narzutu wywołań funkcji.
Wszystkie warianty expandingList
i linkedList
pokazują o(1) dołączanie wydajności, z czasem odniesienia skalowanie liniowo z liczbą elementów dołączanych. linkedList
jest wolniejsza niż expandingList
, a także widoczna jest funkcja wywołania overhead. Więc jeśli naprawdę potrzebujesz całej prędkości, jaką możesz uzyskać (i chcesz trzymać się kodu R), użyj wbudowanej wersji expandingList
.
Przyjrzałem się również implementacji C R i oba podejścia powinny być o(1) dołączane dla dowolnego rozmiaru, dopóki nie zabraknie pamięci.
Zmieniłem również env_as_container_
, Oryginalna wersja przechowywałaby każdy element w indeksie "i", nadpisując wcześniej Dołączony element. better_env_as_container
, które dodałem, jest bardzo podobny do env_as_container_
, ale bez deparse
rzeczy. Oba wykazują o (1) wydajność, ale mają one narzut, który jest nieco większy niż listy połączone / rozszerzające.
Pamięć overhead
W implementacji C R jest narzut 4 słów i 2 intów na alokowany obiekt. Podejście linkedList
przydziela jedną listę długości dwóch na każde dołączenie, w sumie (4*8+4+4+2*8=) 56 bajty na Dołączony element na komputerach 64-bitowych(z wyłączeniem nadmiarowych alokacji pamięci, więc prawdopodobnie bliżej 64 bajtów). Podejście expandingList
używa jednego słowa na Dołączony element, plus kopię podczas podwajania długość wektora, więc całkowite zużycie pamięci do 16 bajtów na element. Ponieważ pamięć znajduje się w jednym lub dwóch obiektach, overhead dla każdego obiektu jest nieistotny. Nie zajrzałem głęboko w env
użycie pamięci, ale myślę, że będzie bliżej linkedList
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-30 15:41:10
W Lispie zrobiliśmy to w ten sposób:
> l <- c(1)
> l <- c(2, l)
> l <- c(3, l)
> l <- rev(l)
> l
[1] 1 2 3
Chociaż to były "minusy", a nie tylko "c". Jeśli chcesz zacząć od listy empy, użyj l
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-04 06:00:43
Może chcesz coś takiego?
> push <- function(l, x) {
lst <- get(l, parent.frame())
lst[length(lst)+1] <- x
assign(l, lst, envir=parent.frame())
}
> a <- list(1,2)
> push('a', 6)
> a
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 6
To nie jest zbyt grzeczna funkcja (przypisywanie do parent.frame()
jest trochę niegrzeczne), ale IIUYC o to prosisz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-15 17:56:48
Jeśli podasz zmienną list jako cytowany ciąg znaków, możesz do niej dotrzeć z poziomu funkcji:
push <- function(l, x) {
assign(l, append(eval(as.name(l)), x), envir=parent.frame())
}
Więc:
> a <- list(1,2)
> a
[[1]]
[1] 1
[[2]]
[1] 2
> push("a", 3)
> a
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
>
Lub za dodatkowy kredyt:
> v <- vector()
> push("v", 1)
> v
[1] 1
> push("v", 2)
> v
[1] 1 2
>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-13 02:30:07
Dokonałem małego porównania wspomnianych tu metod.
n = 1e+4
library(microbenchmark)
### Using environment as a container
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(substitute(lab))]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}
microbenchmark(times = 5,
env_with_list_ = {
listptr <- new.env(parent=globalenv())
listptr$list <- NULL
for(i in 1:n) {envAppendList(listptr, i)}
listptr$list
},
c_ = {
a <- list(0)
for(i in 1:n) {a = c(a, list(i))}
},
list_ = {
a <- list(0)
for(i in 1:n) {a <- list(a, list(i))}
},
by_index = {
a <- list(0)
for(i in 1:n) {a[length(a) + 1] <- i}
a
},
append_ = {
a <- list(0)
for(i in 1:n) {a <- append(a, i)}
a
},
env_as_container_ = {
listptr <- new.env(parent=globalenv())
for(i in 1:n) {lPtrAppend(listptr, i, i)}
listptr
}
)
Wyniki:
Unit: milliseconds
expr min lq mean median uq max neval cld
env_with_list_ 188.9023 198.7560 224.57632 223.2520 229.3854 282.5859 5 a
c_ 1275.3424 1869.1064 2022.20984 2191.7745 2283.1199 2491.7060 5 b
list_ 17.4916 18.1142 22.56752 19.8546 20.8191 36.5581 5 a
by_index 445.2970 479.9670 540.20398 576.9037 591.2366 607.6156 5 a
append_ 1140.8975 1316.3031 1794.10472 1620.1212 1855.3602 3037.8416 5 b
env_as_container_ 355.9655 360.1738 399.69186 376.8588 391.7945 513.6667 5 a
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-20 12:22:39
Nie wiem, dlaczego myślisz, że twoja pierwsza metoda nie zadziała. Masz błąd w funkcji lappend: length (list) should be length (lst). Działa to dobrze i zwraca listę z dołączonym obj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-17 19:18:28
Spróbuj tej funkcji lappend
lappend <- function (lst, ...){
lst <- c(lst, list(...))
return(lst)
}
I inne propozycje z tej strony Dodaj named vector do listy
Pa.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:45
Myślę, że to, co chcesz zrobić, to w rzeczywistości przekazać przez odniesienie (wskaźnik) do funkcji -- utworzyć nowe środowisko (które są przekazywane przez odniesienie do funkcji) z dodaną do niego listą:
listptr=new.env(parent=globalenv())
listptr$list=mylist
#Then the function is modified as:
lPtrAppend <- function(lstptr, obj) {
lstptr$list[[length(lstptr$list)+1]] <- obj
}
Teraz tylko modyfikujesz istniejącą listę (Nie tworząc Nowej)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-24 16:02:11
Jest to prosty sposób dodawania elementów do listy R:
# create an empty list:
small_list = list()
# now put some objects in it:
small_list$k1 = "v1"
small_list$k2 = "v2"
small_list$k3 = 1:10
# retrieve them the same way:
small_list$k1
# returns "v1"
# "index" notation works as well:
small_list["k2"]
Lub programowo:
kx = paste(LETTERS[1:5], 1:5, sep="")
vx = runif(5)
lx = list()
cn = 1
for (itm in kx) { lx[itm] = vx[cn]; cn = cn + 1 }
print(length(lx))
# returns 5
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-11 04:10:43
W rzeczywistości istnieje podzbiór z funkcją c()
. Jeśli tak:
x <- list()
x <- c(x,2)
x = c(x,"foo")
Otrzymasz zgodnie z oczekiwaniami:
[[1]]
[1]
[[2]]
[1] "foo"
Ale jeśli dodasz macierz z x <- c(x, matrix(5,2,2)
, Twoja lista będzie miała kolejne 4 elementy wartości 5
!
Lepiej zrób:
x <- c(x, list(matrix(5,2,2))
Działa dla każdego innego obiektu i uzyskasz zgodnie z oczekiwaniami:
[[1]]
[1]
[[2]]
[1] "foo"
[[3]]
[,1] [,2]
[1,] 5 5
[2,] 5 5
W końcu twoja funkcja staje się:
push <- function(l, ...) c(l, list(...))
I działa dla każdego typu obiektu. Możesz być mądrzejszy i robić:
push_back <- function(l, ...) c(l, list(...))
push_front <- function(l, ...) c(list(...), l)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-24 20:38:47
> LL<-list(1:4)
> LL
[[1]]
[1] 1 2 3 4
> LL<-list(c(unlist(LL),5:9))
> LL
[[1]]
[1] 1 2 3 4 5 6 7 8 9
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-24 08:24:12
Jest to bardzo interesujące pytanie i mam nadzieję, że moja myśl poniżej może przyczynić się do rozwiązania go. Ta metoda daje płaską listę bez indeksowania, ale ma listę i unlist, aby uniknąć struktur zagnieżdżania. Nie jestem pewien prędkości, ponieważ Nie wiem, jak ją porównać.
a_list<-list()
for(i in 1:3){
a_list<-list(unlist(list(unlist(a_list,recursive = FALSE),list(rnorm(2))),recursive = FALSE))
}
a_list
[[1]]
[[1]][[1]]
[1] -0.8098202 1.1035517
[[1]][[2]]
[1] 0.6804520 0.4664394
[[1]][[3]]
[1] 0.15592354 0.07424637
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 16:50:37
Do walidacji użyłem kodu benchmarka dostarczonego przez @Cron. Jest jedna zasadnicza różnica (oprócz szybszego działania na nowszym procesorze i7): by_list
Teraz działa prawie tak dobrze, jak list_
:
Unit: milliseconds
expr min lq mean median uq
env_with_list_ 167.882406 175.969269 185.966143 181.817187 185.933887
c_ 485.524870 501.049836 516.781689 518.637468 537.355953
list_ 6.155772 6.258487 6.544207 6.269045 6.290925
by_index 9.290577 9.630283 9.881103 9.672359 10.219533
append_ 505.046634 543.319857 542.112303 551.001787 553.030110
env_as_container_ 153.297375 154.880337 156.198009 156.068736 156.800135
Dla odniesienia tutaj jest kod benchmarku skopiowany dosłownie z odpowiedzi @ Cron (na wypadek, gdyby później zmienił zawartość):
n = 1e+4
library(microbenchmark)
### Using environment as a container
lPtrAppend <- function(lstptr, lab, obj) {lstptr[[deparse(substitute(lab))]] <- obj}
### Store list inside new environment
envAppendList <- function(lstptr, obj) {lstptr$list[[length(lstptr$list)+1]] <- obj}
microbenchmark(times = 5,
env_with_list_ = {
listptr <- new.env(parent=globalenv())
listptr$list <- NULL
for(i in 1:n) {envAppendList(listptr, i)}
listptr$list
},
c_ = {
a <- list(0)
for(i in 1:n) {a = c(a, list(i))}
},
list_ = {
a <- list(0)
for(i in 1:n) {a <- list(a, list(i))}
},
by_index = {
a <- list(0)
for(i in 1:n) {a[length(a) + 1] <- i}
a
},
append_ = {
a <- list(0)
for(i in 1:n) {a <- append(a, i)}
a
},
env_as_container_ = {
listptr <- new.env(parent=globalenv())
for(i in 1:n) {lPtrAppend(listptr, i, i)}
listptr
}
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-24 22:31:24
Istnieje również list.append
z rlist
(link do dokumentacji )
require(rlist)
LL <- list(a="Tom", b="Dick")
list.append(LL,d="Pam",f=c("Joe","Ann"))
To bardzo proste i wydajne.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-15 17:07:27