Заменить набор слов другим набором слов в R

Это простой вопрос. У меня есть список названий стран. Однако я хотел изменить несколько имен на правильные. Итак, у меня есть еще два вектора; один с именами, которые нужно изменить, а второй с правильными именами. См. пример:

#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")

Спасибо

Пожалуйста, не делайте это вручную для каждого имени.

Neeraj 28.07.2024 11:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
97
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Используя 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

ThomasIsCoding 28.07.2024 13:30

У меня один вопрос: работает ли это, если задано отображение типа "A" --> "B" и "B" --> "A"? Кажется, он не может дать желаемое отображение

ThomasIsCoding 28.07.2024 15:03

@ThomasIsCoding В этом случае нам, вероятно, понадобится vectorize_all=TRUE (по умолчанию), x <- rep_len(LETTERS[1:2], 10);stringi::stri_replace_all_fixed(x, c('A', 'B'), c('B', 'A')).

jay.sf 28.07.2024 15:53

да, это сработало, спасибо! В чем преимущество vectorize_all=FALSE в вашем решении (независимо от краевого случая)? Причина скорости или что-то еще?

ThomasIsCoding 28.07.2024 16:14

@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).

jay.sf 28.07.2024 17:09

это кажется интересным заблуждением

ThomasIsCoding 28.07.2024 21:41

Вы можете использовать функцию 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 слов, которые нужно заменить. Спасибо

Neeraj 28.07.2024 11:50

решение ifelse не будет работать, если у вас есть замещающее сопоставление, например "A" --> "X" и "X" --> "A"

ThomasIsCoding 28.07.2024 13:29

Базовый цикл 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 28.07.2024 13:30

@ThomasIsCoding: Зависит от того, что вы подразумеваете под «не будет работать», результаты могут быть желательными, но в любом случае у ОП нет таких сопоставлений.

AkselA 28.07.2024 14:00

в примере ОП ваш метод сработал хорошо. Меня беспокоит только надежность того, как он справляется с некоторыми крайними случаями, как я указал в комментарии.

ThomasIsCoding 28.07.2024 14:42

Я думаю, что цикличность вряд ли станет проблемой в этом вопросе, но в моем ответе мы покажем, как ее проверить, если вас действительно беспокоит этот вопрос.

G. Grothendieck 28.07.2024 16:15

Вы можете попробовать replace + match, как показано ниже.

> d <- tochange[match(cn, change)]

> replace(cn, !is.na(d), na.omit(d))
[1] "I" "X" "Y" "C" "X" "C" "D" "P"

Я работал с той же базовой концепцией, но пытался сделать ее однострочной, не выглядя ужасной, но безуспешно.

AkselA 28.07.2024 14:02

Вот несколько альтернатив

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

Другие вопросы по теме