Подавлять НС в пасте()
Что касается награды
Ben Bolker paste2
-solution создает ""
, когда вставляемые строки содержат 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"
но это похоже на объезд.
Ответы
Ответ 1
Для целей "истина-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
Ответ 2
Я знаю, что этому вопросу уже много лет, но он все еще является лучшим результатом 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"
В случае, если это не очевидно, это будет работать на любом количестве векторов с АН в любых позициях.
ИМХО, преимущество этого перед существующими ответами - разборчивость. Это однострочный текст, который всегда хорош, и он не опирается на кучу регулярных выражений и утверждений if/else, которые могут сбить с толку ваших коллег или будущее. Ответ эрика Шитца в основном разделяет эти преимущества, но предполагает, что есть только два вектора и что только последний из них содержит NA.
Мое решение не удовлетворяет требованию в вашем редактировании, потому что мой проект имеет противоположное требование. Однако вы можете легко решить эту проблему, добавив вторую строку, заимствованную из 42- ответа:
is.na(bar) <- bar == ""
Ответ 3
Функция, которая следует за ответом @ErikShilt и комментарием @agstudy. Это немного упрощает ситуацию, позволяя указать sep
и обрабатывать случаи, когда любой элемент (первый, последний или промежуточный) 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
-removal необязательным, добавив аргумент na.rm
(и установив по умолчанию значение FALSE
, чтобы сделать paste2
обратно совместимым с paste
). Если бы кто-то хотел сделать это более сложным (т.е. Удалить несколько последовательных NA
s) или быстрее, может возникнуть смысл записать его на С++ через Rcpp (я мало знаю о строковой обработке С++, но это может быть не слишком hard - see конвертировать Rcpp:: CharacterVector в std::string и Конкатенация строк не работает должным образом для начала...)
Ответ 4
В качестве Ben Bolker упомянутые выше подходы могут упасть, если в строке есть несколько НС. Я попробовал другой подход, который, кажется, преодолел это.
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% "")
, если при желании также удалить пустые строки. Обратите внимание, что с помощью collapse с 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"
Ответ 5
Вы можете использовать ifelse
, векторную конструкцию if-else, чтобы определить, является ли значение NA и заменить пробел. Затем вы будете использовать gsub, чтобы вырезать конечный ",", если за ним не следует какая-либо другая строка.
gsub(", $", "", paste(1:4, ifelse(is.na(foo), "", foo), sep = ", "))
Ваш ответ верный. Нет лучшего способа сделать это. Эта проблема явно упоминается в вставить документацию в разделе "Подробности".
Ответ 6
Вариант решения Джо (fooobar.com/questions/274293/...), который учитывает как 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
}
}
Ответ 7
Или сделайте мутацию после paste() и удалите NA:
data <- data.frame(col1= c(rep(NA, 5)), col2 = c(2:6)) %>%
mutate(col3 = paste(col1, col2)) %>%
mutate(col3 = gsub('NA', '', col3))
Ответ 8
Или удалите NA после вставки с помощью str_replace_all
data$1 <- str_replace_all(data$1, "NA", "")
Ответ 9
Вот решение, которое ведет себя больше как вставка и обрабатывает больше краевых случаев, чем текущие решения (пустые строки, строки "NA", более 2 аргументов, использование аргумента collapse...).
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,"
Created on 2019-10-01 by the reprex package (v0.3.0)