vectores unir extraer elementos concatenar como combinar columnas campos r paste r-faq

unir - extraer columnas en r



Pegar varias columnas juntas (9)

Analicé las respuestas de Anthony Damico, Brian Diggs y data_steve en una pequeña muestra tbl_df y obtuve los siguientes resultados.

> 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

Sin embargo, cuando evalué en mi propio tbl_df con ~ 1 millón de filas y 10 columnas, los resultados fueron bastante diferentes.

> 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

Tengo un grupo de columnas en un marco de datos que quiero pegar juntas (separadas por "-") de la siguiente manera:

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

Que quiero ser:

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

Normalmente podría hacer esto con:

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

y luego eliminar las columnas antiguas, pero desafortunadamente no sé específicamente los nombres de las columnas, solo un nombre colectivo para todas las columnas, por ejemplo, sé que cols <- c(''b'',''c'',''d'')

¿Alguien sabe una manera de hacer esto?


Como una variante de la respuesta de Baptiste , con los data definidos como usted y las columnas que desea unir definidas en cols

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

Puede agregar la nueva columna a los data y eliminar los antiguos con

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

lo que da

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


Construiría un nuevo 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 = ''-''))))


En mi opinión, la función sprintf también merece un lugar entre estas respuestas. Puedes usar sprintf siguiente manera:

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

lo que da:

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

Y para crear el marco de datos requerido:

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

dando:

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

Aunque sprintf no tiene una clara ventaja sobre la combinación do.call / paste de @BrianDiggs, es especialmente útil cuando también desea rellenar ciertas partes de la cadena deseada o cuando desea especificar el número de dígitos. Ver ?sprintf para las diversas opciones.

Un punto de referencia en un conjunto de datos más grande:

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

resultados en:

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

Datos usados:

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


Solo para agregar una solución adicional con Reduce que probablemente sea más lenta que do.call pero con mejor funcionamiento que apply porque evitará la conversión de la matrix . Además, en lugar de un bucle for , podríamos usar setdiff para eliminar columnas no deseadas

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

De forma alternativa, podríamos actualizar los data en el lugar utilizando el paquete data.table (suponiendo que hay datos nuevos)

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

Otra opción es usar .SDcols lugar de mget como en

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


Usando el paquete tidyr , esto se puede manejar fácilmente en una llamada de función.

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

Editar: Excluya la primera columna, todo lo demás se pega.

# 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


Utilice tidyr :: unite - todo si no necesita excluir ninguna columna.

unite(data,newCol,everything()) newCol 1 1_a_d_g 2 2_b_e_h 3 3_c_f_i


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


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