-->

多列粘贴在一起(Paste multiple columns together)

2019-07-18 20:48发布

我有一堆我要粘贴在一起(由分隔“ - ”)在数据帧的列如下:

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

这是我想成为:

a x  
1 a-d-g  
2 b-e-h  
3 c-f-i  

我通常能做到这一点有:

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

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

有谁知道这样做的方法吗?

Answer 1:

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


Answer 2:

作为一个变型巴蒂斯特的回答 ,用data和定义你要放在一起中定义的列cols

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

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

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

这使

> data
  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i


Answer 3:

使用tidyr包,这可以很容易地在1函数调用处理。

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


Answer 4:

我想制定一个新的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 = '-'))))


Answer 5:

只需添加额外的解决方案Reduce其可能慢于do.call但probebly优于apply ,因为它避免了matrix转换。 此外,而不是for循环中,我们可以只使用setdiff ,以消除不需要的列

cols <- c('b','c','d')
data$x <- Reduce(function(...) paste(..., sep = "-"), data[cols])
data[setdiff(names(data), cols)]
#   a     x
# 1 1 a-d-g
# 2 2 b-e-h
# 3 3 c-f-i

另外,我们可以更新data代替使用data.table包(假设新的数据)

library(data.table)
setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD[, mget(cols)])]
data[, (cols) := NULL]
data
#    a     x
# 1: 1 a-d-g
# 2: 2 b-e-h
# 3: 3 c-f-i

另一种选择是使用.SDcols代替mget作为

setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols]


Answer 6:

我基准安东尼达米科,布莱恩·迪格斯和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用约1万行和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


Answer 7:

在我看来, sprintf -function值得这些答案中的地方为好。 您可以使用sprintf如下:

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

这使:

 [1] "a-d-g" "b-e-h" "c-f-i"

并创建所需要的数据框:

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

赠送:

  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i

虽然sprintf没有在明显的优势do.call / paste @BrianDiggs的组合,它是特别有用的,当你也想所需的字符串的垫某些部分或当你想指定位的数量。 见?sprintf的几个选项。

另一种变体是使用pmap从purrr :

pmap(d[2:4], paste, sep = '-')

注:此pmap解决方案,只有当列不是因素的作品。


在更大的数据集的基准:

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


Answer 8:

library(plyr)

ldply(apply(data, 1, function(x) data.frame(
                      x = paste(x[2:4],sep="",collapse="-"))))

#      x
#1 a-d-g
#2 b-e-h
#3 c-f-i

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


文章来源: Paste multiple columns together
标签: r paste r-faq