将多个列粘贴在一起

我在一个数据框中有一堆列,我想粘贴在一起(用“ – ”分隔),如下所示:

data <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) ie abcd 1 adg 2 beh 3 cfi 

我想成为:

 ax 1 adg 2 beh 3 cfi 

我通常可以这样做:

 within(data, x <- paste(b,c,d,sep='-')) 

然后删除旧的列,但不幸的是我不知道专栏的名称,只有所有列的集体名称,例如我会知道cols <- c('b','c','d')

有谁知道这样做的方式?

 # your starting data.. data <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) # columns to paste together cols <- c( 'b' , 'c' , 'd' ) # create a new column `x` with the three columns collapsed together data$x <- apply( data[ , cols ] , 1 , paste , collapse = "-" ) # remove the unnecessary columns data <- data[ , !( names( data ) %in% cols ) ] 

作为baptiste答案的一个变体, data定义为你所拥有,而你想要放在一起的列以cols定义

 cols <- c("b", "c", "d") 

您可以将新列添加到data并删除旧data

 data$x <- do.call(paste, c(data[cols], sep="-")) for (co in cols) data[co] <- NULL 

这使

 > data ax 1 1 adg 2 2 beh 3 3 cfi 

使用tidyr包,这可以在一个函数调用中轻松处理。

 data <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) tidyr::unite_(data, paste(colnames(data)[-1], collapse="_"), colnames(data)[-1]) a b_c_d 1 1 a_d_g 2 2 b_e_h 3 3 c_f_i 

编辑:排除第一列,其他一切都被粘贴。

 # tidyr_0.6.3 unite(data, newCol, -a) # or by column index unite(data, newCol, -1) # a newCol # 1 1 a_d_g # 2 2 b_e_h # 3 3 c_f_i 

我会构造一个新的data.frame:

 d <- data.frame('a' = 1:3, 'b' = c('a','b','c'), 'c' = c('d', 'e', 'f'), 'd' = c('g', 'h', 'i')) cols <- c( 'b' , 'c' , 'd' ) data.frame(a = d[, 'a'], x = do.call(paste, c(d[ , cols], list(sep = '-')))) 

只要增加额外的解决scheme与Reduce可能比do.call慢,但探讨比apply更好,因为它会避免matrix转换。 此外,而不是for循环,我们可以使用setdiff来删除不需要的列

 cols <- c('b','c','d') data$x <- Reduce(function(...) paste(..., sep = "-"), data[cols]) data[setdiff(names(data), cols)] # ax # 1 1 adg # 2 2 beh # 3 3 cfi 

或者我们可以使用data.table包来更新data (假设有新的数据)

 library(data.table) setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD[, mget(cols)])] data[, (cols) := NULL] data # ax # 1: 1 adg # 2: 2 beh # 3: 3 cfi 

另一个select是使用.SDcols而不是mget

 setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols] 
 library(plyr) ldply(apply(data, 1, function(x) data.frame( x = paste(x[2:4],sep="",collapse="-")))) # x #1 adg #2 beh #3 cfi # and with just the vector of names you have: ldply(apply(data, 1, function(x) data.frame( x = paste(x[c('b','c','d')],sep="",collapse="-")))) # or equally: mynames <-c('b','c','d') ldply(apply(data, 1, function(x) data.frame( x = paste(x[mynames],sep="",collapse="-")))) 

我将Anthony Damico,Brian Diggs和data_steve的答案作为tbl_df的一个小样本,得到如下结果。

 > data <- data.frame('a' = 1:3, + 'b' = c('a','b','c'), + 'c' = c('d', 'e', 'f'), + 'd' = c('g', 'h', 'i')) > data <- tbl_df(data) > cols <- c("b", "c", "d") > microbenchmark( + do.call(paste, c(data[cols], sep="-")), + apply( data[ , cols ] , 1 , paste , collapse = "-" ), + tidyr::unite_(data, "x", cols, sep="-")$x, + times=1000 + ) Unit: microseconds expr min lq mean median uq max neval do.call(paste, c(data[cols], sep = "-")) 65.248 78.380 93.90888 86.177 99.3090 436.220 1000 apply(data[, cols], 1, paste, collapse = "-") 223.239 263.044 313.11977 289.514 338.5520 743.583 1000 tidyr::unite_(data, "x", cols, sep = "-")$x 376.716 448.120 556.65424 501.877 606.9315 11537.846 1000 

但是,当我用自己的tbl_df进行评估时, tbl_df有100万行10列,结果是完全不同的。

 > microbenchmark( + do.call(paste, c(data[c("a", "b")], sep="-")), + apply( data[ , c("a", "b") ] , 1 , paste , collapse = "-" ), + tidyr::unite_(data, "c", c("a", "b"), sep="-")$c, + times=25 + ) Unit: milliseconds expr min lq mean median uq max neval do.call(paste, c(data[c("a", "b")], sep="-")) 930.7208 951.3048 1129.334 997.2744 1066.084 2169.147 25 apply( data[ , c("a", "b") ] , 1 , paste , collapse = "-" ) 9368.2800 10948.0124 11678.393 11136.3756 11878.308 17587.617 25 tidyr::unite_(data, "c", c("a", "b"), sep="-")$c 968.5861 1008.4716 1095.886 1035.8348 1082.726 1759.349 25 

在我看来, sprintf function在这些答案中也是值得一席之地的。 你可以使用sprintf ,如下所示:

 do.call(sprintf, c(d[cols], '%s-%s-%s')) 

这使:

  [1] "adg" "beh" "cfi" 

并创build所需的数据框:

 data.frame(a = d$a, x = do.call(sprintf, c(d[cols], '%s-%s-%s'))) 

赠送:

  ax 1 1 adg 2 2 beh 3 3 cfi 

尽pipesprintf与@BrianDiggs的do.call / paste组合没有明显的优势,但是当您还想要填充所需string的某些部分或想要指定数字位数时,sprintf尤其有用。 请参阅?sprintf以获得几个选项。


一个更大的数据集的基准:

 # create a larger dataset d2 <- d[sample(1:3,1e6,TRUE),] # benchmark library(microbenchmark) microbenchmark( docp = do.call(paste, c(d2[cols], sep="-")), appl = apply( d2[, cols ] , 1 , paste , collapse = "-" ), tidr = tidyr::unite_(d2, "x", cols, sep="-")$x, docs = do.call(sprintf, c(d2[cols], '%s-%s-%s')), times=10) 

结果是:

 Unit: milliseconds expr min lq mean median uq max neval cld docp 214.1786 226.2835 297.1487 241.6150 409.2495 493.5036 10 a appl 3832.3252 4048.9320 4131.6906 4072.4235 4255.1347 4486.9787 10 c tidr 206.9326 216.8619 275.4556 252.1381 318.4249 407.9816 10 a docs 413.9073 443.1550 490.6520 453.1635 530.1318 659.8400 10 b 

使用的数据:

 d <- data.frame(a = 1:3, b = c('a','b','c'), c = c('d','e','f'), d = c('g','h','i'))