Это простой вопрос. У меня есть список названий стран. Однако я хотел изменить несколько имен на правильные. Итак, у меня есть еще два вектора; один с именами, которые нужно изменить, а второй с правильными именами. См. пример:
#country names (names are repetitive in the list)
cn <- c("I", "A", "B", "C", "A", "C", "D", "P")
change <- c("A", "B")
tochange <- c("X", "Y")
Ожидаемый результат
cn <- c("I", "X", "Y", "C", "X", "C", "D", "P")
Спасибо
Используя stringi::stri_replace_all_fixed
.
> stringi::stri_replace_all_fixed(cn, change, tochange, vectorize_all=FALSE)
[1] "I" "X" "Y" "C" "X" "C" "D" "P"
Я не могу найти лучшего решения, чем это, ура! +1
У меня один вопрос: работает ли это, если задано отображение типа "A" --> "B"
и "B" --> "A"
? Кажется, он не может дать желаемое отображение
@ThomasIsCoding В этом случае нам, вероятно, понадобится vectorize_all=TRUE
(по умолчанию), x <- rep_len(LETTERS[1:2], 10);stringi::stri_replace_all_fixed(x, c('A', 'B'), c('B', 'A'))
.
да, это сработало, спасибо! В чем преимущество vectorize_all=FALSE
в вашем решении (независимо от краевого случая)? Причина скорости или что-то еще?
@ThomasIsCoding Это ошибка переработки. Нам нужен x <- c("A", "B", "C");stringi::stri_replace_all_fixed(x, c('A', 'B', 'C'), c('B', 'A', 'C'), vectorize_all=TRUE)
. Без выполнения C=C это не удастся. Чтобы получить то же самое, но с использованием vectorize_all=FALSE
, нам нужно будет сделать x <- c("A", "B", "C"); x <- stringi::stri_replace_all_fixed(x, "A", "TMP", vectorize_all=FALSE);x <- stringi::stri_replace_all_fixed(x, "B", "A", vectorize_all=FALSE);stringi::stri_replace_all_fixed(x, "TMP", "B", vectorize_all=FALSE)
.
это кажется интересным заблуждением
Вы можете использовать функцию ifelse
в R
cn <- c("I", "A", "B", "C", "A", "C", "D", "P")
cn <- ifelse(cn == "A", "X", ifelse(cn == "B", "Y", cn))
print(cn)
ИЛИ
Альтернативно вы можете использовать пакет dplyr
для более читаемого решения.
library(dplyr)
cn <- c("I", "A", "B", "C", "A", "C", "D", "P")
cn <- cn %>% recode("A" = "X", "B" = "Y")
print(cn)
ВЫХОД:
[1] "I" "X" "Y" "C" "X" "C" "D" "P"
Я не могу этого сделать. У меня более 30 слов, которые нужно заменить. Спасибо
решение ifelse
не будет работать, если у вас есть замещающее сопоставление, например "A" --> "X"
и "X" --> "A"
Базовый цикл for
:
cn.new <- cn
for (i in seq_along(change)) {
cn.new[cn.new == change[i]] <- tochange[i]
}
cn
# [1] "I" "A" "B" "C" "A" "C" "D" "P"
cn.new
# [1] "I" "X" "Y" "C" "X" "C" "D" "P"
это не сработает, если у вас есть замещающее сопоставление, например "A" --> "X"
и "X" --> "A"
@ThomasIsCoding: Зависит от того, что вы подразумеваете под «не будет работать», результаты могут быть желательными, но в любом случае у ОП нет таких сопоставлений.
в примере ОП ваш метод сработал хорошо. Меня беспокоит только надежность того, как он справляется с некоторыми крайними случаями, как я указал в комментарии.
Я думаю, что цикличность вряд ли станет проблемой в этом вопросе, но в моем ответе мы покажем, как ее проверить, если вас действительно беспокоит этот вопрос.
Вы можете попробовать replace
+ match
, как показано ниже.
> d <- tochange[match(cn, change)]
> replace(cn, !is.na(d), na.omit(d))
[1] "I" "X" "Y" "C" "X" "C" "D" "P"
Я работал с той же базовой концепцией, но пытался сделать ее однострочной, не выглядя ужасной, но безуспешно.
Вот несколько альтернатив
1) gsubfn gsubfn
— это обобщение gsub
, в котором второй аргумент может быть не только строкой символов, но и именованным списком, который мы здесь используем (или функцией, или прото-объектом).
library(gsubfn)
gsubfn("^.*$", setNames(as.list(change), tochange), cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
2) Уменьшить. Решение по базе R следует использовать Reduce
dict <- setNames(change, tochange)
Reduce(\(x, y) replace(x, names(y), y), init = cn, dict)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
3)chartr. Если имена в строках состоят из одиночных символов, как в вопросе, то можно использовать основание R chartr
.
chartr(paste0(tochange, collapse = ""), paste0(change, collapse = ""), cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
или жестко закодировать имена
chartr("XY", "AB", cn)
## [1] "I" "A" "B" "C" "A" "C" "D" "P"
Округлость
Хотя кажется маловероятным, что проблема здесь будет проявлять цикличность, например, где A -> B -> A, мы можем проверить это, если вы считаете, что это возможно.
library(igraph)
cnt <- cbind(change, tochange) |>
graph_from_edgelist() |>
count_components()
if (cnt != length(change)) stop("circularity found")
Используемые входы
cn <- c("I", "A", "B", "C", "A", "C", "D", "P")
change <- c("A", "B")
tochange <- c("X", "Y")
Пожалуйста, не делайте это вручную для каждого имени.