在paste()中抑制NAs

关于赏金

Ben Bolker的paste2解决scheme产生一个""当粘贴的string包含NA在同一个位置。 喜欢这个,

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

第四个元素是一个""而不是一个NA像这样,

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

我为所有能解决这个问题的人提供这个小小的奖励。

原来的问题

我已阅读帮助页面?paste ,但我不明白如何让R忽略NA s。 我做了以下,

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

并得到

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

我想得到什么,

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

我可以这样做,

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

但这似乎是一个弯路。

出于“真NA”的目的:似乎最直接的路由就是修改paste2返回的值为"" NA当值为""

  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 

一个函数,跟在@ ErikShilt的回答和@ agstudy的评论。 它通过允许指定sep并处理任何元素(第一个,最后一个或中间)是NA的情况,略微概括了这种情况。 (如果在一行中有多个NA值,或者在其他棘手的情况下,它可能会中断…顺便说一句,请注意,这种情况正好在?pasteDetails部分的第二段中描述,这表明在R作者至less知道这种情况(虽然没有提供解决scheme)。

 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" 

这不处理@ agstudy的build议:(1)包含可选的collapse参数; (2)通过添加一个na.rm参数(并将默认值设置为FALSE使paste2向后兼容paste ),使NA -removal成为可选项。 如果有人想让这个更复杂的(即删除多个连续NA )或更快,它可能是有道理的,通过Rcpp写在C ++(我不太了解C ++的string处理,但它可能不是太难 – – 请参阅将Rcpp :: CharacterVector转换为std :: string,并且按照预期的方式连接string不起作用 …)

正如本·博尔克(Ben Bolker)提到的,如果连续存在多个NA,上述方法可能会失败。 我尝试了一种似乎克服了这个问题的方法。

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

第二行删除了连接文本和数字时引入的额外空格。 上面的代码可以用来使用apply命令连接dataframe的多个列(或行),或者根据需要重新打包以将数据强制转换为dataframe。

 EDIT 

再过几个小时后,我认为下面的代码结合了上面的build议来允许指定折叠和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) } } } 

如上所述, na.omit(x)可以被replace为(x[!is.na(x) & !(x %in% "") ,如果需要也可以删除空string。注意,使用collapse与na.rm = T返回一个没有任何“NA”的string,虽然这可以通过用paste(ret, collapse = collapse)replace最后一行代码来改变。

 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" 

你可以使用ifelse ,一个vector化的if-else结构来确定一个值是否为NA,并replace为空白。 如果没有任何其他string跟随,你将使用gsub去掉尾随的“,”。

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

你的回答是正确的。 没有更好的方法来做到这一点。 此问题在“详细信息”部分的粘贴文档中明确提到。