抑制粘贴中的NA()

Eri*_*ail 32 r paste na

关于赏金

Ben Bolkerpaste2解决方案产生了一个""粘贴的字符串包含NA在同一位置的时间.像这样,

> paste2(c("a","b", "c", NA), c("A","B", NA, NA))
[1] "a, A" "b, B" "c"    ""
Run Code Online (Sandbox Code Playgroud)

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

[1] "a, A" "b, B" "c"  NA     
Run Code Online (Sandbox Code Playgroud)

我正在为能解决这个问题的人提供这笔小额奖金.

原始问题

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

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

得到

[1] "1, A"  "2, B"  "3, C"  "4, NA"
Run Code Online (Sandbox Code Playgroud)

我想得到什么,

[1] "1, A" "2, B" "3, C" "4"
Run Code Online (Sandbox Code Playgroud)

我可以这样做,

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

但这似乎是绕道而行.

42-*_*42- 35

对于"真正的-NA"的宗旨:似乎最直接的途径就是修改返回的值paste2NA当值""

 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    
Run Code Online (Sandbox Code Playgroud)


Joe*_*Joe 19

我知道这个问题已有很多年了,但它仍然是google的最佳结果r paste na.我正在寻找一个快速的解决方案来解决我认为是一个简单的问题,并且有点对答案的复杂性感到吃惊.我选择了另一种解决方案,并将其发布在此处以防其他人感兴趣.

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

如果不是很明显,这将适用于任何位置具有NA的任何数量的操作员.

恕我直言,这在现有答案上的优势是易读性.这是一个单行,总是很好,它不依赖于一堆正则表达式和if/else语句可能会绊倒你的同事或未来的自我.Erik Shitts的答案主要是分享这些优点,但假设只有两个向量,而且只有最后一个包含NA.

我的解决方案不满足您的编辑要求,因为我的项目有相反的要求.但是,您可以通过添加从42-答案中借用的第二行来轻松解决此问题:

is.na(bar) <- bar == ""
Run Code Online (Sandbox Code Playgroud)

  • 这对我有用。很简单。我希望这是用糊状物运送的。 (2认同)

Ben*_*ker 14

一个函数跟随@ErikShilt的回答和@ agstudy的评论.它通过允许sep指定和处理任何元素(第一个,最后一个或中间元素)的情况来略微概括情况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"   
Run Code Online (Sandbox Code Playgroud)

这不能处理@ agstudy的建议(1)包含可选collapse参数; (2)使得NA通过添加去除-可选的na.rm参数(和设置默认到FALSE使paste2后向兼容paste).如果一个人想要使它更复杂(即删除多个顺序NAs)或更快,那么通过Rcpp用C++编写它可能是有意义的(我不太了解C++的字符串处理,但它可能不太难 -请参阅将Rcpp :: CharacterVector转换为std :: string,并且连接字符串无法按预期启动......)


han*_*101 14

我找到了该问题的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
Run Code Online (Sandbox Code Playgroud)

  • “dplyr”再次提供了一种优雅的解决方案 (2认同)

JWi*_*man 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)
  }
Run Code Online (Sandbox Code Playgroud)

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

EDIT
Run Code Online (Sandbox Code Playgroud)

经过几个小时后,我认为以下代码包含了上述建议,以允许指定崩溃和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)
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

如上所述,如果需要,na.omit(x)可以替换(x[!is.na(x) & !(x %in% "")为也删除空字符串.注意,使用na.rm = T的折叠返回没有任何"NA"的字符串,尽管可以通过替换最后一行代码来更改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" 
Run Code Online (Sandbox Code Playgroud)


小智 6

如果使用 tidyverse 处理 df 或 tibbles,我会在 or 之前使用ormutate_all以避免粘贴NA。mutate_atstr_replace_napasteunite

library(tidyverse)
new_df <- df  %>%
mutate_all(~str_replace_na(., "")) %>%
mutate(combo_var = paste0(var1, var2, var3))
Run Code Online (Sandbox Code Playgroud)

或者

new_df <- df  %>%
mutate_at(c('var1', 'var2'), ~str_replace_na(., "")) %>%
mutate(combo_var = paste0(var1, var2))
Run Code Online (Sandbox Code Playgroud)


Eri*_*lts 5

您可以使用ifelse向量化的if-else结构来确定值是否为NA并替换为空白.然后,如果没有任何其他字符串,则使用gsub去掉尾随的",".

gsub(", $", "", paste(1:4, ifelse(is.na(foo), "", foo), sep = ", "))
Run Code Online (Sandbox Code Playgroud)

你的答案是对的.没有更好的方法来做到这一点."详细信息"部分的粘贴文档中明确提到了此问题.

  • @EricFail` paste`就好了.您希望使用它来执行非标准的操作,因此您需要做一些工作来指定所需的行为才有意义.它目前的工作方式是精细的IMO. (2认同)
  • @Dason,我不是说粘贴不好,我只是想解决一个我认为其他人也会遇到的问题。在我的“真实”示例中,我尝试将许多变量组合成一个向量。我想没有捷径可以解决这个问题。无论如何,感谢您的回复! (2认同)