Ben Bolker的paste2
- 当粘贴的字符串包含""
在同一位置时,解决方案会生成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
。我做了以下,
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"
但这似乎是绕道而行。
答案 0 :(得分:28)
出于“真-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
答案 1 :(得分:14)
跟进@ ErikShilt的答案和@ agstudy评论的函数。它通过允许指定sep
并处理任何元素(first,last或intermediate)为NA
的情况,略微概括了这种情况。 (如果一行中有多个NA
值,或者在其他棘手的情况下,它可能会中断...)顺便说一下,请注意这种情况在Details
部分的第二段中有详细描述?paste
,表示至少R作者知道这种情况(虽然没有提供解决方案)。
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的建议(1)包含可选的collapse
参数; (2)通过添加NA
参数(并将默认值设置为na.rm
以使FALSE
与paste2
向后兼容)使paste
- 删除可选。如果有人想让它更复杂(即删除多个顺序NA
)或更快,那么通过Rcpp在C ++中编写它可能是有意义的(我不太了解C ++的字符串处理,但它可能不会太难了 - 请看convert Rcpp::CharacterVector to std::string和Concatenating strings doesn't work as expected开始......)
答案 2 :(得分:11)
正如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
命令连接数据帧的多个列(或行),或者重新打包以在需要时首先将数据强制转换为数据帧。
EDIT
经过几个小时后,我认为以下代码包含了上述建议,以便指定崩溃和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)
可以替换为(x[!is.na(x) & !(x %in% "")
以删除空字符串。注意,使用na.rm = T的折叠返回一个没有任何&#34; NA&#34;的字符串,尽管可以通过用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"
答案 3 :(得分:11)
我知道这个问题已经存在很多年了,但它仍然是r paste na
的最佳google结果。我正在寻找一个快速的解决方案来解决我认为是一个简单的问题,并且有点对答案的复杂性感到吃惊。我选择了另一种解决方案,并将其发布在此处以防其他人感兴趣。
bar <- apply(cbind(1:4, foo), 1, function(x) paste(x[!is.na(x)], collapse = ", "))
bar
[1] "1, A" "2, B" "3, C" "4"
如果不是很明显,这将适用于任何职位上具有NA的任何数量的操作员。
恕我直言,这在现有答案上的优势是易读性。它是一个单行,总是很好,并且它不依赖于一堆正则表达式和if / else语句可能会绊倒你的同事或未来的自我。 Erik Shitts' answer主要分享这些优点,但假设只有两个向量,并且只有最后一个向量包含NA。我的解决方案并不满足您的编辑要求,因为我的项目有相反的要求。但是,您可以通过添加从42-'s answer借用的第二行来轻松解决此问题:
is.na(bar) <- bar == ""
答案 4 :(得分:5)
您可以使用ifelse
,一个向量化的if-else结构来确定值是否为NA并替换为空白。然后,如果没有任何其他字符串,则使用gsub去掉尾随的“,”。
gsub(", $", "", paste(1:4, ifelse(is.na(foo), "", foo), sep = ", "))
你的答案是对的。没有更好的方法来做到这一点。 “详细信息”部分的paste documentation中明确提到了此问题。
答案 5 :(得分:3)
如果使用tidyverse处理df或tibble,我在mutate_all
或mutate_at
之前使用str_replace_na
或paste
和unite
来避免粘贴NA。 >
library(tidyverse)
new_df <- df %>%
mutate_all(~str_replace_na(., "")) %>%
mutate(combo_var = paste0(var1, var2, var3))
OR
new_df <- df %>%
mutate_at(c('var1', 'var2'), ~str_replace_na(., "")) %>%
mutate(combo_var = paste0(var1, var2))
答案 6 :(得分:1)
或者在粘贴()后删除NAs:
data <- data.frame(col1= c(rep(NA, 5)), col2 = c(2:6)) %>%
mutate(col3 = paste(col1, col2)) %>%
mutate(col3 = gsub('NA', '', col3))
答案 7 :(得分:1)
Joe解决方案(https://stackoverflow.com/a/49201394/3831096)的一种变体,它同时尊重sep
和collapse
并在所有值为NA时返回NA为:
paste_missing <- function(..., sep=" ", collapse=NULL) {
ret <-
apply(
X=cbind(...),
MARGIN=1,
FUN=function(x) {
if (all(is.na(x))) {
NA_character_
} else {
paste(x[!is.na(x)], collapse = sep)
}
}
)
if (!is.null(collapse)) {
paste(ret, collapse=collapse)
} else {
ret
}
}
答案 8 :(得分:0)
或者在粘贴str_replace_all后删除NAs
data$1 <- str_replace_all(data$1, "NA", "")
答案 9 :(得分:0)
与当前解决方案(空字符串,“ NA”字符串,两个以上自变量,使用塌陷自变量...)相比,这里的解决方案行为更像粘贴,并且处理更多的边沿情况。
paste2 <- function(..., sep = " ", collapse = NULL, na.rm = FALSE){
# in default case, use paste
if(!na.rm) return(paste(..., sep = sep, collapse = collapse))
# cbind is convenient to recycle, it warns though so use suppressWarnings
dots <- suppressWarnings(cbind(...))
res <- apply(dots, 1, function(...) {
if(all(is.na(c(...)))) return(NA)
do.call(paste, as.list(c(na.omit(c(...)), sep = sep)))
})
if(is.null(collapse)) res else
paste(na.omit(res), collapse = collapse)
}
# behaves like `paste()` by default
paste2(c("a","b", "c", NA), c("A","B", NA, NA))
#> [1] "a A" "b B" "c NA" "NA NA"
# trigger desired behavior by setting `na.rm = TRUE` and `sep = ", "`
paste2(c("a","b", "c", NA), c("A","B", NA, NA), sep = ",", na.rm = TRUE)
#> [1] "a,A" "b,B" "c" NA
# handles hedge cases
paste2(c("a","b", "c", NA, "", "", ""),
c("a","b", "c", NA, "", "", "NA"),
c("A","B", NA, NA, NA, "", ""),
sep = ",", na.rm = TRUE)
#> [1] "a,a,A" "b,b,B" "c,c" NA "," ",," ",NA,"
由reprex package(v0.3.0)于2019-10-01创建
答案 10 :(得分:0)
我找到了一个针对该问题的dplyr
/ tidyverse
解决方案,在我看来这是相当优雅的。
library(data.table)
library(tidyverse)
foo <- LETTERS[1:4]
foo[4] <- NA
dt <- data.table(foo, num = 1:4)
dt %>% unite(., col = "New.Col", num, foo, na.rm=TRUE, sep = ",")
> New.Col
1: 1,A
2: 2,B
3: 3,C
4: 4
答案 11 :(得分:0)
更新@Erik Shilts 解决方案以去掉最后一个逗号:
x = gsub(",$", "", paste(1:4, ifelse(is.na(foo), "", foo), sep = ","))
然后为了摆脱尾随的 last","
只需再重复一次:
x <- gsub(",$", "", x)
答案 12 :(得分:0)
这对我有用
library(stringr)
foo <- LETTERS[1:4]
foo[4] <- NA
foo
# [1] "A" "B" "C" NA
if_else(!is.na(foo),
str_c(1:4, str_replace_na(foo, ""), sep = ", "),
str_c(1:4, str_replace_na(foo, ""), sep = "")
)
# [1] "1, A" "2, B" "3, C" "4"
答案 13 :(得分:0)
这可以在一行中实现。 例如,
vec<-c("A","B",NA,"D","E")
res<-paste(vec[!is.na(vec)], collapse=',' )
print(res)
[1] "A,B,D,E"