37

我有几个要按行组合的数据框。在生成的单个数据框中,我想创建一个新变量来标识观察来自哪个数据集。

# 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")
# x y source
# 1 2    df1
# 3 4    df1
# 5 6    df2
# 7 8    df2

我怎样才能做到这一点?提前致谢!

4

7 回答 7

44

这不完全是你要求的,但它非常接近。将您的对象放在命名列表中并使用do.call(rbind...)

> do.call(rbind, list(df1 = df1, df2 = df2))
      x y
df1.1 1 2
df1.2 3 4
df2.1 5 6
df2.2 7 8

请注意,行名称现在反映了源data.frames。

更新:使用cbindrbind

另一种选择是制作如下基本功能:

AppendMe <- function(dfNames) {
  do.call(rbind, lapply(dfNames, function(x) {
    cbind(get(x), source = x)
  }))
}

然后,此函数采用data.frame您要“堆叠”的名称的字符向量,如下所示:

> AppendMe(c("df1", "df2"))
  x y source
1 1 2    df1
2 3 4    df1
3 5 6    df2
4 7 8    df2

更新 2:combine从“gdata”包中使用

> library(gdata)
> combine(df1, df2)
  x y source
1 1 2    df1
2 3 4    df1
3 5 6    df2
4 7 8    df2

更新 3:使用rbindlist“data.table”

现在可以使用的另一种方法是使用rbindlist“data.table”及其idcol参数。有了这个,方法可能是:

> rbindlist(mget(ls(pattern = "df\\d+")), idcol = TRUE)
   .id x y
1: df1 1 2
2: df1 3 4
3: df2 5 6
4: df2 7 8

更新 4:使用map_df“purrr”

与 类似rbindlist,您也可以使用map_dffrom "purrr" with Iorc作为函数来应用到每个列表元素。

> mget(ls(pattern = "df\\d+")) %>% map_df(I, .id = "src")
Source: local data frame [4 x 3]

    src     x     y
  (chr) (int) (int)
1   df1     1     2
2   df1     3     4
3   df2     5     6
4   df2     7     8
于 2013-03-01T16:32:45.900 回答
27

另一种方法使用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     x     y
   (chr) (dbl) (dbl)
1    df1     1     2
2    df1     3     4
3    df2     5     6
4    df2     7     8
于 2016-04-17T10:55:31.403 回答
7

我不确定这样的功能是否已经存在,但这似乎可以解决问题:

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警告:这在-like 调用中不起作用

于 2013-03-01T16:28:39.617 回答
6

其他两个答案的混合:

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)
  x y 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)来访问名称。

于 2013-03-01T16:33:33.163 回答
2

另一种解决方法是在 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 x y
  df1 1 2
  df1 3 4
  df2 5 6
  df2 7 8
于 2013-03-06T14:36:28.133 回答
1

尽管这里已经有一些很好的答案,但我只想添加我一直在使用的答案。它是基础的R,因此如果您想在一个包中使用它,它的限制可能会更少,而且它比其他一些基础R解决方案要快一点。

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)将数据帧的行绑定到单个数据帧中。vapply(dfs, nrow, numeric(1))查找每个数据框有多少行,这些行被传递给以reprep(names(dfs), vapply(dfs, nrow, numeric(1)))数据框的每一行重复数据框的名称一次。 cbind把它们放在一起。

这类似于之前发布的解决方案,但速度提高了约 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

我不是 100% 确定,但我相信加速是由于对cbind每个数据帧进行一次调用而不是一次调用。

于 2017-01-12T18:35:48.837 回答
0

这是使用Map. 首先,我创建了一个命名的数据框列表。然后,我可以cbind为每个数据框命名。然后,使用unname删除行名。最后,rbind将所有数据帧放在一起。

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

df.list <- Hmisc::llist(df1, df2)

do.call(rbind, unname(Map(cbind, source = names(df.list), df.list)))

输出

  source x y
1    df1 1 2
2    df1 3 4
3    df2 5 6
4    df2 7 8
于 2022-03-04T08:24:53.213 回答