Tworzenie R dataframe wiersz po wierszu

107

Chciałbym skonstruować ramkę danych wiersz po wierszu w R. Dokonałem wyszukiwania i jedyne, co wpadłem, to sugestia, aby utworzyć pustą listę, zachować skalar indeksu listy, a następnie za każdym razem dodawać do listy jednowierszową ramkę danych i przesuń indeks listy o jeden do przodu. Wreszcie do.call(rbind,)na liście.

Chociaż to działa, wydaje się bardzo kłopotliwe. Czy nie ma łatwiejszego sposobu na osiągnięcie tego samego celu?

Oczywiście odnoszę się do przypadków, w których nie mogę użyć jakiejś applyfunkcji i jawnie muszę utworzyć ramkę danych wiersz po wierszu. Przynajmniej czy istnieje sposób pushna zakończenie listy zamiast jawnego śledzenia ostatnio używanego indeksu?

David B.
źródło
1
Możesz użyć append()[który prawdopodobnie powinien się nazywać insert] lub c()dodać elementy na koniec listy, ale nie pomoże ci tutaj.
hatmatrix
Nie ma zbyt wiele funkcji w R że ramki danych powrotu, chyba że ich Powrót [row-wise] z lapply(), Map()i tak dalej, ale może również chcesz przyjrzeć aggregate(), dapply() {heR.Misc}oraz cast() {reshape}aby sprawdzić, czy zadania nie mogą być obsługiwane przez te funkcje (wszystkie zwracają ramki danych).
hatmatrix

Odpowiedzi:

96

Możesz je rozwijać wiersz po wierszu, dołączając lub używając rbind().

To nie znaczy, że powinieneś. Dynamicznie rosnące struktury to jeden z najmniej wydajnych sposobów kodowania w języku R.

Jeśli możesz, przydziel całą ramkę data.frame z góry:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

a następnie podczas operacji wstawiaj kolejne wiersze

DF[i, ] <- list(1.4, "foo")

To powinno działać dla arbitralnych data.frame i być znacznie bardziej wydajne. Jeśli przekroczysz N, zawsze możesz zmniejszyć puste wiersze na końcu.

Dirk Eddelbuettel
źródło
6
Czy nie miałeś na myśli wstawienia N zamiast 10 i listy (1.4, "foo") zamiast c (1.4, "foo"), aby nie zmuszać 1.4 do trybu znakowego?
hatmatrix
Tak, chciałem użyć N w tworzeniu data.frame. Poza tym bardzo dobrze złapałem przymus na czacie - przegapiłem to.
Dirk Eddelbuettel
1
Lepiej byłoby edytować odpowiedź niż zostawić ją w komentarzach. Byłem zdezorientowany, próbując zrozumieć tę odpowiedź.
Użytkownik
4
data.tablewydaje się być nawet szybszy niż wstępna alokacja przy użyciu data.frames. Testowanie tutaj: stackoverflow.com/a/11486400/636656
Ari B. Friedman.
czy jest to nadal prawdą w R 3.1, gdzie powinno to być szybsze?
userJT
49

Można dodawać wiersze do NULL:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

na przykład

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)
mbq
źródło
3
wysyła matrycę, a nie ramkę danych
Olga
1
@Olga Tylko jeśli łączysz wiersze elementów równego typu - BTW w takim przypadku lepiej jest sapply(lub wektoryzować) i transponować.
mbq
1
@mbq Dokładnie to, co robię. Odkryłem również, że jeśli zainicjujesz go za pomocą df <-data.frame (), wyprowadza ramkę danych.
Olga
9

To jest głupi przykład, jak używać do.call(rbind,)na wyjściu Map()[który jest podobny do lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

Często używam tej konstrukcji.

hatmatrix
źródło
8

Powodem, dla którego tak bardzo lubię Rcpp, jest to, że nie zawsze rozumiem, jak myśli R Core, aw przypadku Rcpp częściej niż nie muszę.

Mówiąc filozoficznie, jesteś w stanie grzechu w odniesieniu do paradygmatu funkcjonalnego, który stara się zapewnić, że każda wartość wydaje się niezależna od każdej innej wartości; zmiana jednej wartości nigdy nie powinna powodować widocznej zmiany innej wartości, tak jak w przypadku wskaźników udostępniających reprezentację w C.

Problemy pojawiają się, gdy programowanie funkcjonalne sygnalizuje małemu statkowi, aby usunął się z drogi, a mały statek odpowiada „Jestem latarnią morską”. Dokonanie długiej serii małych zmian w dużym obiekcie, na którym chcesz w międzyczasie przetworzyć, umieszcza kwadrat na terytorium latarni morskiej.

W C ++ STL push_back()to sposób na życie. Nie stara się być funkcjonalny, ale stara się efektywnie dostosowywać popularne idiomy programowania .

Mając trochę sprytu za kulisami, możesz czasem ustawić jedną stopę w każdym świecie. Dobrym przykładem są systemy plików oparte na migawkach (które wyewoluowały z takich koncepcji, jak łączenie montowań, które również obejmują obie strony).

Gdyby R Core chciał to zrobić, bazowa pamięć wektorowa mogłaby działać jak sumator. Jedno odniesienie do pamięci wektorowej może być ważne dla indeksów dolnych 1:N, podczas gdy inne odniesienie do tego samego magazynu jest ważne dla indeksów dolnych 1:(N+1). Może istnieć zarezerwowane miejsce do przechowywania, do którego nie ma jeszcze ważnego odniesienia, ale wygodne dla szybkiego push_back(). Nie naruszasz koncepcji funkcjonalnej, gdy dołączasz poza zakresem, który jakiekolwiek istniejące odniesienie uważa za prawidłowe.

Ostatecznie dodając wiersze przyrostowo, zabraknie zarezerwowanego miejsca. Będziesz musiał utworzyć nowe kopie wszystkiego, pomnożąc przestrzeń dyskową przez pewien przyrost. Implementacje STL, których używam, mają tendencję do mnożenia pamięci przez 2 podczas rozszerzania alokacji. Myślałem, że przeczytałem w R Internals, że istnieje struktura pamięci, w której pojemność zwiększa się o 20%. Tak czy inaczej, operacje wzrostu występują z częstotliwością logarytmiczną w stosunku do całkowitej liczby dołączonych elementów. Na zasadzie amortyzacji jest to zwykle dopuszczalne.

W miarę jak chodzą za kulisami sztuczki, widziałem gorzej. Za każdym razem, push_back()gdy dodajesz nowy wiersz do ramki danych, należałoby skopiować strukturę indeksu najwyższego poziomu. Nowy wiersz może zostać dołączony do wspólnej reprezentacji bez wpływu na jakiekolwiek stare wartości funkcjonalne. Nie sądzę nawet, żeby to zbytnio skomplikowałoby śmieciarza; ponieważ nie proponuję, że push_front()wszystkie odwołania są prefiksami z przodu przydzielonej pamięci wektorowej.

Allan Stokes
źródło
2

Odpowiedź Dirka Eddelbuettela jest najlepsza; tutaj tylko zauważam, że możesz uciec bez wcześniejszego określenia wymiarów lub typów danych ramki danych, co jest czasami przydatne, jeśli masz wiele typów danych i wiele kolumn:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(df,row2) #now this works as you'd expect.
Jan
źródło
Czy miałeś na myśli df<-rbind(df, row2)?
Timothy C. Quinn
1

Znalazłem ten sposób, aby stworzyć ramkę danych w formacie raw bez macierzy.

Z automatyczną nazwą kolumny

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

Z nazwą kolumny

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )
phili_b
źródło
0

Jeśli masz wektory, które mają stać się wierszami, połącz je za pomocą c(), przekaż je do macierzy wiersz po wierszu i przekonwertuj tę macierz na ramkę danych.

Na przykład wiersze

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

można przekształcić w ramkę danych w ten sposób:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Trzeba przyznać, że widzę 2 główne ograniczenia: (1) działa to tylko z danymi jednomodowymi i (2) aby to zadziałało, musisz znać końcowe # kolumny (tj. Zakładam, że nie pracujesz z tablica ragged, której największa długość wiersza jest nieznana a priori ).

To rozwiązanie wydaje się proste, ale z mojego doświadczenia z konwersją typów w R jestem pewien, że stwarza nowe wyzwania w przyszłości. Czy ktoś może to skomentować?

Keegan Smith
źródło
0

W zależności od formatu nowego wiersza możesz użyć, tibble::add_rowjeśli nowy wiersz jest prosty i można go określić w „parach wartości”. Lub możesz użyć dplyr::bind_rows„wydajnej implementacji wspólnego wzorca do.call (rbind, dfs)”.

Arthur Yip
źródło