R: Эффективный способ замены значений столбца на основе строк (может быть, с помощью case_when или какой-либо другой формы мутации)?

Есть ли более эффективный способ замены значений в столбцах на основе поиска в них строк?

prac <- data.frame(`External Placement` = c(NA, NA, "Spanish words We place outside of our company", NA, NA, "Spanish words We place outside of our company", "Spanish words We place outside of our company", "Spanish words We place outside of our company", NA, "Spanish words We place outside of our company"),
             `Internal Placement` = c(NA, NA, "Spanish words we place inside of our organisation", NA, "Spanish words we place inside of our organisation", "Spanish words we place inside of our organisation", NA, NA, NA, NA),
             `None of the above` = c("Ninguno none", "Ninguno none", NA, NA, NA, NA, NA, NA, NA, NA))

Это примерные данные. Я использовал серию if/else, чтобы получить нужные мне значения, но я долго пытался найти способ сделать это с помощью mutate, case_when и т. д., чтобы делать меньше повторений. Есть ли способ лучше? Вот что я сделал до сих пор:

prac$`Vocational Training` <- ifelse(grepl("vocational training$", prac$`Vocational Training`),
                                 "Vocational training", NA)
prac$`External Placement` <- ifelse(grepl("outside of our company$", prac$`External Placement`),
                                    "External placement", NA)
prac$`Internal Placement` <- ifelse(grepl("inside our organisation$", prac$`Internal Placement`),
                                    "Internal placement", NA)  
prac$`None of the Above` <- ifelse(grepl("^Ninguno", prac$`None of the Above`), "None or other", NA)

person Ben West    schedule 02.04.2020    source источник


Ответы (2)


Может быть, вы можете попробовать код, как показано ниже

v <- c("outside of our company$","inside our organisation$","^Ninguno")
r <- c("External placement","Internal placement","None or other")

prac[]<- mapply(function(x,y) ifelse(x,y,NA),
                data.frame(mapply(grepl, v,prac)),
                r)

такой, что

> prac
   External.Placement Internal.Placement None.of.the.above
1                <NA>               <NA>     None or other
2                <NA>               <NA>     None or other
3  External placement               <NA>              <NA>
4                <NA>               <NA>              <NA>
5                <NA>               <NA>              <NA>
6  External placement               <NA>              <NA>
7  External placement               <NA>              <NA>
8  External placement               <NA>              <NA>
9                <NA>               <NA>              <NA>
10 External placement               <NA>              <NA>

ДАННЫЕ

prac <- structure(list(External.Placement = structure(c(NA, NA, 1L, NA, 
NA, 1L, 1L, 1L, NA, 1L), .Label = "Spanish words We place outside of our company", class = "factor"), 
    Internal.Placement = structure(c(NA, NA, 1L, NA, 1L, 1L, 
    NA, NA, NA, NA), .Label = "Spanish words we place inside of our organisation", class = "factor"), 
    None.of.the.above = structure(c(1L, 1L, NA, NA, NA, NA, NA, 
    NA, NA, NA), .Label = "Ninguno none", class = "factor")), class = "data.frame", row.names = c(NA, 
-10L))
person ThomasIsCoding    schedule 02.04.2020
comment
Спасибо, на самом деле это именно то, что мне нужно, и достаточно обобщаемое, чтобы быть полезным для некоторых других приложений, которые я имею в виду. Я понимаю логику, но когда я пытаюсь это сделать, я получаю следующее сообщение об ошибке: 1: В mapply (grepl, v, prac): более длинный аргумент, не кратный длине более короткого 2: В mapply (функция (x, y) ifelse(x, y, NA), data.frame(mapply(grepl, : более длинный аргумент, не кратный длине более короткого) Это потому, что в реальных данных есть другие столбцы, которые я здесь не упомянул (id, date, так далее.)? - person Ben West; 02.04.2020
comment
@BenWest Длина r и v должна совпадать с количеством столбцов df, в противном случае несовпадающие размеры вызовут ошибки. - person ThomasIsCoding; 02.04.2020
comment
Ах хорошо. Итак, как я могу изменить этот код, чтобы он работал, учитывая, что есть столбцы, к которым я не хочу применять эту формулу? Мне просто добавить имена других столбцов? - person Ben West; 02.04.2020
comment
@BenWest Может быть, вы можете попробовать prac[c("a","b")]<- mapply(function(x,y) ifelse(x,y,NA), data.frame(mapply(grepl, v,prac[c("a","b")])), r), если вы хотите применить формулу только к столбцам a и ``b`, например - person ThomasIsCoding; 02.04.2020

Это зависит от того, хотите ли вы добиться эффективности вычислений (время вычислений) или эффективности программирования (количество используемых строк).

С точки зрения эффективности программирования будет очень сложно превзойти решение data.table. Я могу предложить вам это:

prac <- data.frame(`External Placement` = c(NA, NA, "Spanish words We place outside of our company", NA, NA, "Spanish words We place outside of our company", "Spanish words We place outside of our company", "Spanish words We place outside of our company", NA, "Spanish words We place outside of our company"),
                   `Internal Placement` = c(NA, NA, "Spanish words we place inside of our organisation", NA, "Spanish words we place inside of our organisation", "Spanish words we place inside of our organisation", NA, NA, NA, NA),
                   `None of the Above` = c("Ninguno none", "Ninguno none", NA, NA, NA, NA, NA, NA, NA, NA))

library(data.table)

list_conditions <- c(#"Vocational.Training" = "vocational training$",
  "External.Placement" = "outside of our company$",
  "Internal.Placement" = "inside our organisation$",
  "None.of.the.Above" = "^Ninguno")


dt <- data.table(prac)

Моя идея состоит в том, чтобы использовать вектор с условиями, чьи имена являются переменными, к которым он должен применяться. Затем lapply, который обновляет ваш фрейм данных по ссылке (здесь вы теряете некоторую эффективность программирования, но у вас будет очень хорошая вычислительная эффективность)

dt <- lapply(seq_len(length(list_conditions)), function(i) {

  var <- names(list_conditions)[i]
  cond <- list_conditions[i]
  val <- gsub("\\.","", var)
  dt[, 'tempcol' := NA_character_]
  dt[grepl(as.character(cond), get(var)), tempcol := as.character(val)]
  dt[,c(var) := tempcol]

})
dt <- dt[[length(dt)]]
dt[,'tempcol' := NULL]

Строка dt <- dt[[length(dt)]] здесь, потому что R возвращает список, но нас интересует только его последний элемент (последнее обновление для фрейма данных). Вы можете обобщить эту программу, если предпочитаете создавать новые столбцы, а не переписывать существующие.

Результат:

dt
    External.Placement Internal.Placement None.of.the.Above
 1:               <NA>               <NA> None of the Above
 2:               <NA>               <NA> None of the Above
 3: External Placement               <NA>              <NA>
 4:               <NA>               <NA>              <NA>
 5:               <NA>               <NA>              <NA>
 6: External Placement               <NA>              <NA>
 7: External Placement               <NA>              <NA>
 8: External Placement               <NA>              <NA>
 9:               <NA>               <NA>              <NA>
10: External Placement               <NA>              <NA>
person linog    schedule 02.04.2020
comment
Это не совсем то, что я искал - в основном, я пытаюсь получить значение в столбце для внешнего размещения, внутреннего размещения или ничего из вышеперечисленного, в зависимости от правильного случая. - person Ben West; 02.04.2020
comment
Я сделал ошибку относительно выходных значений. я исправил - person linog; 02.04.2020