Saya memiliki banyak kolom dalam kerangka data yang ingin saya tempelkan bersama (dipisahkan oleh "-") sebagai berikut:
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
Yang saya ingin menjadi:
a x
1 a-d-g
2 b-e-h
3 c-f-i
Saya biasanya dapat melakukan ini dengan:
within(data, x <- paste(b,c,d,sep='-'))
dan kemudian menghapus kolom lama, tapi sayangnya saya tidak tahu nama kolom secara spesifik, hanya nama kolektif untuk semua kolom, misalnya saya akan tahu bahwa cols <- c('b','c','d')
Adakah yang tahu cara melakukan ini?
do.call
?evil(parse(...))
, tapi saya yakindo.call
itu panggilan yang tepat di sini.collapse = "-"
? kepadapaste
?Sebagai varian dari jawaban baptiste , dengan
data
didefinisikan sebagai yang Anda miliki dan kolom yang ingin Anda susun ditentukancols
cols <- c("b", "c", "d")
Anda dapat menambahkan kolom baru ke
data
dan menghapus yang lama dengandata$x <- do.call(paste, c(data[cols], sep="-")) for (co in cols) data[co] <- NULL
pemberian yang mana
> data a x 1 1 a-d-g 2 2 b-e-h 3 3 c-f-i
sumber
data.frame
vektor dengan satu karakter akan menjadi pengindeksan kolom, meskipun argumen pertama biasanya adalah indeks baris.Dengan menggunakan
tidyr
paket, ini dapat dengan mudah ditangani dalam 1 panggilan fungsi.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
Edit: Kecualikan kolom pertama, yang lainnya akan ditempel.
# 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
sumber
within(data, x <- paste(b,c,d,sep='-'))
seperti yang mereka ilustrasikan.unite_(data, "b_c_d", cols)
akan, atau tergantung pada data.frame aktual mereka,unite(data, b_c_d, -a)
mungkin juga menjadi kandidat.Saya akan membuat data.frame baru:
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 = '-'))))
sumber
d[ , cols]
Anda mungkin ingin menggunakand[ , names(d) != 'a']
jika semua kecualia
kolom akan ditempelkan bersama.cbind(a = d['a'], x = do.call(paste, c(d[cols], sep = '-')))
, misalnya menghindari koma,list
dandata.frame
saat menggunakandata.frame
metodecbind
Hanya untuk menambahkan solusi tambahan
Reduce
yang mungkin lebih lambatdo.call
tetapi mungkin lebih baik daripadaapply
karena itu akan menghindarimatrix
konversi. Juga, sebagai gantinyafor
kita bisa menggunakan loopsetdiff
untuk menghapus kolom yang tidak diinginkancols <- 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
Atau kami dapat memperbarui
data
di tempat menggunakandata.table
paket (dengan asumsi data baru)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
Pilihan lainnya adalah menggunakan
.SDcols
sebagai penggantimget
dalamsetDT(data)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols]
sumber
Saya membandingkan jawaban Anthony Damico, Brian Diggs dan data_steve pada sampel kecil
tbl_df
dan mendapatkan hasil sebagai berikut.> 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
Namun, ketika saya mengevaluasi sendiri
tbl_df
dengan ~ 1 juta baris dan 10 kolom, hasilnya sangat berbeda.> 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
sumber
Menurut pendapat saya,
sprintf
-fungsi juga layak mendapat tempat di antara jawaban-jawaban ini. Anda dapat menggunakansprintf
sebagai berikut:do.call(sprintf, c(d[cols], '%s-%s-%s'))
pemberian yang mana:
[1] "a-d-g" "b-e-h" "c-f-i"
Dan untuk membuat kerangka data yang dibutuhkan:
data.frame(a = d$a, x = do.call(sprintf, c(d[cols], '%s-%s-%s')))
memberi:
a x 1 1 a-d-g 2 2 b-e-h 3 3 c-f-i
Meskipun
sprintf
tidak memiliki keunggulan yang jelas atas kombinasido.call
/paste
@BrianDiggs, ini sangat berguna ketika Anda juga ingin memasukkan bagian tertentu dari string yang diinginkan atau ketika Anda ingin menentukan jumlah digit. Lihat?sprintf
beberapa opsi.Varian lain akan digunakan
pmap
darimendengkur:pmap(d[2:4], paste, sep = '-')
Catatan:
pmap
solusi ini hanya berfungsi jika kolom bukan merupakan faktor.Tolok ukur pada kumpulan data yang lebih besar:
# 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)
menghasilkan:
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
Data yang digunakan:
d <- data.frame(a = 1:3, b = c('a','b','c'), c = c('d','e','f'), d = c('g','h','i'))
sumber
Berikut adalah pendekatan yang tidak biasa (tapi cepat): gunakan
fwrite
daridata.table
untuk "menempel" kolom bersama-sama, danfread
untuk membacanya kembali. Demi kenyamanan, saya telah menulis langkah-langkah sebagai fungsi yang disebutfpaste
:fpaste <- function(dt, sep = ",") { x <- tempfile() fwrite(dt, file = x, sep = sep, col.names = FALSE) fread(x, sep = "\n", header = FALSE) }
Berikut contohnya:
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") fpaste(d[cols], "-") # V1 # 1: a-d-g # 2: b-e-h # 3: c-f-i
Bagaimana cara kerjanya?
d2 <- d[sample(1:3,1e6,TRUE),] library(microbenchmark) microbenchmark( docp = do.call(paste, c(d2[cols], sep="-")), tidr = tidyr::unite_(d2, "x", cols, sep="-")$x, docs = do.call(sprintf, c(d2[cols], '%s-%s-%s')), appl = apply( d2[, cols ] , 1 , paste , collapse = "-" ), fpaste = fpaste(d2[cols], "-")$V1, dt2 = as.data.table(d2)[, x := Reduce(function(...) paste(..., sep = "-"), .SD), .SDcols = cols][], times=10) # Unit: milliseconds # expr min lq mean median uq max neval # docp 215.34536 217.22102 220.3603 221.44104 223.27224 225.0906 10 # tidr 215.19907 215.81210 220.7131 220.09636 225.32717 229.6822 10 # docs 281.16679 285.49786 289.4514 286.68738 290.17249 312.5484 10 # appl 2816.61899 3106.19944 3259.3924 3266.45186 3401.80291 3804.7263 10 # fpaste 88.57108 89.67795 101.1524 90.59217 91.76415 197.1555 10 # dt2 301.95508 310.79082 384.8247 316.29807 383.94993 874.4472 10
sumber
TMPDIR=/dev/shm R
) tetapi saya tidak melihat perbedaan besar dalam perbandingan dengan hasil ini. Saya juga belum bermain-main sama sekali dengan jumlah utas yang digunakan untukfread
ataufwrite
untuk melihat bagaimana pengaruhnya terhadap hasil.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="-"))))
sumber
Saya tahu ini adalah pertanyaan lama, tetapi saya berpikir bahwa saya tetap harus menyajikan solusi sederhana menggunakan fungsi paste () seperti yang disarankan oleh penanya:
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
sumber