Слияние двух фреймов данных, сохраняя исходный порядок строк
Я хочу объединить два фрейма данных, сохраняя исходный порядок строк одного из них (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
Ответы
Ответ 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
Однако это не общее решение, оно работает, если:
- У вас есть таблица поиска, содержащая уникальные значения
- Вы хотите обновить таблицу, а не создавать новую
- таблица поиска отсортирована по столбцу слияния
- Таблица поиска не имеет дополнительных уровней
- Вы хотите
left_join
- Если вы в порядке с факторами
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)]