Rozejrzałem się po StackOverflow, ale nie mogę znaleźć rozwiązania specyficznego dla mojego problemu, który obejmuje dołączanie wierszy do ramki danych R.
Inicjalizuję pustą 2-kolumnową ramkę danych w następujący sposób.
df = data.frame(x = numeric(), y = character())
Następnie moim celem jest iteracyjne przejrzenie listy wartości i dodanie wartości na końcu listy w każdej iteracji. Zacząłem od następującego kodu.
for (i in 1:10) {
df$x = rbind(df$x, i)
df$y = rbind(df$y, toString(i))
}
Ja również próbował funkcje c
, append
i merge
bez powodzenia. Daj mi znać, jeśli masz jakieś sugestie.
Odpowiedzi:
Aktualizacja
Nie wiedząc, co próbujesz zrobić, podzielę się jeszcze jedną sugestią: wstępnie przydziel wektory żądanego typu dla każdej kolumny, wstaw wartości do tych wektorów, a na końcu utwórz własne
data.frame
.Kontynuacja z Julian's
f3
(wstępnie przydzielonadata.frame
) jako najszybsza jak dotąd opcja, zdefiniowana jako:Oto podobne podejście, ale takie, w którym
data.frame
jest tworzony jako ostatni krok.microbenchmark
z pakietu „microbenchmark” da nam bardziej wszechstronny wgląd niżsystem.time
:f1()
(podejście poniżej) jest niewiarygodnie nieefektywne ze względu na to, jak często wywołujedata.frame
i ponieważ rosnący w ten sposób obiektów jest generalnie powolny w R.f3()
jest znacznie ulepszony dzięki wstępnej alokacji, aledata.frame
sama struktura może być tutaj częścią wąskiego gardła.f4()
próbuje ominąć to wąskie gardło, nie rezygnując z przyjętego podejścia.Oryginalna odpowiedź
To naprawdę nie jest dobry pomysł, ale jeśli chcesz to zrobić w ten sposób, myślę, że możesz spróbować:
Zwróć uwagę, że w Twoim kodzie jest jeszcze jeden problem:
stringsAsFactors
jeśli chcesz, aby znaki nie były konwertowane na czynniki. Posługiwać się:df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)
źródło
data.frame
ostatecznego rozmiaru, jakiego oczekujesz, i dodaniu wartości podczas[
ekstrakcji / wymiany.Porównajmy trzy proponowane rozwiązania:
Najlepszym rozwiązaniem jest wstępne przydzielenie miejsca (zgodnie z zamierzeniami w R). Następnym najlepszym rozwiązaniem jest użycie
list
, a najgorszym rozwiązaniem (przynajmniej w oparciu o te wyniki czasowe) wydaje się byćrbind
.źródło
df <- rbind(df, data.frame(x = i, y = toString(i)))
Załóżmy, że po prostu nie znasz wcześniej rozmiaru ramki data.frame. Może to być kilka rzędów lub kilka milionów. Trzeba mieć jakiś pojemnik, który będzie się dynamicznie rozrastał. Biorąc pod uwagę moje doświadczenie i wszystkie powiązane odpowiedzi w SO, przedstawiam 4 różne rozwiązania:
rbindlist
do data.frameSkorzystaj
data.table
z szybkiejset
obsługi i połącz ją z ręcznym podwojeniem stołu w razie potrzeby.Użyj
RSQLite
i dołącz do tabeli przechowywanej w pamięci.data.frame
własna zdolność do rozwijania i używania niestandardowego środowiska (które ma semantykę referencyjną) do przechowywania data.frame, aby nie było kopiowane po zwróceniu.Oto test wszystkich metod zarówno dla małej, jak i dużej liczby dołączonych wierszy. Z każdą metodą są powiązane 3 funkcje:
create(first_element)
która zwraca odpowiedni obiekt podkładowy zfirst_element
wstawioną.append(object, element)
który dołącza znakelement
do końca tabeli (reprezentowany przezobject
).access(object)
pobieradata.frame
ze wszystkimi wstawionymi elementami.rbindlist
do data.frameTo dość łatwe i proste:
data.table::set
+ w razie potrzeby ręczne podwojenie tabeli.Będę przechowywać prawdziwą długość tabeli w
rowcount
atrybucie.SQL powinien być zoptymalizowany pod kątem szybkiego wstawiania rekordów, więc początkowo miałem duże nadzieje na
RSQLite
rozwiązanieTo jest po prostu skopiuj i wklej odpowiedź Karstena W. w podobnym wątku.
data.frame
własne dołączanie wierszy + środowisko niestandardowe.Zestaw testów:
Dla wygody użyję jednej funkcji testowej, aby objąć je wszystkie połączeniami pośrednimi. (Sprawdziłem: używanie
do.call
zamiast bezpośredniego wywoływania funkcji nie powoduje, że kod działa dłużej).Zobaczmy wydajność dla n = 10 wstawień.
Dodałem również funkcje „placebo” (z sufiksem
0
), które nic nie wykonują - tylko po to, aby zmierzyć narzut konfiguracji testowej.Dla rzędów 1E5 (pomiary wykonane na procesorze Intel (R) Core (TM) i7-4710HQ @ 2,50 GHz):
Wygląda na to, że sulution oparty na SQLite, chociaż odzyskuje pewną prędkość na dużych danych, nie jest nigdzie w pobliżu data.table + ręczny wzrost wykładniczy. Różnica wynosi prawie dwa rzędy wielkości!
Podsumowanie
Jeśli wiesz, że dodasz niewielką liczbę wierszy (n <= 100), użyj najprostszego możliwego rozwiązania: po prostu przypisz wiersze do data.frame za pomocą notacji nawiasowej i zignoruj fakt, że data.frame jest nie wypełnione wstępnie.
Do wszystkiego innego używaj
data.table::set
i rozwijaj data.table wykładniczo (np. Używając mojego kodu).źródło
Zaktualizuj za pomocą purrr, tidyr i dplyr
Ponieważ pytanie jest już przestarzałe (6 lat), w odpowiedziach brakuje rozwiązania z nowszymi pakietami tidyr i purrr. Dlatego dla osób pracujących z tymi pakietami chcę dodać rozwiązanie do poprzednich odpowiedzi - wszystkie, szczególnie interesujące.
Największą zaletą purrr i tidyr jest lepsza czytelność IMHO. purrr zastępuje lapply bardziej elastyczną rodziną map (), tidyr oferuje superintuicyjną metodę add_row - po prostu robi to, co mówi :)
To rozwiązanie jest krótkie i intuicyjne w czytaniu oraz stosunkowo szybkie:
Skaluje się prawie liniowo, więc dla wierszy 1e5 wydajność jest następująca:
co dałoby mu drugie miejsce zaraz po data.table (jeśli zignorujesz placebo) w benchmarku @Adama Ryczkowskiego:
źródło
add_row
. Na przykład:map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) })
.bind_rows(df, map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) }))
zamiast używaćadd_row
.Weźmy wektor „punkt”, który ma liczby od 1 do 5
point = c(1,2,3,4,5)
jeśli chcemy dodać liczbę 6 w dowolnym miejscu wewnątrz wektora, poniższe polecenie może się przydać
i) Wektory
new_var = append(point, 6 ,after = length(point))
ii) kolumny tabeli
new_var = append(point, 6 ,after = length(mtcars$mpg))
Polecenie
append
przyjmuje trzy argumenty:prosty...!! Przepraszamy w przypadku ...!
źródło
Bardziej ogólne rozwiązanie może wyglądać następująco.
Funkcja extensionDf () rozszerza ramkę danych o n wierszy.
Jako przykład:
źródło
Moje rozwiązanie jest prawie takie samo jak oryginalna odpowiedź, ale nie zadziałało.
Więc nadałem nazwy kolumnom i działa:
źródło