Использование purrr для итеративной замены строк в столбце данных - PullRequest
2 голосов
/ 03 февраля 2020

Я хотел бы использовать purrr для итеративного запуска нескольких замен строк в столбце данных с помощью функции gsub().

Это пример кадра данных:

df <- data.frame(Year = "2019",
                 Text = c(rep("a aa", 5), 
                          rep("a bb", 3), 
                          rep("a cc", 2)))

> df
   Year Text
1  2019 a aa
2  2019 a aa
3  2019 a aa
4  2019 a aa
5  2019 a aa
6  2019 a bb
7  2019 a bb
8  2019 a bb
9  2019 a cc
10 2019 a cc

Это так я обычно запускаю замену строк и желаемый результат.

df$Text <- gsub("aa", "One", df$Text, fixed = T)
df$Text <- gsub("bb", "Two", df$Text, fixed = T)
df$Text <- gsub("cc", "Three", df$Text, fixed = T)

> df
   Year    Text
1  2019   a One
2  2019   a One
3  2019   a One
4  2019   a One
5  2019   a One
6  2019   a Two
7  2019   a Two
8  2019   a Two
9  2019 a Three
10 2019 a Three

Однако это нереально c использовать по мере увеличения списка замен строк, поэтому я попытался использовать purrr для повторения таких изменений, используя список patterns и replacements, но Мне удалось создать только сообщения об ошибках. Я ожидаю, что код будет перебирать text_pattern и text_replacement и запускать gsub на df$Text для каждой пары шаблон / замена. Пример ниже вместе с сообщениями об ошибках.

text_pattern <- c("aa", "bb", "cc")
text_replacement <- c("One", "Two", "Three")

walk2(text_pattern, text_replacement, function(...){
  gsub(text_pattern, text_replacement, df$Text, fixed = F)
  }
)

Warning messages:
1: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'pattern' has length > 1 and only the first element will be used
2: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'replacement' has length > 1 and only the first element will be used
3: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'pattern' has length > 1 and only the first element will be used
4: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'replacement' has length > 1 and only the first element will be used
5: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'pattern' has length > 1 and only the first element will be used
6: In gsub(text_former, text_replace, df$Text, fixed = F) :
  argument 'replacement' has length > 1 and only the first element will be used

Возможно ли выполнить sh, используя функции из purrr? Или я пытаюсь использовать не тот инструмент, и мне нужно использовать другую функцию?

Ответы [ 2 ]

3 голосов
/ 03 февраля 2020

Мы можем использовать reduce2

library(purrr)
library(stringr)
df$Text <- reduce2(text_pattern, text_replacement, ~ str_replace(..1, ..2, ..3), 
           .init = df$Text)
df$Text
#[1] "a One"   "a One"   "a One"   "a One"   "a One"   "a Two"   "a Two"   "a Two"   "a Three" "a Three"

Или без использования анонимного вызова функции

reduce2(text_pattern, text_replacement, .init = df$Text, str_replace)
2 голосов
/ 04 февраля 2020

@ akrun ответ велик, однако есть несколько промежуточных моментов, которые также могут оказаться полезными для лучшего понимания purrr.

  1. walk2 не вернет вывод, он просто вернет первый входной вектор.

    Из документов :

    walk () вызывает .f для его побочного эффекта и возвращает ввод .x.

    Ближайший аналог того, что вы делаете, это map2, но посмотрите ниже, почему это тоже не совсем то, что вам нужно.

  2. Аргументы внутри purrr функций, таких как map и walk, относятся к обобщенным c представлениям векторов, которые перебираются.

    У вас есть несколько вариантов того, как обращаться к входным векторам. Один из них - назвать аргументы в function(...). Например, с function(x, y) тогда будет получен безошибочный вывод:

    map2(text_pattern, text_replacement, function(x, y){
      gsub(x, y, df$Text, fixed = F)
    }
    )  # switching to map2() because walk2 gives silent output
    

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

    map2(text_pattern, text_replacement, ~gsub(.x, .y, df$Text, fixed = F))
    
  3. Результат не соответствует ожиданиям.

    purrr методы, такие как map и walk l oop по всему вектору для каждого шаблона. Вывод для обоих фрагментов кода в 2. следующий:

    [[1]]
     [1] "a One" "a One" "a One" "a One" "a One" "a bb"  "a bb"  "a bb"  "a cc"  "a cc" 
    
    [[2]]
     [1] "a aa"  "a aa"  "a aa"  "a aa"  "a aa"  "a Two" "a Two" "a Two" "a cc"  "a cc" 
    
    [[3]]
     [1] "a aa"    "a aa"    "a aa"    "a aa"    "a aa"    "a bb"    "a bb"    "a bb"   
     [9] "a Three" "a Three"  
    

    Таким образом, даже исправляя синтаксис, вы по-прежнему получаете список из трех элементов, содержимое каждого элемента является результатом операция замены для каждой пары text_pattern - text_replacement. Есть еще операция smu sh, которая должна произойти, чтобы собрать их всех вместе только с замененными элементами. Это то, что совершает переход @ akrun к reduce2.

    Еще одна заметка о синтаксисе reduce - аргументы ..1, ..2, ..3 относятся к входным данным на каждой итерации, а использование .init создает первый аргумент (..1) равно df$Text. ..2 и ..3 - это то, что в более ранних примерах map2 было .x и .y, соответственно (ie значения шаблона и замены). См. reduce документы для получения дополнительной информации.

...