suprimir AN en pasta ()

En cuanto a la recompensa

La solución paste2 de Ben Bolker produce un "" cuando las cuerdas que se pegan contienen NA en la misma posición. Me gusta esto,

 > paste2(c("a","b", "c", NA), c("A","B", NA, NA)) [1] "a, A" "b, B" "c" "" 

El cuarto elemento es un "" lugar de una NA como este,

 [1] "a, A" "b, B" "c" NA 

Ofrezco esta pequeña recompensa para cualquiera que pueda arreglar esto.

Pregunta original

He leído la página de ayuda ?paste , pero no entiendo cómo hacer que R ignore las de NA . Yo hago lo siguiente,

 foo <- LETTERS[1:4] foo[4] <- NA foo [1] "A" "B" "C" NA paste(1:4, foo, sep = ", ") 

y obten

 [1] "1, A" "2, B" "3, C" "4, NA" 

Lo que me gustaría obtener,

 [1] "1, A" "2, B" "3, C" "4" 

Podría hacer esto,

 sub(', NA$', '', paste(1:4, foo, sep = ", ")) [1] "1, A" "2, B" "3, C" "4" 

pero eso parece un desvío.

A los efectos de una “NA verdadera”: Parece que la ruta más directa es simplemente modificar el valor devuelto por paste2 para que sea NA cuando el valor es ""

  paste3 <- function(...,sep=", ") { L <- list(...) L <- lapply(L,function(x) {x[is.na(x)] <- ""; x}) ret <-gsub(paste0("(^",sep,"|",sep,"$)"),"", gsub(paste0(sep,sep),sep, do.call(paste,c(L,list(sep=sep))))) is.na(ret) <- ret=="" ret } val<- paste3(c("a","b", "c", NA), c("A","B", NA, NA)) val #[1] "a, A" "b, B" "c" NA 

Una función que da seguimiento a la respuesta de @ ErikShilt y al comentario de @ agstudy. Generaliza la situación ligeramente al permitir que se especifique sep y al manejar casos donde cualquier elemento (primero, último o intermedio) es NA . (Podría romperse si hay múltiples valores de NA seguidos o en otros casos complicados …) Por cierto, tenga en cuenta que esta situación se describe exactamente en el segundo párrafo de la sección Details de ?paste , que indica que a al menos los autores de R conocen la situación (aunque no se ofrece ninguna solución).

 paste2 <- function(...,sep=", ") { L <- list(...) L <- lapply(L,function(x) {x[is.na(x)] <- ""; x}) gsub(paste0("(^",sep,"|",sep,"$)"),"", gsub(paste0(sep,sep),sep, do.call(paste,c(L,list(sep=sep))))) } foo <- c(LETTERS[1:3],NA) bar <- c(NA,2:4) baz <- c("a",NA,"c","d") paste2(foo,bar,baz) # [1] "A, a" "B, 2" "C, 3, c" "4, d" 

Esto no maneja las sugerencias de @ agstudy de (1) incorporar el argumento de collapse opcional; (2) haciendo NA -removal opcional agregando un argumento na.rm (y estableciendo el valor por defecto a FALSE para hacer que paste2 compatible con paste hacia atrás). Si uno quisiera hacer esto más sofisticado (es decir, eliminar múltiples NA consecutivas) o más rápido, podría tener sentido escribirlo en C ++ a través de Rcpp (no sé mucho sobre el manejo de cadenas de C ++, pero podría no ser demasiado difícil - - ver convertir Rcpp :: CharacterVector a std :: string y Concatenar cadenas no funciona como se espera para empezar ...)

Como mencionó Ben Bolker, los enfoques anteriores pueden fallar si hay varias NA consecutivas. Probé con un enfoque diferente que parece superar esto.

 paste4 <- function(x, sep = ", ") { x <- gsub("^\\s+|\\s+$", "", x) ret <- paste(x[!is.na(x) & !(x %in% "")], collapse = sep) is.na(ret) <- ret == "" return(ret) } 

La segunda línea elimina el espacio en blanco adicional introducido al concatenar texto y números. El código anterior se puede usar para concatenar múltiples columnas (o filas) de un dataframe usando el comando apply , o reempaquetado para forzar primero los datos en un dataframe si es necesario.

 EDIT 

Después de unas horas más pensé que el siguiente código incorpora las sugerencias anteriores para permitir la especificación de las opciones de colapso y na.rm.

 paste5 <- function(..., sep = " ", collapse = NULL, na.rm = F) { if (na.rm == F) paste(..., sep = sep, collapse = collapse) else if (na.rm == T) { paste.na <- function(x, sep) { x <- gsub("^\\s+|\\s+$", "", x) ret <- paste(na.omit(x), collapse = sep) is.na(ret) <- ret == "" return(ret) } df <- data.frame(..., stringsAsFactors = F) ret <- apply(df, 1, FUN = function(x) paste.na(x, sep)) if (is.null(collapse)) ret else { paste.na(ret, sep = collapse) } } } 

Como se na.omit(x) anteriormente, na.omit(x) puede reemplazarse con (x[!is.na(x) & !(x %in% "") para también soltar las cadenas vacías si así lo desea. Nota, usando colapso con na.rm = T devuelve una cadena sin ningún "NA", aunque esto podría cambiarse reemplazando la última línea de código con paste(ret, collapse = collapse) .

 nth <- paste0(1:12, c("st", "nd", "rd", rep("th", 9))) mnth <- month.abb nth[4:5] <- NA mnth[5:6] <- NA paste5(mnth, nth) [1] "Jan 1st" "Feb 2nd" "Mar 3rd" "Apr NA" "NA NA" "NA 6th" "Jul 7th" "Aug 8th" "Sep 9th" "Oct 10th" "Nov 11th" "Dec 12th" paste5(mnth, nth, sep = ": ", collapse = "; ", na.rm = T) [1] "Jan: 1st; Feb: 2nd; Mar: 3rd; Apr; 6th; Jul: 7th; Aug: 8th; Sep: 9th; Oct: 10th; Nov: 11th; Dec: 12th" paste3(c("a","b", "c", NA), c("A","B", NA, NA), c(1,2,NA,4), c(5,6,7,8)) [1] "a, A, 1, 5" "b, B, 2, 6" "c, , 7" "4, 8" paste5(c("a","b", "c", NA), c("A","B", NA, NA), c(1,2,NA,4), c(5,6,7,8), sep = ", ", na.rm = T) [1] "a, A, 1, 5" "b, B, 2, 6" "c, 7" "4, 8" 

Sé que esta pregunta tiene muchos años, pero sigue siendo el mejor resultado de google para r paste na . Estaba buscando una solución rápida a lo que asumí que era un problema simple, y me sorprendió un poco la complejidad de las respuestas. Opté por una solución diferente y la publico aquí en caso de que alguien más esté interesado.

 bar <- apply(cbind(1:4, foo), 1, function(x) paste(x[!is.na(x)], collapse = ", ")) bar [1] "1, A" "2, B" "3, C" "4" 

En caso de que no sea obvio, esto funcionará en cualquier número de vecotrs con NA en cualquier posición.

En mi humilde opinión, la ventaja de esto sobre las respuestas existentes es la legibilidad. Es un juego de una sola línea, que siempre es agradable, y no depende de un montón de expresiones regulares y declaraciones de if / else que puedan hacer tropezar a tus colegas o tu yo futuro. La respuesta de Erik Shitts principalmente comparte estas ventajas, pero supone que solo hay dos vectores y que solo el último de ellos contiene NA.

Mi solución no cumple con el requisito en su edición, porque mi proyecto tiene el requisito opuesto. Sin embargo, puede resolverlo fácilmente agregando una segunda línea tomada de la respuesta de 42 :

 is.na(bar) <- bar == "" 

Puede usar ifelse , una construcción vectorizada if-else para determinar si un valor es NA y sustituirlo por un espacio en blanco. Luego usarás gsub para quitar el final “,” si no está seguido por ninguna otra cadena.

 gsub(", $", "", paste(1:4, ifelse(is.na(foo), "", foo), sep = ", ")) 

Tu respuesta es correcta No hay una mejor manera de hacerlo. Este problema se menciona explícitamente en la documentación de pegado en la sección Detalles.

O hacer una mutación después de pegar () y eliminar NA:

 data <- data.frame(col1= c(rep(NA, 5)), col2 = c(2:6)) %>% mutate(col3 = paste(col1, col2)) %>% mutate(col3 = gsub('NA', '', col3))