合并(rbind)dataframe并创build具有原始dataframe名称的列
我有几个数据框,我想按行组合。 在生成的单一数据框架中,我想创build一个新的variables来标识观察来自哪个数据集。
# original data frames df1 <- data.frame(x = c(1, 3), y = c(2, 4)) df2 <- data.frame(x = c(5, 7), y = c(6, 8)) # desired, combined data frame df3 <- data.frame(x = c(1, 3, 5, 7), y = c(2, 4, 6, 8), source = c("df1", "df1", "df2", "df2") # xy source # 1 2 df1 # 3 4 df1 # 5 6 df2 # 7 8 df2
我怎样才能做到这一点? 提前致谢!
这不是你要求的,但是非常接近。 把你的对象放在一个命名列表中,并使用do.call(rbind...)
> do.call(rbind, list(df1 = df1, df2 = df2)) xy df1.1 1 2 df1.2 3 4 df2.1 5 6 df2.2 7 8
请注意,行名现在反映了源data.frame
。
更新:使用cbind
和rbind
另一个select是做一个基本的function,如下所示:
AppendMe <- function(dfNames) { do.call(rbind, lapply(dfNames, function(x) { cbind(get(x), source = x) })) }
然后这个函数接受一个你想“堆栈”的data.frame
名称的字符向量,如下所示:
> AppendMe(c("df1", "df2")) xy source 1 1 2 df1 2 3 4 df1 3 5 6 df2 4 7 8 df2
更新2:使用“gdata”包中的组合
> library(gdata) > combine(df1, df2) xy source 1 1 2 df1 2 3 4 df1 3 5 6 df2 4 7 8 df2
更新3:使用“data.table”中的rbindlist
现在可以使用的另一种方法是使用“data.table”中的rbindlist。 这样做的方法可以是:
> rbindlist(mget(ls(pattern = "df\\d+")), idcol = TRUE) .id xy 1: df1 1 2 2: df1 3 4 3: df2 5 6 4: df2 7 8
更新4:使用来自“purrr”的map_df
与rbindlist
类似,您也可以使用“purrr”中的map_df和I
或c
作为应用于每个列表元素的函数。
> mget(ls(pattern = "df\\d+")) %>% map_df(I, .id = "src") Source: local data frame [4 x 3] src xy (chr) (int) (int) 1 df1 1 2 2 df1 3 4 3 df2 5 6 4 df2 7 8
我不确定这样的function是否已经存在,但这似乎有诀窍:
bindAndSource <- function(df1, df2) { df1$source <- as.character(match.call())[[2]] df2$source <- as.character(match.call())[[3]] rbind(df1, df2) }
结果:
bindAndSource(df1, df2) 1 1 2 df1 2 3 4 df1 3 5 6 df2 4 7 8 df2
警告:这不适用于*aply
的电话
另外两个答案的混合:
df1 <- data.frame(x = 1:3,y = 1:3) df2 <- data.frame(x = 4:6,y = 4:6) > foo <- function(...){ args <- list(...) result <- do.call(rbind,args) result$source <- rep(as.character(match.call()[-1]),times = sapply(args,nrow)) result } > foo(df1,df2,df1) xy source 1 1 1 df1 2 2 2 df1 3 3 3 df1 4 4 4 df2 5 5 5 df2 6 6 6 df2 7 1 1 df1 8 2 2 df1 9 3 3 df1
如果你想避免match.call
业务,你总是可以限制自己命名函数参数(即df1 = df1, df2 = df2
)和使用names(args)
来访问名称。
另一种使用dplyr
方法dplyr
:
df1 <- data.frame(x = c(1,3), y = c(2,4)) df2 <- data.frame(x = c(5,7), y = c(6,8)) df3 <- dplyr::bind_rows(list(df1=df1, df2=df2), .id = 'source') df3 Source: local data frame [4 x 3] source xy (chr) (dbl) (dbl) 1 df1 1 2 2 df1 3 4 3 df2 5 6 4 df2 7 8
另一个解决方法是在plyr包中使用ldply。
df1 <- data.frame(x = c(1,3), y = c(2,4)) df2 <- data.frame(x = c(5,7), y = c(6,8)) list = list(df1 = df1, df2 = df2) df3 <- ldply(list) df3 .id xy df1 1 2 df1 3 4 df2 5 6 df2 7 8
尽pipe这里已经有了一些很好的答案,但我只是想添加一个我一直在使用的答案。 它是基本的R
所以如果你想在一个包中使用它,它可能会受到更less的限制,而且比其他一些基本的R
解决scheme要快一些。
dfs <- list(df1 = data.frame("x"=c(1,2), "y"=2), df2 = data.frame("x"=c(2,4), "y"=4), df3 = data.frame("x"=2, "y"=c(4,5,7))) > microbenchmark(cbind(do.call(rbind,dfs), rep(names(dfs), vapply(dfs, nrow, numeric(1)))), times = 1001) Unit: microseconds min lq mean median uq max neval 393.541 409.083 454.9913 433.422 453.657 6157.649 1001
第一部分, do.call(rbind, dfs)
将dataframe的行绑定到单个数据框中。 vapply(dfs, nrow, numeric(1))
查找每个dataframe有多less行被传递给rep
中的rep(names(dfs), vapply(dfs, nrow, numeric(1)))
,重复名称dataframe的每一行dataframe一次。 cbind
把它们放在一起。
这与以前发布的解决scheme类似,但速度提高了约2倍。
> microbenchmark(do.call(rbind, lapply(names(dfs), function(x) cbind(dfs[[x]], source = x))), times = 1001) Unit: microseconds min lq mean median uq max neval 844.558 870.071 1034.182 896.464 1210.533 8867.858 1001
我不是百分之百肯定的,但我相信加快速度是由于一次调用cbind
而不是每个dataframe一次。