Одновременно объединить несколько данных.кадры в списке
у меня есть список из многих данных.кадры, которые я хочу объединить. Проблема здесь в том, что каждый данные.фрейм отличается по количеству строк и столбцов, но все они разделяют ключевые переменные (которые я назвал "var1" и "var2" в коде ниже). Если данные есть.рамки были идентичны с точки зрения столбцов, я мог просто rbind, для которого rbind plyr.fill выполнит эту работу, но это не так с этими данными.
потому что merge команда работает только на 2 данные.кадры, я обратился к Интернету за идеями. Я получил это от здесь, который отлично работал в R 2.7.2, что и было у меня в то время:
merge.rec <- function(.list, ...){
if(length(.list)==1) return(.list[[1]])
Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}
и я бы назвал функцию так:
df <- merge.rec(my.list, by.x = c("var1", "var2"),
by.y = c("var1", "var2"), all = T, suffixes=c("", ""))
но в любой версии R после 2.7.2, включая 2.11 и 2.12, этот код завершается со следующей ошибкой:
Error in match.names(clabs, names(xi)) :
names do not match previous names
(кстати, я вижу другие ссылки на эту ошибку в другом месте без разрешения).
есть ли способ чтобы решить эту проблему?
5 ответов:
другой вопрос, заданный специально как выполнить несколько левых соединений с помощью dplyr в R . Вопрос был отмечен как дубликат этого, поэтому я отвечаю здесь, используя 3 образца фреймов данных ниже:
library(dplyr) x <- data_frame(i = c("a","b","c"), j = 1:3) y <- data_frame(i = c("b","c","d"), k = 4:6) z <- data_frame(i = c("c","d","a"), l = 7:9)Обновление Июня 2018: я разделил ответ на три раздела, представляющих три различных способа выполнения слияния. Вы, вероятно, хотите использовать
purrrкстати, если вы уже используете tidyverse пакеты. Для ниже вы найдете базовую версию R, используя тот же образец набора данных.присоединиться к ним с
reduceСpurrrпакетаThe
purrrпакет предоставляетreduceфункция, которая имеет краткий синтаксис:library(tidyverse) list(x, y, z) %>% reduce(left_join, by = "i") # A tibble: 3 x 4 # i j k l # <chr> <int> <int> <int> # 1 a 1 NA 9 # 2 b 2 4 NA # 3 c 3 5 7вы также можете выполнять другие соединения, такие как
full_joinилиinner_join:list(x, y, z) %>% reduce(full_join, by = "i") # A tibble: 4 x 4 # i j k l # <chr> <int> <int> <int> # 1 a 1 NA 9 # 2 b 2 4 NA # 3 c 3 5 7 # 4 d NA 6 8 list(x, y, z) %>% reduce(inner_join, by = "i") # A tibble: 1 x 4 # i j k l # <chr> <int> <int> <int> # 1 c 3 5 7
dplyr::left_join()С базой RReduce()list(x,y,z) %>% Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .) # i j k l # 1 a 1 NA 9 # 2 b 2 4 NA # 3 c 3 5 7База R
merge()С основанием RReduce()и для сравнения, вот базовая версия R левого соединения
Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all.x = TRUE), list(x,y,z)) # i j k l # 1 a 1 NA 9 # 2 b 2 4 NA # 3 c 3 5 7
уменьшить делает это довольно легко:
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)вот полный пример использования некоторых макетных данных:
set.seed(1) list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10))) merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames) tail(merged.data.frame) # x a b y #12 12 NA 18 NA #13 13 NA 19 NA #14 14 NA 20 0.4976992 #15 15 NA NA 0.7176185 #16 16 NA NA 0.3841037 #17 19 NA NA 0.3800352и вот пример использования эти данные повторить
my.list:merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list) merged.data.frame[, 1:12] # matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y #1 ALGIERE 200 RI 026 S NA <NA> NA NA NA NA <NA> #2 ALVES 100 RI 019 S NA <NA> NA NA NA NA <NA> #3 BADEAU 100 RI 032 S NA <NA> NA NA NA NA <NA>
Примечание: похоже, что это, возможно, ошибка в
merge. Проблема в том, что нет никакой проверки, что добавление суффиксов (для обработки перекрывающихся несоответствующих имен) фактически делает их уникальными. В определенный момент он использует[.data.frameчто тутmake.uniqueимена, причинивrbindна провал.# first merge will end up with 'name.x' & 'name.y' merge(my.list[[1]], my.list[[2]], by=match.by, all=T) # [1] matchname party st district chamber senate1993 name.x # [8] votes.year.x senate1994 name.y votes.year.y #<0 rows> (or 0-length row.names) # as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T) # [1] matchname party st district chamber senate1993 name.x # [8] votes.year.x senate1994 name.y votes.year.y senate1995 name votes.year #<0 rows> (or 0-length row.names) # the next merge will fail as 'name' will get renamed to a pre-existing field.самый простой способ исправить это не оставлять поле переименования для дубликатов полей (которых здесь много) до
merge. Например:my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by, names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list))The
merge/Reduceбудет работать нормально.
вы можете сделать это с помощью
merge_allнаreshapeпакета. Вы можете передать параметры вmergeС помощью...аргументreshape::merge_all(list_of_dataframes, ...)вот отличный ресурс по различным методам слияния фреймов данных.
для этого можно использовать рекурсию. Я не проверял, но это должно дать вам хорошую идею:
MergeListOfDf = function( data , ... ) { if ( length( data ) == 2 ) { return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) ) } return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) ) }
Я буду повторно использовать пример данных из @PaulRougieux
x <- data_frame(i = c("a","b","c"), j = 1:3) y <- data_frame(i = c("b","c","d"), k = 4:6) z <- data_frame(i = c("c","d","a"), l = 7:9)вот короткое и сладкое решение с помощью
purrrиtidyrlibrary(tidyverse) list(x, y, z) %>% map_df(gather, key=key, value=value, -i) %>% spread(key, value)
Comments