2013-07-26 27 views
35

Chcę scalić dwie ramki danych zachowując oryginalną kolejność rzędów jednego z nich (df.2 w poniższym przykładzie).Scalanie dwóch ramek danych przy zachowaniu oryginalnego rzędu

Oto kilka przykładowych danych (wszystkie wartości z class kolumny są zdefiniowane w obu ramkach danych):

df.1 <- data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3)) 
df.2 <- data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1)) 

Jeśli zrobić:

merge(df.2,df.1) 

Wynik jest:

class object prob 
1  1  B 0.5 
2  1  C 0.5 
3  2  A 0.7 
4  2  D 0.7 
5  3  F 0.3 

Jeśli dodaję sort=FALSE:

merge(df.2,df.1, sort=F)               

Wynik jest:

class object prob 
1  2  A 0.7 
2  2  D 0.7 
3  1  B 0.5 
4  1  C 0.5 
5  3  F 0.3 

Chciałbym to:

class object prob 
1  2  A 0.7 
2  1  B 0.5 
3  2  D 0.7 
4  3  F 0.3  
5  1  C 0.5 
+0

Problem nie jest dobrze zdefiniowany. Co się stanie, jeśli w połączonym wyniku uzyskasz więcej lub mniej wierszy niż df.2? – agstudy

+0

Niemożliwe, ponieważ używam df.2 jako pierwszej zmiennej w funkcji scalania ... – DJack

+0

Nie, to jest złe. wciąż źle określone. weź sprawę, że masz klasę w df; 2, która nie istnieje w df.1. Na przykład: 'df.2 <-data.frame (object = c ('A', 'B', 'D', 'F', 'C'), class = c (2,1,2,4,1)) ' – agstudy

Odpowiedz

21

Sprawdź funkcję łączenia w pakiecie plyr. To jak scalanie, ale pozwala zachować kolejność rzędów jednego z zestawów danych. Ogólnie rzecz biorąc, jest bardziej elastyczny niż scalanie.

Twoje przykładowe dane, będziemy używać join takiego:

> join(df.2,df.1) 
Joining by: class 
    object class prob 
1  A  2 0.7 
2  B  1 0.5 
3  D  2 0.7 
4  F  3 0.3 
5  C  1 0.5 

Oto kilka linków opisujących poprawki do funkcji scalania za utrzymanie porządku rzędu:

http://www.r-statistics.com/2012/01/merging-two-data-frame-objects-while-preserving-the-rows-order/

http://r.789695.n4.nabble.com/patching-merge-to-allow-the-user-to-keep-the-order-of-one-of-the-two-data-frame-objects-merged-td4296561.html

+0

Pokaż rozwiązania tutaj, nie tylko odniesienia do nich. Łącza mogą stać się nieważne dość szybko w Internecie .. – stefan

+0

Każdy z tych linków jest dogłębną dyskusją na temat problemu z funkcją scalania, więc zostawię je jako łącza. (Ale zamiast tego ponownie wstawię wtyczkę do funkcji łączenia w pakiecie plyr.) – user2635373

+2

I (i dokumentacja dołączenia) nie zgadzają się, że jest "bardziej elastyczny" niż "scalanie": "Dołączanie jest często szybsze niż Scalaj, chociaż jest nieco mniej funkcjonalny - obecnie nie ma możliwości zmiany nazwy wyjścia lub scalenia różnych zmiennych w ramkach danych x i y. " – joran

38

Wystarczy utworzyć zmienną, która podaje liczbę wierszy w df.2. Następnie, po scaleniu danych, sortujesz nowy zestaw danych zgodnie z tą zmienną. Oto przykład:

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3)) 
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1)) 
df.2$id <- 1:nrow(df.2) 
out <- merge(df.2,df.1, by = "class") 
out[order(out$id), ] 
1

Dzięki @PAC, wymyśliłem coś takiego:

merge_sameord = function(x, y, ...) { 
    UseMethod('merge_sameord') 
} 

merge_sameord.data.frame = function(x, y, ...) { 
    rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='') 
    x[, rstr] = 1:nrow(x) 
    res = merge(x, y, all.x=TRUE, sort=FALSE, ...) 
    res = res[order(res[, rstr]), ] 
    res[, rstr] = NULL 
    res 
} 

ta zakłada, że ​​chcesz zachować kolejność pierwszej ramki danych, a połączony ramka danych będą miały taką samą liczbę wierszy, jak w pierwszej ramce danych. Da ci czystą ramkę danych bez dodatkowych kolumn.

5

Możesz również sprawdzić funkcję inner_join w pakiecie Hadleya dplyr (następna iteracja plyr). Zachowuje kolejność wierszy pierwszego zestawu danych. Mniejsza różnica w stosunku do pożądanego rozwiązania polega na tym, że zachowuje on oryginalną kolejność kolumn pierwszego zestawu danych. Więc niekoniecznie umieszcza kolumnę, której użyliśmy do scalenia na pierwszej pozycji.

Korzystając z powyższego przykładu, wynik inner_join wygląda następująco:

inner_join(df.2,df.1) 
Joining by: "class" 
    object class prob 
1  A  2 0.7 
2  B  1 0.5 
3  D  2 0.7 
4  F  3 0.3 
5  C  1 0.5 
3

accepted answer proponuje ręczny sposób, aby utrzymać porządek podczas korzystania merge, który działa w większości przypadków, ale wymaga niepotrzebnej pracy ręcznej.Rozwiązanie to pochodzi z tyłu How to ddply() without sorting?, która dotyczy kwestii utrzymania porządku, ale w ułamku ubiegać-łączą kontekstu:

ten pojawił się na liście mailingowej plyr jakiś czas temu (podniesione przez @kohske nie mniej) i jest to rozwiązanie oferowane przez Petera Meilstrup dla ograniczonych przypadkach:

#Peter's version used a function gensym to 
# create the col name, but I couldn't track down 
# what package it was in. 
keeping.order <- function(data, fn, ...) { 
    col <- ".sortColumn" 
    data[,col] <- 1:nrow(data) 
    out <- fn(data, ...) 
    if (!col %in% colnames(out)) stop("Ordering column not preserved by function") 
    out <- out[order(out[,col]),] 
    out[,col] <- NULL 
    out 
} 

Więc teraz można korzystać z tej funkcji rodzajowe keeping.order zachować oryginalną kolejność wierszy o merge rozmowy:

df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3)) 
df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1)) 
keeping.order(df.2, merge, y=df.1, by = "class") 

Który przyniesie, zgodnie z wnioskiem:

> keeping.order(df.2, merge, y=df.1, by = "class") 
    class object id prob 
3  2  A 1 0.7 
1  1  B 2 0.5 
4  2  D 3 0.7 
5  3  F 4 0.3 
2  1  C 5 0.5 

Więc keeping.order skutecznie automatyzuje podejścia przyjętego odpowiedź.

0

Może być bardziej efektywny sposób w bazie. Byłoby to dość proste do wykonania funkcji.

varorder <- names(mydata) # --- Merge 
mydata <- merge(mydata, otherData, by="commonVar") 
restOfvars <- names(mydata[!(names(mydata) %in% varorder)]) 

mydata[c(varorder,restOfvars)] 
9

Od data.table v1.9.5+, można zrobić:

require(data.table) # v1.9.5+ 
setDT(df.1)[df.2, on="class"] 

wykonuje dołączyć na kolumnie class przez ustaleniu pasujących wierszy w df.1 dla każdego wiersza w df.2 i wyodrębnianie odpowiednich kolumnach.

2

W celu uzupełnienia, aktualizacja w połączeniu zachowuje również oryginalną kolejność rzędów. To może być alternatywą dla Arun's data.table answer jeśli istnieje tylko kilka kolumn dołączyć:

library(data.table) 
setDT(df.2)[df.1, on = "class", prob := i.prob][] 
object class prob 
1:  A  2 0.7 
2:  B  1 0.5 
3:  D  2 0.7 
4:  F  3 0.3 
5:  C  1 0.5 

Tutaj df.2 ma rację dołączył do df.1 i zyskuje nową kolumnę prob który jest kopiowany z pasujące wiersze z df.1.

1

W tym konkretnym przypadku może nas factor kompaktowego rozwiązania bazowego:

df.2$prob = factor(df.2$class,labels=df.1$prob) 

df.2 
# object class prob 
# 1  A  2 0.7 
# 2  B  1 0.5 
# 3  D  2 0.7 
# 4  F  3 0.3 
# 5  C  1 0.5 

Nie ogólne rozwiązanie, działa jednak, jeżeli:

  1. mieć tablicę przeglądową zawierającą unikalne wartości
  2. Chcesz zaktualizować tabelę, a nie utworzyć nową
  3. tabeli odnośników jest posortowana według kolumny scalenia
  4. tabeli odnośników nie ma dodatkowych poziomów
  5. Chcesz left_join
  6. Jeśli jesteś w porządku z czynników

1 nie jest do negocjacji, do końca możemy zrobić:

df.3 <- df.2 # deal with 2. 
df.1b <- df.1[order(df.1$class),] # deal with 3 
df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4. 
df.3$prob = factor(df.3$class,labels=df.1b$prob) 
df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join` 
df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6. 
Powiązane problemy