Несколько подстановок в одной строке R

У меня есть столбец в кадре данных в R со значениями "-1", "0", "1". Я бы хотел заменить эти значения словами "нет", "возможно" и "да" соответственно. Я сделаю это, используя sub.

Я мог бы написать условную функцию, а затем код:

    df[col] <- lapply(df[col], conditional_function_substitution)

Я мог бы также делать подстановки по одному (пример первого из трех):

   df[col] <- lapply(df[col], sub, pattern = '-1', replacement = "no")

Мне интересно, можно ли это сделать в одной строке? Что-то вроде:

   df[col] <- lapply(df[col], sub, pattern = c('-1','0','1'), replacement = c('no','maybe','yes')

Спасибо за понимание!

Ответ 1

Добавив 2 к -1, 0 и 1, вы можете получить индексы в вектор желаемых результатов:

c("no", "maybe", "yes")[dat + 2]
# [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  

Связанная опция может использовать функцию match для определения индексации:

c("no", "maybe", "yes")[match(dat, -1:1)]
# [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  

В качестве альтернативы вы можете использовать именованный вектор для перекодирования:

unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
# [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"   

Вы также можете использовать вложенный ifelse:

ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
# [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"   

Если вы не против загрузки нового пакета, функция Recode из пакета car выполняет следующее:

library(car)
Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
# [1] "no"    "yes"   "maybe" "yes"   "yes"   "no"  

Данные

dat <- c(-1, 1, 0, 1, 1, -1)

Обратите внимание, что все, кроме первого, будут работать, если dat хранится в виде строки; в первом случае вам нужно будет использовать as.numeric(dat).

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

Если скорость кода представляет интерес, то вы можете сравнить решения. Здесь были указаны те пять параметров, которые я представил, включая два других решения, которые в настоящее время размещены как другие ответы, сравниваются с случайным вектором длиной 100 тыс. Страниц:

set.seed(144)
dat <- sample(c(-1, 0, 1), replace=TRUE, 100000)
opt1 <- function(dat) c("no", "maybe", "yes")[dat + 2]
opt2 <- function(dat) c("no", "maybe", "yes")[match(dat, -1:1)]
opt3 <- function(dat) unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
opt4 <- function(dat) ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
opt5 <- function(dat) Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
AnandaMahto <- function(dat) factor(dat, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
hrbrmstr <- function(dat) sapply(as.character(dat), switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE)
library(microbenchmark)
microbenchmark(opt1(dat), opt2(dat), opt3(dat), opt4(dat), opt5(dat), AnandaMahto(dat), hrbrmstr(dat))
# Unit: milliseconds
#              expr        min         lq       mean     median         uq        max neval
#         opt1(dat)   1.513500   2.553022   2.763685   2.656010   2.837673   4.384149   100
#         opt2(dat)   2.153438   3.013502   3.251850   3.117058   3.269230   5.851234   100
#         opt3(dat)  59.716271  61.890470  64.978685  62.509046  63.723048 144.708757   100
#         opt4(dat)  62.934734  64.715815  71.181477  65.652195  71.123384 123.840577   100
#         opt5(dat)  82.976441  84.849147  89.071808  85.752429  88.473162 155.347273   100
#  AnandaMahto(dat)  57.267227  58.643889  60.508402  59.065642  60.368913  80.852157   100
#     hrbrmstr(dat) 137.883307 148.626496 158.051220 153.441243 162.594752 228.271336   100

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

Как указано @AnandaMahto, эти результаты качественно отличаются, если у нас есть ввод символов вместо числового ввода:

set.seed(144)
dat <- sample(c("-1", "0", "1"), replace=TRUE, 100000)
opt1 <- function(dat) c("no", "maybe", "yes")[as.numeric(dat) + 2]
opt2 <- function(dat) c("no", "maybe", "yes")[match(dat, -1:1)]
opt3 <- function(dat) unname(c("-1"="no", "0"="maybe", "1"="yes")[as.character(dat)])
opt4 <- function(dat) ifelse(dat == -1, "no", ifelse(dat == 0, "maybe", "yes"))
opt5 <- function(dat) Recode(dat, "-1='no'; 0='maybe'; 1='yes'")
AnandaMahto <- function(dat) factor(dat, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
hrbrmstr <- function(dat) sapply(dat, switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE)
library(microbenchmark)
microbenchmark(opt1(dat), opt2(dat), opt3(dat), opt4(dat), opt5(dat), AnandaMahto(dat), hrbrmstr(dat))
# Unit: milliseconds
#              expr       min        lq       mean     median         uq        max neval
#         opt1(dat)  8.397194  9.519075  10.784108   9.693706  10.163203   55.78417   100
#         opt2(dat)  2.281438  3.091418   4.231162   3.210794   3.436038   49.39879   100
#         opt3(dat)  3.606863  5.481115   6.466393   5.720282   6.344651   48.47924   100
#         opt4(dat) 66.819638 69.996704  74.596960  71.290522  73.404043  127.52415   100
#         opt5(dat) 32.897019 35.701401  38.488489  36.336489  38.950272   88.20915   100
#  AnandaMahto(dat)  1.329443  2.114504   2.824306   2.275736   2.493907   46.19333   100
#     hrbrmstr(dat) 81.898572 91.043729 154.331766 100.006203 141.425717 1594.17447   100

Теперь решение factor, предложенное @AnandaMahto, является самым быстрым, за которым следует векторная индексация с помощью match и именованный векторный поиск. Опять же, все время автономной работы достаточно быстро, что вам понадобится большой вектор или множество прогонов для любого из них.

Ответ 2

factor обычно используется для этого типа задачи и приводит к довольно легко читаемому коду:

vec <- c(0, 1, -1, -1, 1, 0)
vec
# [1]  0  1 -1 -1  1  0

factor(vec, levels = c(-1, 0, 1), labels = c("no", "maybe", "yes"))
# [1] maybe yes   no    no    yes   maybe
# Levels: no maybe yes

Если вам нужен только вывод символа, заверните его в as.character.


Если значения столбцов уже являются строками, вы просто изменяете аргумент levels в factor для использования as.character:

vec2 <- as.character(c(0, 1, -1, -1, 1, 0))
vec2
# [1] "0"  "1"  "-1" "-1" "1"  "0" 

factor(vec2, levels = as.character(c(-1, 0, 1)), labels = c("no", "maybe", "yes"))
# [1] maybe yes   no    no    yes   maybe
# Levels: no maybe yes

Ответ 3

Это также может быть злым приложением для switch:

set.seed(1492)
thing <- sample(c(-1, 0, 1), 100, replace=TRUE)
sapply(as.character(thing), switch, `-1`="no", `0`="maybe", `1`="yes", USE.NAMES=FALSE))

Если они фактически являются символами, вы можете оставить бит as.character().

ПРИМЕЧАНИЕ. Я не обязательно рекомендую это, просто показывая все возможные способы (и это скорее выход из извилистых лабиринтов проходов ifelse).

IMO factor - путь.