Подтвердить что ты не робот

Слияние двух фреймов данных, сохраняя исходный порядок строк

Я хочу объединить два фрейма данных, сохраняя исходный порядок строк одного из них (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
4b9b3361

Ответ 1

Проверьте функцию соединения в пакете 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

Ответ 2

Вам просто нужно создать переменную, которая дает номер строки в 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), ]

Ответ 3

Из data.table v1.9. 5+ вы можете сделать:

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

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

Ответ 4

Вы также можете проверить функцию inner_join в пакете Hadley 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

Ответ 5

Для полноты обновления в объединении также сохраняется исходный порядок строк. Это может быть альтернативой data.table Arun data.table если нужно добавить только несколько столбцов:

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.

Ответ 6

В принятом ответе предлагается ручной способ сохранить порядок при использовании merge, который работает большую часть времени, но требует ненужной ручной работы. Это решение появляется на обратной стороне Как ddply() без сортировки?, в котором рассматривается проблема сохранения порядка, но в контексте split-apply-comb:

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

#Peter 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 эффективно автоматизирует подход в принятом ответе.

Ответ 7

Благодаря @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
}

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

Ответ 8

В данном конкретном случае вы можете использовать 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.

Ответ 9

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

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

mydata[c(varorder,restOfvars)]