2013-07-26 3 views
35

Я хочу объединить два кадра данных, сохраняя исходный порядок строк одного из них (df.2 в примере ниже).Объединить два кадра данных при сохранении исходного порядка строк

Вот некоторые выборочные данные (все значения class столбца определяются в обоих кадрах данных):

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)) 

Если я:

merge(df.2,df.1) 

Результат:

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 

Если я добавлю sort=FALSE:

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

Результат:

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 

То, что я хотел бы это:

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

Проблема не определена. Что, если у вас в объединенном результате больше или меньше строк, чем df.2? – agstudy

+0

Невозможно, потому что я использую df.2 как первую переменную в функции слияния ... – DJack

+0

Нет. Это неправильно. все еще плохо определено. возьмем случай, если у вас есть класс в df; 2, который не существует в df.1. Например: 'df.2 <-data.frame (object = c ('A', 'B', 'D', 'F', 'C'), class = c (2,1,2,4,1)) ' – agstudy

ответ

21

Проверьте функцию соединения в пакете plyr. Это похоже на merge, но это позволяет сохранить порядок строк одного из наборов данных. В целом, он более гибкий, чем слияние.

Используя ваши данные примера, мы будем использовать join так:

> 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 

Вот несколько ссылок, описывающих исправления функции слияния для поддержания порядка строк:

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

Показать решения здесь, а не просто ссылаться на них. Ссылки могут стать недействительными довольно быстро в Интернете. – stefan

+0

Каждая из этих ссылок является углубленным обсуждением проблемы с функцией слияния, поэтому я просто оставлю их в качестве ссылок. (Но вместо этого я снова включу вилку для функции соединения в пакете plyr.) – user2635373

+2

I (и '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' merge, хотя он несколько менее функциональен - в настоящее время он не предлагает никакого способа переименования вывода или слияния с разными переменными в кадрах данных x и y ». – joran

38

Вам просто нужно создать переменную, которая дает номер строки в df.2. Затем, как только вы объедините свои данные, вы сортируете новый набор данных в соответствии с этой переменной. Вот пример:

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

Благодаря @PAC, я придумал что-то вроде этого:

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 
} 

Это предполагает, что вы хотите, чтобы сохранить порядок первый фрейм данных и объединенный кадр данных будет иметь такое же количество строк в качестве первого кадра данных. Он предоставит вам чистый фрейм данных без дополнительных столбцов.

5

Вы также можете проверить функцию inner_join в пакете dplyr Хэдли (следующая итерация plyr). Он сохраняет порядок строк первого набора данных. Небольшая разница с вашим желаемым решением заключается в том, что он также сохраняет исходный порядок столбцов первого набора данных. Поэтому он не обязательно помещает столбец, который мы использовали для слияния в первой позиции.

Используя ваш пример выше, inner_join результат выглядит следующим образом:

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 предлагает ручной способ сохранить порядок при использовании merge, который работает большую часть времени, но требует ненужную ручной работы.Это решение приходит на задней How to ddply() without sorting?, которая касается вопроса о поддержании порядка, но в разбитом применить-скомбинировать контексте:

Это придумали в списке рассылки plyr некоторое время назад (поднятый @kohske нет меньше), и это решение, предложенное Питером Meilstrup для ограниченных случаев:

#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 
} 

Итак, теперь вы можете использовать эту общую keeping.order функции, чтобы сохранить первоначальный порядок строки для merge вызова:

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") 

Который даст, по запросу:

> 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 

Так keeping.order эффективно автоматизирует подход в принятом ответе.

0

В основе может быть более эффективный способ. Это было бы довольно просто сделать в функции.

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

mydata[c(varorder,restOfvars)] 
9

От data.table v1.9.5+, вы можете сделать:

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

выполняет соединение на колонке class, выяснив совпадающие строки в df.1 для каждой строки в df.2 и извлечения соответствующих столбцов.

2

Для полноты, Обновление в соединении сохраняет исходный порядок строк. Это может быть альтернативой Arun's data.table answer, если есть только несколько столбцов, чтобы добавить:

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 

Здесь df.2 правильно присоединился к df.1 и получает новый столбец prob который копируется из совпадающие строки df.1.

1

В данном конкретном случае вы могли бы нам factor для компактного базового раствора:

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 

не общее решение, однако, оно работает, если:

  1. У вас есть справочная таблица, содержащую уникальные значения
  2. Вы хотите обновить таблицу, а не создать новый
  3. таблица поиска сортируется по столбцу слияния
  4. Эта таблица не имеет дополнительных уровней
  5. Вы хотите left_join
  6. Если вы хорошо с факторами

1 не является предметом переговоров, для остальных мы можем сделать:

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. 
Смежные вопросы