Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在列表中同时合并多个data.frames_R_List_Merge_Dataframe_R Faq - Fatal编程技术网

在列表中同时合并多个data.frames

在列表中同时合并多个data.frames,r,list,merge,dataframe,r-faq,R,List,Merge,Dataframe,R Faq,我有许多要合并的data.frames的列表。这里的问题是,每个data.frame的行数和列数不同,但它们都共享关键变量(我在下面的代码中将其称为“var1”和“var2”)。如果data.frames在列方面是相同的,那么我只能rbind,plyr将负责这项工作,但这些数据不是这样的 因为merge命令只对2个data.frames有效,所以我转向互联网寻求灵感。我从中得到了这个,它在R 2.7.2中运行得非常好,这就是我当时所拥有的: merge.rec <- function(.l

我有许多要合并的data.frames的列表。这里的问题是,每个data.frame的行数和列数不同,但它们都共享关键变量(我在下面的代码中将其称为
“var1”
“var2”
)。如果data.frames在列方面是相同的,那么我只能
rbind
,plyr将负责这项工作,但这些数据不是这样的

因为
merge
命令只对2个data.frames有效,所以我转向互联网寻求灵感。我从中得到了这个,它在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)]), ...)
}
(顺便说一句,我看到其他人提到这一错误,但没有得到解决)


有什么办法解决这个问题吗

您可以使用递归来实现这一点。我尚未验证以下内容,但它应该会给您提供正确的想法:

MergeListOfDf = function( data , ... )
{
    if ( length( data ) == 2 ) 
    {
        return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
    }    
    return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}

您可以使用
重塑
包中的
merge\u all
来执行此操作。您可以使用
参数将参数传递给
合并

reshape::merge_all(list_of_dataframes, ...)

.

Reduce使这变得相当容易:

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
之前不要将字段重命名为重复字段(此处有许多重复字段)。例如:

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

合并
/
减少
将很好地工作。

另一个问题特别提出。该问题被标记为该问题的副本,因此我在这里使用以下3个样本数据框进行回答:

x <- data.frame(i = c("a","b","c"), j = 1:3, stringsAsFactors=FALSE)
y <- data.frame(i = c("b","c","d"), k = 4:6, stringsAsFactors=FALSE)
z <- data.frame(i = c("c","d","a"), l = 7:9, stringsAsFactors=FALSE)
您还可以执行其他联接,例如
完全联接
内部联接

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

3)基本R
merge()
与基本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
为了便于比较,这里是基于Charles答案的左连接的基本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

我将重用@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)

我有一个没有公共id列的数据帧列表。
我丢失了许多dfs的数据。存在空值。 数据帧是使用table函数生成的。 Reduce、Merging、rbind、rbind.fill和它们类似的函数不能帮助我实现目标。 我的目标是生成一个可理解的合并数据帧,与丢失的数据和公共id列无关

因此,我做了以下函数。也许这个功能可以帮助别人

##########################################################
####             Dependencies                        #####
##########################################################

# Depends on Base R only

##########################################################
####             Example DF                          #####
##########################################################

# Example df
ex_df           <- cbind(c( seq(1, 10, 1), rep("NA", 0), seq(1,10, 1) ), 
                         c( seq(1, 7, 1),  rep("NA", 3), seq(1, 12, 1) ), 
                         c( seq(1, 3, 1),  rep("NA", 7), seq(1, 5, 1), rep("NA", 5) ))

# Making colnames and rownames
colnames(ex_df) <- 1:dim(ex_df)[2]
rownames(ex_df) <- 1:dim(ex_df)[1]

# Making an unequal list of dfs, 
# without a common id column
list_of_df      <- apply(ex_df=="NA", 2, ( table) )

如果您愿意,我的软件包的函数
eat
就有这样的功能 它将创建一个data.frames列表作为第二个输入,并将它们连接起来 递归地返回到第一个输入

借用和扩展已接受答案的数据:

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)
z2 <- data_frame(i = c("a","b","c"), l = rep(100L,3),l2 = rep(100L,3)) # for later

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
eat(x, list(y,z), .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
或删除特定的:

eat(x, list(y,z), -starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     k
#   <chr> <int> <int>
# 1 a         1    NA
# 2 b         2     4
# 3 c         3     5
保持最后:

eat(x, list(y, z, z2), .by = "i", .conflict = ~.y)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   100
# 2 b         2     4   100
# 3 c         3     5   100
默认情况下,它是一个增强的
左\u连接
,但所有dplyr连接都通过
.mode
参数、模糊联接也通过
匹配乐趣
参数(它围绕包
fuzzyjoin
)或 将诸如
~X(“var1”)>Y(“var2”)&X(“var3”)
之类的公式提供给
by
参数。

当您有一个dfs列表,并且一列包含“ID”,但在某些列表中,某些ID缺失时,您可以使用此版本的Reduce/Merge来连接缺失行ID或标签的多个dfs:

eat(x, list(y, z), .by = "i", .fill = 0)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <dbl> <dbl>
# 1 a         1     0     9
# 2 b         2     4     0
# 3 c         3     5     7
Reduce(function(x, y) merge(x=x, y=y, by="V1", all.x=T, all.y=T), list_of_dfs)

这是一个通用包装器,可用于将二进制函数转换为多参数函数。此解决方案的好处是它非常通用,可以应用于任何二进制函数。你只需要做一次,然后你就可以在任何地方应用它

为了演示这个想法,我使用简单的递归来实现。当然,它可以用更优雅的方式实现,这得益于R对功能范式的良好支持

fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}

看起来我刚刚复制了merge_recurse=)很高兴知道这个函数已经存在。是的。每当我有一个想法,我总是检查@hadley是否已经做了,大多数时候他已经做了:-)我有点困惑;我应该合并所有还是合并递归?在任何情况下,当我尝试将我的附加参数添加到其中一个时,我会得到错误“formal argument”all“matched by multiple real arguments”。我想我从Reforme2中删除了这个。Reduce+merge也一样简单。@Ramnath,link死了,有镜子吗?谢谢!我在Ramnath的链接上也看到了这个解决方案。看起来很容易。但是我得到了以下错误:“match.names(clab,names(xi))中的错误:名称与以前的名称不匹配”。我匹配的变量都存在于列表中的所有数据帧中,因此我没有捕捉到这个错误告诉我的信息。我在R2.7.2上测试了这个解决方案,得到了相同的match.names错误。所以这个解决方案和我的数据还有一些更基本的问题。我使用了代码:Reduce(函数(x,y)merge(x,y,all=T,by.x=match.by,by.y=match.by),my.list,accumulate=F)奇怪的是,我添加了我测试它的代码,它运行良好。我猜根据您使用的合并参数会发生一些字段重命名?合并的结果必须仍然具有相关的键,才能与后续数据帧合并。我怀疑空数据帧发生了什么。我试过一些这样的例子:
empty@Charles你发现了什么。你的代码在上面运行得很好。当我将它改编为我的时,它也运行得很好——只是它进行了一次合并,忽略了我想要的关键变量。当我尝试添加关键变量而不是将其忽略时,我得到一个新错误“error in is.null(x):'x'缺失”。代码行是“test.reduce-The-full_-join variant工作得很好,看起来没有公认的答案那么可怕。虽然速度差别不大。@Axeman是对的,但通过使用
map_-dfr()
map_-dfc()可以避免(明显地)返回数据帧列表
我想我可以使用'ls(pattern=“DF_name_contains_this”)')基于一个模式加入许多DF,但是没有使用'noquote(粘贴(())?
##########################################################
####             Running the example                 #####
##########################################################

rbind_null_df_lists ( list_of_df )
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)
z2 <- data_frame(i = c("a","b","c"), l = rep(100L,3),l2 = rep(100L,3)) # for later

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
eat(x, list(y,z), .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
eat(x, list(y,z), starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     l
#   <chr> <int> <int>
# 1 a         1     9
# 2 b         2    NA
# 3 c         3     7
eat(x, list(y,z), -starts_with("l") ,.by = "i")
# # A tibble: 3 x 3
#   i         j     k
#   <chr> <int> <int>
# 1 a         1    NA
# 2 b         2     4
# 3 c         3     5
eat(x, dplyr::lst(y,z), .by = "i")
# # A tibble: 3 x 4
#   i         j   y_k   z_l
#   <chr> <int> <int> <int>
# 1 a         1    NA     9
# 2 b         2     4    NA
# 3 c         3     5     7
eat(x, list(y, z, z2), .by = "i", .conflict = ~.x)
# # 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
eat(x, list(y, z, z2), .by = "i", .conflict = ~.y)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   100
# 2 b         2     4   100
# 3 c         3     5   100
eat(x, list(y, z, z2), .by = "i", .conflict = `+`)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA   109
# 2 b         2     4    NA
# 3 c         3     5   107
eat(x, list(y, z, z2), .by = "i", .conflict = dplyr::coalesce)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <int> <dbl>
# 1 a         1    NA     9
# 2 b         2     4   100
# 3 c         3     5     7
eat(x, list(y, z, z2), .by = "i", .conflict = ~tibble(first=.x, second=.y))
# # A tibble: 3 x 4
#   i         j     k l$first $second
#   <chr> <int> <int>   <int>   <int>
# 1 a         1    NA       9     100
# 2 b         2     4      NA     100
# 3 c         3     5       7     100
eat(x, list(y, z), .by = "i", .fill = 0)
# # A tibble: 3 x 4
#   i         j     k     l
#   <chr> <int> <dbl> <dbl>
# 1 a         1     0     9
# 2 b         2     4     0
# 3 c         3     5     7
Reduce(function(x, y) merge(x=x, y=y, by="V1", all.x=T, all.y=T), list_of_dfs)
fold_left <- function(f) {
return(function(...) {
    args <- list(...)
    return(function(...){
    iter <- function(result,rest) {
        if (length(rest) == 0) {
            return(result)
        } else {
            return(iter(f(result, rest[[1]], ...), rest[-1]))
        }
    }
    return(iter(args[[1]], args[-1]))
    })
})}
merge_all <- fold_left(merge)
merge_all(df1, df2, df3, df4, df5)(by.x = c("var1", "var2"), by.y = c("var1", "var2"))

left_join_all <- fold_left(left_join)
left_join_all(df1, df2, df3, df4, df5)(c("var1", "var2"))
left_join_all(df1, df2, df3, df4, df5)()