Одновременно объединить несколько данных.кадры в списке



у меня есть список из многих данных.кадры, которые я хочу объединить. Проблема здесь в том, что каждый данные.фрейм отличается по количеству строк и столбцов, но все они разделяют ключевые переменные (которые я назвал "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


(кстати, я вижу другие ссылки на эту ошибку в другом месте без разрешения).



есть ли способ чтобы решить эту проблему?

622   5  

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() С базой R Reduce()

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() С основанием R Reduce()

и для сравнения, вот базовая версия 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 и tidyr

library(tidyverse)

 list(x, y, z) %>% 
  map_df(gather, key=key, value=value, -i) %>% 
  spread(key, value)

Comments

    Ничего не найдено.