111

次のように、一緒に貼り付けたい(「-」で区切られた)データフレームに多数の列があります。

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

誰かがこれを行う方法を知っていますか?

4

10 に答える 10

112
# 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 ) ]
于 2013-01-28T18:31:38.917 に答える
54

baptiste's answer のバリアントとして、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
于 2013-01-28T19:29:40.777 に答える
43

パッケージを使用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
于 2015-10-07T19:40:49.843 に答える
15

新しい 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 = '-'))))
于 2013-01-28T18:37:40.257 に答える
10

変換を回避するため、Reduceおそらくよりも遅いが、おそらくよりもdo.call優れている追加のソリューションを追加するだけです。また、代わりに、不要な列を削除するために使用できるループapplymatrixforsetdiff

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

別のオプションは、 as の.SDcols代わりに使用することですmget

setDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols]
于 2015-10-08T12:02:03.960 に答える
7

私の意見では、sprintf関数もこれらの答えの中に位置するに値します。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

@BrianDiggsの/の組み合わせに勝るsprintf明確な利点はありませんが、目的の文字列の特定の部分をパディングしたい場合や、桁数を指定したい場合に特に便利です。いくつかのオプションについては、を参照してください。do.callpaste?sprintf

別のバリアントは、pmapfrom を使用することです:

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')) 
于 2017-01-05T16:29:36.977 に答える
1

これは古い質問であることは知っていますが、質問者が提案したように、paste() 関数を使用して簡単な解決策を提示する必要があると考えました。

data_1<-data.frame(a=data$a,"x"=paste(data$b,data$c,data$d,sep="-")) 
data_1
  a     x
1 1 a-d-g
2 2 b-e-h
3 3 c-f-i
于 2020-07-15T15:32:16.337 に答える
0
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="-"))))    
于 2013-01-28T18:31:40.677 に答える