10  Текст и последовательности

На данной странице демонстрируется применение пакета stringr для оценки и работы с текстовыми значениями (“последовательностями”).

  1. Объединение, упорядочивание, разделение, сортировка - str_c(), str_glue(), str_order(), str_split()
  2. Вычистка и стандартизация
    • Корректировка длины - str_pad(), str_trunc(), str_wrap()
    • Изменение регистра - str_to_upper(), str_to_title(), str_to_lower(), str_to_sentence()
  3. Оценка и извлечение по позиции - str_length(), str_sub(), word()
  4. Комбинации символов
    • Поиск и обнаружение - str_detect(), str_subset(), str_match(), str_extract()
    • Модификация и замена - str_sub(), str_replace_all()
  5. Регулярные выражения (“regex”)

Для простоты отображения большинство примеров показаны с использованием краткого определенного текстового вектора, однако их можно легко адаптировать к столбцам в датафрейме.

Эта страница во многом основана на идеях виньетки по stringr.

10.1 Подготовка

Загрузка пакетов

Установите или загрузите stringr и другие пакеты tidyverse.

# установка/загрузка пакетов
pacman::p_load(
  stringr,    # много функций для работы с последовательностями
  tidyverse,  # для опциональных манипуляций с данными
  tools)      # альтернативы для капитализации всех слов

Импорт данных

На этой странице мы иногда ссылаемся на вычищенный построчный список случаев linelist по имитационной эпидемии Эболы. Если вы хотите параллельно выполнять действия, кликните, чтобы скачать “чистый” построчный список (as .rds file). Импортируйте данные с помощою функции import() из пакета rio (она может работать с разными типами файлов, такими как .xlsx, .csv, .rds - см. страницу Импорт и экспорт для получения детальной информации).

# импорт построчного списка случаев 
linelist <- import("linelist_cleaned.rds")

Первые 50 строк построчного списка отображены ниже.

10.2 Объединение, разделение и упорядочивание

В этом разделе разбирается:

  • Использование str_c(), str_glue() и unite() для объединения последовательностей
  • Использование str_order() для упорядочивания последовательностей
  • Использование str_split() и separate() для разделения последовательностей

Объединение последовательностей

Чтобы объединить или сцепить несколько последовательностей в одну, мы предлагаем использовать str_c из пакета stringr. Если у вас есть конкретные текстовые значения для объединения, просто задайте их как уникальные аргументы, разделенные запятыми.

str_c("String1", "String2", "String3")
[1] "String1String2String3"

Аргумент sep = вставляет текстовое значение между каждым из аргументов, которые вы задали (например, вставляет запятую, пробел или новую строку "\n")

str_c("String1", "String2", "String3", sep = ", ")
[1] "String1, String2, String3"

Аргумент collapse = актуален, если вы закладываете несколько векторов в виде аргументов в str_c(). Он используется для разделения элементов того, что должно стать выходным вектором, так чтобы у выходного вектора был только один длинный текстовый элемент.

Пример ниже показывает комбинирование двух векторов в один (имена и фамилии). Еще одним похожим примером могут быть юрисдикции и количество случаев. В данном примере:

  • Значение sep = появляется между каждым именем и фамилией
  • Значение collapse = появляется между каждым человеком
first_names <- c("abdul", "fahruk", "janice") 
last_names  <- c("hussein", "akinleye", "okeke")

# sep отображается между соответствующими входными последовательностями, а collapse отображается между созданными элементами
str_c(first_names, last_names, sep = " ", collapse = ";  ")
[1] "abdul hussein;  fahruk akinleye;  janice okeke"

Примечание: В зависимости от желаемого контекста отображения при печати такой объединенной последовательности с новыми строками, вам может потребоваться обернуть всю фразу в cat(), чтобы новые строки правильно напечатались:

# Чтобы новые строки были напечатаны правильно, возможно, потребуется обернуть всю фразу в cat()
cat(str_c(first_names, last_names, sep = " ", collapse = ";\n"))
abdul hussein;
fahruk akinleye;
janice okeke

Динамические последовательности

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

  • Все содержимое размещается внутри двойных кавычек str_glue("")
  • Любой динамический код или ссылки на заранее определенные значения размещаются в фигурных скобках {} внутри двойных кавычек. В одной команде str_glue() может быть много фигурных скобок.
  • Чтобы отобразить текстовые кавычки ’’, используйте одинарные кавычки внутри двойных кавычек (например, при указании формата даты - см. пример ниже)
  • Совет: вы можете использовать \n, чтобы принудительно перейти на новую строку
  • Совет: вы можете использовать format(), чтобы откорректировать отображение даты, и использовать Sys.Date(), чтобы отобразить текущую дату.

Простой пример динамической подписи к графику:

str_glue("Data include {nrow(linelist)} cases and are current to {format(Sys.Date(), '%d %b %Y')}.")
Data include 5888 cases and are current to 19 Jun 2024.

Альтернативный формат - использовать заполнители внутри скобок, а код определить в отдельном аргументе в конце функции str_glue(), как показано ниже. Это может повысить читаемость кода, если текст длинный.

str_glue("Linelist as of {current_date}.\nLast case hospitalized on {last_hospital}.\n{n_missing_onset} cases are missing date of onset and not shown",
         current_date = format(Sys.Date(), '%d %b %Y'),
         last_hospital = format(as.Date(max(linelist$date_hospitalisation, na.rm=T)), '%d %b %Y'),
         n_missing_onset = nrow(linelist %>% filter(is.na(date_onset)))
         )
Linelist as of 19 Jun 2024.
Last case hospitalized on 30 Apr 2015.
256 cases are missing date of onset and not shown

Извлечение из датафрейма

Иногда полезно взять данные датафрейма и вставить их последовательно вместе. Ниже приведен пример датафрейма. Мы используем его, чтобы создать сводное утверждение о юрисдикциях и количестве новых и общем количестве случаев.

# создаем датафрейм случаев
case_table <- data.frame(
  zone        = c("Zone 1", "Zone 2", "Zone 3", "Zone 4", "Zone 5"),
  new_cases   = c(3, 0, 7, 0, 15),
  total_cases = c(40, 4, 25, 10, 103)
  )

Используем str_glue_data(), которая специально создана для того, чтобы взять данные из строк датафрейма:

case_table %>% 
  str_glue_data("{zone}: {new_cases} ({total_cases} total cases)")
Zone 1: 3 (40 total cases)
Zone 2: 0 (4 total cases)
Zone 3: 7 (25 total cases)
Zone 4: 0 (10 total cases)
Zone 5: 15 (103 total cases)

Объединение последовательностей между строками

Если вы пытаетесь свести значения в столбце датафрейма, например, объединить значения из нескольких строк в одну строку, вставив их через разделитесь, см. раздел на странице Дедупликация по “Сведение” значений.

Датафрейм в одну строку

Вы можете сделать так, чтобы утверждение появлялось одной строкой, используя str_c() (уточнив датафрейм и имена столбцов), а также задав аргументы sep = и collapse =.

str_c(case_table$zone, case_table$new_cases, sep = " = ", collapse = ";  ")
[1] "Zone 1 = 3;  Zone 2 = 0;  Zone 3 = 7;  Zone 4 = 0;  Zone 5 = 15"

Вы можете добавить текстовый префикс “New Cases:” в начало утверждение, обернув его в отдельную команду str_c() (если “New Cases:” было в оригинальной str_c(), эта фраза появится несколько раз).

str_c("New Cases: ", str_c(case_table$zone, case_table$new_cases, sep = " = ", collapse = ";  "))
[1] "New Cases: Zone 1 = 3;  Zone 2 = 0;  Zone 3 = 7;  Zone 4 = 0;  Zone 5 = 15"

Объединение столбцов

Объединить текстовые значения из нескольких столбцов в датафрейме можно с помощью unite() из tidyr. Это действие, противоположное separate().

Задайте имя нового объединенного столбца. Затем задайте имена столбцов, которые вы хотите объединить.

  • По умолчанию разделителем, используемым в объединенном столбце, будет нижнее подчеркивание _, но его можно изменить в аргументе sep =.
  • remove = удаляет входные столбцы из датафрейма (по умолчанию TRUE (истина))
  • na.rm = удаляет отсутствующие значения при объединении (по умолчанию FALSE (ложь))

Ниже мы определяем мини-датафрейм для демонстрации:

df <- data.frame(
  case_ID = c(1:6),
  symptoms  = c("jaundice, fever, chills",     # пациент 1
                "chills, aches, pains",        # пациент 2 
                "fever",                       # пациент 3
                "vomiting, diarrhoea",         # пациент 4
                "bleeding from gums, fever",   # пациент 5
                "rapid pulse, headache"),      # пациент 6
  outcome = c("Recover", "Death", "Death", "Recover", "Recover", "Recover"))
df_split <- separate(df, symptoms, into = c("sym_1", "sym_2", "sym_3"), extra = "merge")
Warning: Expected 3 pieces. Missing pieces filled with `NA` in 2 rows [3, 4].

Вот пример датафрейма:

Ниже мы объединяем три столбца симптомов:

df_split %>% 
  unite(
    col = "all_symptoms",         # имя нового объединенного столбца
    c("sym_1", "sym_2", "sym_3"), # столбцы для объединения
    sep = ", ",                   # разделитель, который будет использован в объединенном столбце
    remove = TRUE,                # если TRUE (истина), удаляет входные столбцы из датафрейма
    na.rm = TRUE                  # если TRUE (истина), отсутствующие значения удаляются перед объединением
  )
  case_ID                all_symptoms outcome
1       1     jaundice, fever, chills Recover
2       2        chills, aches, pains   Death
3       3                       fever   Death
4       4         vomiting, diarrhoea Recover
5       5 bleeding, from, gums, fever Recover
6       6      rapid, pulse, headache Recover

Разделение

Чтобы разделить последовательность на основе комбинации символов, используйте str_split(). Она оценивает последовательность(и) и выдает список текстовых векторов list для только что разделенных значений.

Простой пример ниже оценивает одну последовательность и разделяет ее на три. По умолчанию команда выдает объект класса list с одним элементом (текстовым вектором) для каждой изначально заданой последовательности. Если simplify = TRUE команда выдает текстовую матрицу.

В данном примере задается одна последовательность, а функция выдает список с одним элементом - текстовым вектором с тремя значениями.

str_split(string = "jaundice, fever, chills",
          pattern = ",")
[[1]]
[1] "jaundice" " fever"   " chills" 

Если выходной продукт сохраняется, вы можете затем получить доступ к n-ному разделенному значению с помощью синтаксиса с квадратными скобками. Чтобы получить доступ к конкретному значению, вы можете использовать подобный синтаксис: the_returned_object[[1]][2], который получит доступ ко второму значению из первой оцениваемой последовательности (“fever”). См. страницу [Основы R] для получения более детальной информации о доступе к элементам.

pt1_symptoms <- str_split("jaundice, fever, chills", ",")

pt1_symptoms[[1]][2]  # извлекает 2-е значение из 1-го (и только) элемента списка
[1] " fever"

Если в str_split() заданы несколько последовательностей, то будет выдано больше 1 элемента в получившемся списке.

symptoms <- c("jaundice, fever, chills",     # пациент 1
              "chills, aches, pains",        # пациент 2 
              "fever",                       # пациент 3
              "vomiting, diarrhoea",         # пациент 4
              "bleeding from gums, fever",   # пациент 5
              "rapid pulse, headache")       # пациент 6

str_split(symptoms, ",")                     # разделить симптомы каждого пациента
[[1]]
[1] "jaundice" " fever"   " chills" 

[[2]]
[1] "chills" " aches" " pains"

[[3]]
[1] "fever"

[[4]]
[1] "vomiting"   " diarrhoea"

[[5]]
[1] "bleeding from gums" " fever"            

[[6]]
[1] "rapid pulse" " headache"  

Чтобы вместо этого получить “текстовую матрицу”, которая может быть полезна, если вы создаете столбцы датафрейма, задайте аргумент simplify = TRUE, как показано ниже:

str_split(symptoms, ",", simplify = TRUE)
     [,1]                 [,2]         [,3]     
[1,] "jaundice"           " fever"     " chills"
[2,] "chills"             " aches"     " pains" 
[3,] "fever"              ""           ""       
[4,] "vomiting"           " diarrhoea" ""       
[5,] "bleeding from gums" " fever"     ""       
[6,] "rapid pulse"        " headache"  ""       

Вы можете также скорректировать количество разделенных частей, которые вы создаете, с помощью аргумента n =. Например, это ограничивает количество разделенных частей до 2. Любые дополнительные запятые остаются во втором значении.

str_split(symptoms, ",", simplify = TRUE, n = 2)
     [,1]                 [,2]            
[1,] "jaundice"           " fever, chills"
[2,] "chills"             " aches, pains" 
[3,] "fever"              ""              
[4,] "vomiting"           " diarrhoea"    
[5,] "bleeding from gums" " fever"        
[6,] "rapid pulse"        " headache"     

Примечание - те же выходные данные можно получить с помощью str_split_fixed(), где вы не задаете аргумент simplify, но вместо этого указываете количество столбцов (n).

str_split_fixed(symptoms, ",", n = 2)

Разделение столбцов

Если вы пытаетесь разделить столбец датафрейма, лучше использовать функцию separate() из dplyr. Она используется для разделения одного текстового столбца на другие столбцы.

Представим, что у нас есть простой датафрейм df (определенный и объединенный в разделе объединения), содержащий столбец case_ID, один текстовый столбец с множеством симптомов, а также столбец исхода. Наша цель - разделить столбец symptoms на несколько столбцов - в каждом из которых будет один симптом.

Если предположить, что данные передаются в отдельную функцию separate(), сначала задайте столбец, который нужно разделить. Затем задайте into = как вектор c( ), содержащий новые имена столбцов, как показано ниже.

  • sep = разделитесь, может быть текстовым знаком или числом (интерпретируется как позиция текстового знака, где происходит разделение)
  • remove = по умолчанию FALSE (ложь), удаляет входной столбец
  • convert = по умолчанию FALSE (ложь), превращает “NA” в последовательности в NA
  • extra = контролирует, что происходит, если разделением создается больше значений, чем новых именованных столбцов.
    • extra = "warn" означает, что вы увидите предупреждение, но система выкинет излишние значения (по умолчанию)
    • extra = "drop" означает, что излишние значения будут выкинуты без предупреждения
    • extra = "merge" разделит только на количество новых столбцов, указанных в into - эта опция сохранит все ваши данные

Пример с использованием extra = "merge" приведен ниже - никакие данные не теряются. Определены два новых столбца, но любые третьи симптомы остаются во втором новом столбце:

# третьи симптомы объединяются во втором новом столбце
df %>% 
  separate(symptoms, into = c("sym_1", "sym_2"), sep=",", extra = "merge")
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [3].
  case_ID              sym_1          sym_2 outcome
1       1           jaundice  fever, chills Recover
2       2             chills   aches, pains   Death
3       3              fever           <NA>   Death
4       4           vomiting      diarrhoea Recover
5       5 bleeding from gums          fever Recover
6       6        rapid pulse       headache Recover

Когда ниже используется значение по умолчанию extra = "drop", выдается предупреждение, но третьи симптомы теряются:

# третьи симптомы теряются
df %>% 
  separate(symptoms, into = c("sym_1", "sym_2"), sep=",")
Warning: Expected 2 pieces. Additional pieces discarded in 2 rows [1, 2].
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [3].
  case_ID              sym_1      sym_2 outcome
1       1           jaundice      fever Recover
2       2             chills      aches   Death
3       3              fever       <NA>   Death
4       4           vomiting  diarrhoea Recover
5       5 bleeding from gums      fever Recover
6       6        rapid pulse   headache Recover

ВНИМАНИЕ: Если вы не зададите достаточное количество значений into для новых столбцов, ваши данные могут быть подрезаны.

Упорядочивание в алфавитном порядке

Несколько последовательностей можно отсортировать в алфавитном порядке. str_order() выдает порядок, а str_sort() выдает последовательности в этом порядке.

# последовательности
health_zones <- c("Alba", "Takota", "Delta")

# выдает алфавитный порядок
str_order(health_zones)
[1] 1 3 2
# выдает последовательности в алфавитном порядке
str_sort(health_zones)
[1] "Alba"   "Delta"  "Takota"

Чтобы использовать другой алфавит, добавьте аргумент locale =. См. полный список локалей, введя stringi::stri_locale_list() в консоли R.

базовые функции R

Часто используются базовые функции R paste() и paste0(), которые сцепляют векторы после конвертации всех частей в текстовые. Они работают похожим на str_c() образом, но синтаксис считается более сложным - в скобках, где каждая часть отделена запятой. Части либо являются текстом (в кавычках), либо заранее определенными объектами кода (без кавычек). Например:

n_beds <- 10
n_masks <- 20

paste0("Regional hospital needs ", n_beds, " beds and ", n_masks, " masks.")
[1] "Regional hospital needs 10 beds and 20 masks."

Можно уточнить аргументы sep = и collapse =. paste() - это просто paste0() с sep = " " по умолчанию (один пробел).

10.3 Вычистка и стандартизация

Изменение регистра

Часто бывает необходимость менять капитализацию значения последовательности, например, названий юрисдикций. Используйте str_to_upper(), str_to_lower() и str_to_title() из stringr, как показано ниже:

str_to_upper("California")
[1] "CALIFORNIA"
str_to_lower("California")
[1] "california"

Можно добиться того же, что и выше, с помощью функций toupper(), tolower() из *базового** R.

Капитализация всех слов

Трансформация последовательности таким образом, чтобы каждое слово было капитализировано, может быть выполнена с помощью str_to_title():

str_to_title("go to the US state of california ")
[1] "Go To The Us State Of California "

Используйте toTitleCase() из пакета tools для более сложной капитализации (где не капитализируются такие слова, как “to”, “the” и “of”).

tools::toTitleCase("This is the US state of california")
[1] "This is the US State of California"

Вы можете также использовать str_to_sentence(), которая капитализирует только первую букву последовательности.

str_to_sentence("the patient must be transported")
[1] "The patient must be transported"

Наращивание длины

Используйте str_pad(), чтобы добавить знаки в последовательность до минимальной длины. По умолчанию добавляются пробелы, но вы можете наращивать и с помощью других знаков, используя аргумент pad =.

# Коды МКБ разной длины
ICD_codes <- c("R10.13",
               "R10.819",
               "R17")

# Коды МКБ наращиваются до 7 знаков с правой стороны
str_pad(ICD_codes, 7, "right")
[1] "R10.13 " "R10.819" "R17    "
# Наращивание точками вместо пробелов
str_pad(ICD_codes, 7, "right", pad = ".")
[1] "R10.13." "R10.819" "R17...."

Например, чтобы нарастить числа нулями в начале (например, для часов или минут), вы можете нарастить число до минимальной длины в 2 знака с помощью pad = "0".

# Добавляем нули в начале до получения двух знаков (например, для минут/часов)
str_pad("4", 2, pad = "0") 
[1] "04"
# пример использования числового столбца с именем "hours"
# hours <- str_pad(hours, 2, pad = "0")

Укорачивание

str_trunc() задает максимальную длину каждой последовательности. Если последовательность превышает эту длину, она обрезается (укорачивается) и добавляется многоточие (…), чтобы указать, что ранее последовательность была длиннее. Обратите внимание, что многоточие считается в длине. Знаки многоточия можно изменить с помощью аргумента ellipsis =. Опциональный аргумент side = указывает, где появится многоточие в укороченной последовательности (“left” (слева), “right” (справа), либо “center” (в центре)).

original <- "Symptom onset on 4/3/2020 with vomiting"
str_trunc(original, 10, "center")
[1] "Symp...ing"

Стандартизация длины

Используйте str_trunc(), чтобы задать максимальную длину, а затем используйте str_pad(), чтобы расширить очень короткие последовательности до этой укороченной длины. В примере ниже, 6 задается как максимальная длина (одно значение укорачивается), а затем одно очень короткое значение наразивается до длины в 6 знаков.

# МКБ коды разной длины
ICD_codes   <- c("R10.13",
                 "R10.819",
                 "R17")

# укорачиваем до максимальной длины в 6 знаков
ICD_codes_2 <- str_trunc(ICD_codes, 6)
ICD_codes_2
[1] "R10.13" "R10..." "R17"   
# расширяем до минимальной длины в 6 знаков
ICD_codes_3 <- str_pad(ICD_codes_2, 6, "right")
ICD_codes_3
[1] "R10.13" "R10..." "R17   "

Удаление пустого пространства в начале/в конце

Используйте str_trim(), чтобы удалить пробелы, новые строки (\n) или отступы (\t) по бокам последовательности. Добавьте "right" (справа), "left" (слева), или "both" (оба) к команде, чтобы уточнить, какую сторону обрезать (например, str_trim(x, "right").

# ID номера с лишними пробелами справа
IDs <- c("provA_1852  ", # два лишних пробела
         "provA_2345",   # ноль лишних пробелов
         "provA_9460 ")  # один лишний пробел

# ID номера укорачиваются, чтобы удалить лишние пробелы только с правой стороны
str_trim(IDs)
[1] "provA_1852" "provA_2345" "provA_9460"

Удаление повторяющихся пробелов внутри последовательности

Используйте str_squish() для удаления повторяющихся пробелов внутри последовательности. Например, чтобы конвертировать двойные пробелы в одинарные пробелы. Эта команда также удаляет пробелы, новые строки или табуляцию на внешних концах последовательности, как str_trim().

# в оригинале внутри последовательности есть лишние пробелы
str_squish("  Pt requires   IV saline\n") 
[1] "Pt requires IV saline"

Введите ?str_trim, ?str_pad на консоли R для получения дополнительной информации.

Создание абзацев

Используйте str_wrap(), чтобы из длинного неструктурированного текста создать структурированный абзац с фиксированной длиной строк. Укажите идеальную длину для каждой строки и функция применит алгоритм по вставке разрывов строк внутри абзаца, как показано в примере ниже.

pt_course <- "Symptom onset 1/4/2020 vomiting chills fever. Pt saw traditional healer in home village on 2/4/2020. On 5/4/2020 pt symptoms worsened and was admitted to Lumta clinic. Sample was taken and pt was transported to regional hospital on 6/4/2020. Pt died at regional hospital on 7/4/2020."

str_wrap(pt_course, 40)
[1] "Symptom onset 1/4/2020 vomiting chills\nfever. Pt saw traditional healer in\nhome village on 2/4/2020. On 5/4/2020\npt symptoms worsened and was admitted\nto Lumta clinic. Sample was taken and pt\nwas transported to regional hospital on\n6/4/2020. Pt died at regional hospital\non 7/4/2020."

Указанная выше команда может быть обернута в Базовую функцию cat(), чтобы напечатать получившийся результат, отображающий новые добавленные строки.

cat(str_wrap(pt_course, 40))
Symptom onset 1/4/2020 vomiting chills
fever. Pt saw traditional healer in
home village on 2/4/2020. On 5/4/2020
pt symptoms worsened and was admitted
to Lumta clinic. Sample was taken and pt
was transported to regional hospital on
6/4/2020. Pt died at regional hospital
on 7/4/2020.

10.4 Работа с последовательностями на основе их позиции

Извлечение по позиции текстового знака

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

  1. текстовый вектор(ы)
  2. стартовая позиция
  3. конечная позиция

Несколько комментариев по числам позиции:

  • Если число позиции положительное, позиция считается, начиная с левой части последовательности.
  • Если число позиции отрицательное, она считается, начиная с правой части последовательности.
  • Числа позиции считаются включительно.
  • Позиции за пределами последовательности будут укорочены (удалены).

Ниже приведены некоторые примеры применительно к последовательности “pneumonia”:

# начало и окончание на третьем слева (3я буква слева)
str_sub("pneumonia", 3, 3)
[1] "e"
# 0 не присутствует
str_sub("pneumonia", 0, 0)
[1] ""
# от 6го слева до 1го справа
str_sub("pneumonia", 6, -1)
[1] "onia"
# от 5го справа до 2го справа
str_sub("pneumonia", -5, -2)
[1] "moni"
# от 4го слева до позиции за пределами последовательности
str_sub("pneumonia", 4, 15)
[1] "umonia"

Извлечение по позиции слова

Чтобы извлечь n-ое ‘слово’, используйте word(), также из пакета stringr. Укажите последовательность(и), затем первую позицию слова для извлечения, а также последнюю позицию слова для извлечения.

По умолчанию разделителем между ‘словами’ считается пробел, если не указано иное в аргументе sep = (например, sep = "_", когда слова отделяются нижними подчеркиваниями.

# последовательности для оценки
chief_complaints <- c("I just got out of the hospital 2 days ago, but still can barely breathe.",
                      "My stomach hurts",
                      "Severe ear pain")

# извлечь с 1го по 3е слово каждой последовательности
word(chief_complaints, start = 1, end = 3, sep = " ")
[1] "I just got"       "My stomach hurts" "Severe ear pain" 

Замена по позиции текстового знака

str_sub() вместе с оператором присваивания (<-) может быть использован для модификации части последовательности:

word <- "pneumonia"

# конвертирует третий и четвертый знаки в X 
str_sub(word, 3, 4) <- "XX"

# печать
word
[1] "pnXXmonia"

Пример применения к нескольким последовательностям (например, к столбцу). Обратите внимание на длину “HIV”.

words <- c("pneumonia", "tubercolosis", "HIV")

# конвертирует третий и четвертый знаки в X 
str_sub(words, 3, 4) <- "XX"

words
[1] "pnXXmonia"    "tuXXrcolosis" "HIXX"        

Оценка длины

str_length("abc")
[1] 3

Альтернативно используйте nchar() из базового R

10.5 Комбинации символов

Многие функции stringr работают для обнаружения, поиска, извлечения, поиска соответствия, замены и разделения на основе указанной комбинации символов.

Обнаружение комбинации символов

Используйте str_detect(), как показано ниже, чтобы обнаружить наличие/отсутствие комбинации символов внутри последовательности. Сначала задайте последовательность или вектор, в котором будет идти поиск (string =), а затем комбинацию символов, которую нужно искать (pattern =). Обратите внимание, что по умолчанию, этот поиск чувствителен к регистру!

str_detect(string = "primary school teacher", pattern = "teach")
[1] TRUE

Можно включить аргумент negate = и установить на TRUE (истина), если вы хотите узнать, что комбинации символов НЕТ.

str_detect(string = "primary school teacher", pattern = "teach", negate = TRUE)
[1] FALSE

Чтобы игнорировать регистр/капитализацию, оберните вокруг комбинации символов команду regex(), а внутри regex() добавьте аргумент ignore_case = TRUE (или T в качестве сокращения).

str_detect(string = "Teacher", pattern = regex("teach", ignore_case = T))
[1] TRUE

Когда применяется str_detect() к текстовому вектору или столбцу датафрейма, команда выдаст TRUE (истина) или FALSE (ложь) для каждого из значений.

# вектор/столбец рода занятий 
occupations <- c("field laborer",
                 "university professor",
                 "primary school teacher & tutor",
                 "tutor",
                 "nurse at regional hospital",
                 "lineworker at Amberdeen Fish Factory",
                 "physican",
                 "cardiologist",
                 "office worker",
                 "food service")

# Обнаружение наличия комбинации символов "teach" в каждой последовательности - выходным результатом будет вектор TRUE/FALSE
str_detect(occupations, "teach")
 [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

Если вам надо подсчитать количество значений TRUE, просто используйте sum() для выходных результатов. Команда подсчитает количество значений TRUE.

sum(str_detect(occupations, "teach"))
[1] 1

Чтобы искать включительно по нескольким терминам, включите их и разделите вертикальными чертами ИЛИ (|) внутри аргумента pattern =, как показано ниже:

sum(str_detect(string = occupations, pattern = "teach|professor|tutor"))
[1] 3

Если вам нужно построить длинный список условий поиска, вы можете объединить их, используя str_c() и sep = |, затем определите как текстовый объект, а затем позже вы можете ссылаться на вектор более кратким образом. Пример ниже включает возможные виды деятельности для поиска медицинских работников на передовой.

# условия поиска
occupation_med_frontline <- str_c("medical", "medicine", "hcw", "healthcare", "home care", "home health",
                                "surgeon", "doctor", "doc", "physician", "surgery", "peds", "pediatrician",
                               "intensivist", "cardiologist", "coroner", "nurse", "nursing", "rn", "lpn",
                               "cna", "pa", "physician assistant", "mental health",
                               "emergency department technician", "resp therapist", "respiratory",
                                "phlebotomist", "pharmacy", "pharmacist", "hospital", "snf", "rehabilitation",
                               "rehab", "activity", "elderly", "subacute", "sub acute",
                                "clinic", "post acute", "therapist", "extended care",
                                "dental", "dential", "dentist", sep = "|")

occupation_med_frontline
[1] "medical|medicine|hcw|healthcare|home care|home health|surgeon|doctor|doc|physician|surgery|peds|pediatrician|intensivist|cardiologist|coroner|nurse|nursing|rn|lpn|cna|pa|physician assistant|mental health|emergency department technician|resp therapist|respiratory|phlebotomist|pharmacy|pharmacist|hospital|snf|rehabilitation|rehab|activity|elderly|subacute|sub acute|clinic|post acute|therapist|extended care|dental|dential|dentist"

Эта команда выдает число видов деятельности, которые содержат одно из терминов поиска для передовых медицинских работников (occupation_med_frontline):

sum(str_detect(string = occupations, pattern = occupation_med_frontline))
[1] 2

Функции поиска по последовательности в базовом R

Функция базового R grepl() работает аналогично str_detect(), в том плане, что она ищет соответствие комбинации символов и выдает логический вектор. Базовый синтаксис: grepl(pattern, strings_to_search, ignore.case = FALSE, ...). Преимуществом является то, что аргумент ignore.case легче написать (нет необходимости использовать функцию regex()).

Аналогично, базовые функции sub() и gsub() работают похожим на str_replace() образом. Их основной синтаксис: gsub(pattern, replacement, strings_to_search, ignore.case = FALSE). sub() заменит первое совпадение по комбинации, а gsub() заменит все совпадения по комбинации.

Конвертация запятых в точки

Вот пример использования gsub() для конвертации запятых в точки в векторе чисел. Это может быть полезно, если вы получаете данные из регионов мира за пределами США или Великобритании.

Внутренняя команда gsub(), которая сначала работает над lengths конвертирует любые точки в отсутствие пробелов ““. Знак точки”.” должен быть “изолирован” с помощью двух слэшей, чтобы он собственно означал точку, поскольку “.” в регулярном выражении(regex) означает “любой знак”. Затем результат (только с запятыми) передается во внешнюю функцию gsub(), в которой запятые заменяются на точки.

lengths <- c("2.454,56", "1,2", "6.096,5")

as.numeric(gsub(pattern = ",",                # находит запятые     
                replacement = ".",            # меняет их на точки
                x = gsub("\\.", "", lengths)  # вектор с другими точками удаляется (точки изолируются)
                )
           )                                  # конвертация выходных данных в числовые

Заменить все

Используйте str_replace_all() в качестве инструмента “найти и заменить”. Во-первых, задайте, какие последовательности будут оцениваться в аргументе string =, затем комбинацию, которую нужно заменить в pattern =, и затем на какое значение менять в replacement =. Пример ниже заменяет все случаи, когда встречается “dead” (мертвый) на “deceased” (скончавшийся). Обратите внимание, команда ЧУВСТВИТЕЛЬНА к регистру.

outcome <- c("Karl: dead",
            "Samantha: dead",
            "Marco: not dead")

str_replace_all(string = outcome, pattern = "dead", replacement = "deceased")
[1] "Karl: deceased"      "Samantha: deceased"  "Marco: not deceased"

Примечания:

  • Чтобы заменить комбинацию на NA, используйте str_replace_na().
  • Функция str_replace() заменяет только первое совпадение комбинации в каждой оцениваемой последовательности.

Обнаружение в рамках логических функций

В рамках case_when()

str_detect() часто используется в рамках case_when() (из dplyr). Представим, что в построчном списке есть столбец occupations. Функция mutate() ниже создает новый столбец под названием is_educator, используя условную логику с помощью case_when(). См. страницу вычистка данных для получения более подробной информации о case_when().

df <- df %>% 
  mutate(is_educator = case_when(
    # поиск по термину в роде деятельности, не чувствителен к регистру
    str_detect(occupations,
               regex("teach|prof|tutor|university",
                     ignore_case = TRUE))              ~ "Educator",
    # все прочие
    TRUE                                               ~ "Not an educator"))

Напоминаем, что может быть важным добавить критерии исключения в логику условий (negate = F):

df <- df %>% 
  # значение в новом столбце is_educator основано на условной логике
  mutate(is_educator = case_when(
    
    # столбец род деятельности должен соответствовать 2 критериям, чтобы ему было присвоено значение "Educator":
    # он должен иметь термин из поиска и НЕ СООТВЕТСТВОВАТЬ условиям исключения
    
    # должен иметь термин из поиска
    str_detect(occupations,
               regex("teach|prof|tutor|university", ignore_case = T)) &              
    
    # И НЕ ДОЛЖЕН подпадать под критерии исключения
    str_detect(occupations,
               regex("admin", ignore_case = T),
               negate = TRUE                        ~ "Educator"
    
    # Все строки, не соответствующие указанным выше критериям
    TRUE                                            ~ "Not an educator"))

Поиск позиции комбинации

Чтобы найти первую позицию комбинации, используйте str_locate(). В качестве результата вы получите стартовую и конечную позицию.

str_locate("I wish", "sh")
     start end
[1,]     5   6

Как и другие фукнции str, существует версия “_all” (str_locate_all()), которая выдает позиции всех случаев, когда встречается комбинация в каждой последовательности. Выходным результатом будет list.

phrases <- c("I wish", "I hope", "he hopes", "He hopes")

str_locate(phrases, "h" )     # позиция *первого* случая комбинации
     start end
[1,]     6   6
[2,]     3   3
[3,]     1   1
[4,]     4   4
str_locate_all(phrases, "h" ) # позиция *каждого* случая комбинации
[[1]]
     start end
[1,]     6   6

[[2]]
     start end
[1,]     3   3

[[3]]
     start end
[1,]     1   1
[2,]     4   4

[[4]]
     start end
[1,]     4   4

Извлечение совпадений

str_extract_all() выдает сами совпадающие комбинации, что наиболее полезно, если вы предложили несколько комбинаций с условием “ИЛИ”. Например, поиск по последовательности вектора рода занятий (см. предыдушую вкладку) любого из вариантов “teach”, “prof” или “tutor”.

str_extract_all() выдает список list, который содержит все совпадения для каждой оцениваемой последовательности. См. ниже, что у рода деятельности 3 имеется два соответствия по комбинациям.

str_extract_all(occupations, "teach|prof|tutor")
[[1]]
character(0)

[[2]]
[1] "prof"

[[3]]
[1] "teach" "tutor"

[[4]]
[1] "tutor"

[[5]]
character(0)

[[6]]
character(0)

[[7]]
character(0)

[[8]]
character(0)

[[9]]
character(0)

[[10]]
character(0)

str_extract() извлекает только первое совпадение в каждой оцениваемой последовательности, создавая текстовый вектор с одним элементом для каждой оцениваемой последовательности. Команда выдает NA в тех случаях, где не было совпадений. NA можно удалить, если вы обернете полученный вектор в na.exclude(). Обратите внимание, что второе совпадение рода деятельности 3 не показано.

str_extract(occupations, "teach|prof|tutor")
 [1] NA      "prof"  "teach" "tutor" NA      NA      NA      NA      NA     
[10] NA     

Подмножество и подсчет

Функции сопоставления включают str_subset() и str_count().

str_subset() выдает собственно значения, которые содержат комбинацию:

str_subset(occupations, "teach|prof|tutor")
[1] "university professor"           "primary school teacher & tutor"
[3] "tutor"                         

str_count() выдает вектор чисел: количество раз появления поискового термина в каждом оцениваемом значении.

str_count(occupations, regex("teach|prof|tutor", ignore_case = TRUE))
 [1] 0 1 2 1 0 0 0 0 0 0

Группы регулярных выражений

НАХОДИТСЯ В РАЗРАБОТКЕ

10.6 Специальные символы

Обратный слэш \ для изолирования

Обратный слэш \ используется для “изолирования” значения следующего знака. Таким образом, бэкслэш можно использовать, чтобы отобразить кавычку внутри других кавычек (\") - кавычка в середине не “поломает” кавычки вокруг.

Примечание - таким образом, если вам нужно отобразить обратный слэш, вы должны изолировать его значение еще одним обратным слэшем. То есть, вам нужно написать два обратных слэша, \\ чтобы отобразить один.

Специальные символы

Специальный символ Представляет собой
"\\" обратный слэш
"\n" новая строка
"\"" двойные кавычки внутри двойных кавычек
'\'' одинарная кавычка внутри одинарных кавычек
"\| аксан грав| возврат каретки| tab| вертикальный tab“` backspace

Чтобы увидеть полный список этих специальных символов выполните ?"'" в консоли R (он появится на панели RStudio Help (справка)).

10.7 Регулярные выражения (regex)

10.8 Регулярные выражения и специальные знаки

Регулярные выражения, или “regex” - это краткий язык описания комбинаций в последовательностях. Если вы с ним не знакомы, регулярное выражение может выглядеть как незнакомый язык. Здесь мы постараемся немного раскрыть его тайны.

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

Регулярное выражение часто применяется для извлечения конкретных комбинаций из “неструктурированного” текста - например, медицинских записей, основных жалоб, анамнеза жизни или иных столбцов со свободным текстом в датафрейме.

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

  1. Наборы символов
  2. Мета символы
  3. Квантификаторы
  4. Группы

Наборы символов

Наборы символов - способ выражения опций перечисления для совпадения по символам внутри скобок. Таким образом, будет определено совпадение, если любой из символов в скобках будет обнаружен в последовательности. НАпример, чтобы искать гласные буквы, можно использовать набор символов: “[aeiou]”. Часто встречаются следующие наборы символов:

Набор символов Совпадение для
"[A-Z]" любая одна заглавная буква
"[a-z]" любая одна строчная буква
"[0-9]" любая цифра
[:alnum:] любой алфавитно-цифровой символ
[:digit:] любая числовая цифра
[:alpha:] любая буква (заглавная или строчная)
[:upper:] любая заглавная буква
[:lower:] любая строчная буква

Наборы символов можно комбинировать в одних квадратных скобках (без пробелов!), например: "[A-Za-z]" (любая заглавная или строчная буква), еще один пример: "[t-z0-5]" (строчная буква от t до z ИЛИ цифра от 0 до 5).

Мета символы

Мета символы - сокращения для набора символов. Некоторые наиболее важные перечислены ниже:

Мета символ Представляет собой
"\\s" один пробел
"\\w" любой один алфавитно-цифровой знак (A-Z, a-z, or 0-9)
"\\d" любая одна цифра (0-9)

Квантификаторы

Как правило, вы не ищете совпадение только по одному символу. Квантификаторы позволяют вам определить длину букв/цифр, чтобы считать их совпадением.

Квантификаторы - цифры, записанные в фигурных скобках { } после знака, который они количественно определяют, например,

  • "A{2}" выдаст случаи двух заглавных букв A.
  • "A{2,4}" выдаст случаи, где от двух до четырех заглавных букв A (не ставьте пробелы!).
  • "A{2,}" выдаст случаи двух или более заглавных букв A.
  • "A+" выдаст случаи одной или более заглавной буквы A (группа расширяется, пока не будет встречен другой символ).
  • Поставьте в начале звездочку *, чтобы получить ноль или более совпадений (полезно, если вы не уверены, есть ли совпадения по комбинации)

Если вы используете знак плюс + как квантификатор, совпадение будет происходить, пока не будет встречен другой символ. Например, это выражение выдаст вам все слова (алфавитные символы: "[A-Za-z]+"

# проверка последовательности с квантификаторами
test <- "A-AA-AAA-AAAA"

Когда используется квантификатор {2}, выдаются только пары последовательно идущих A. В AAAA выявляется две пары.

str_extract_all(test, "A{2}")
[[1]]
[1] "AA" "AA" "AA" "AA"

Когда используется квантификатор {2,4}, выдаются группы последовательных A длиной от двух до четырех.

str_extract_all(test, "A{2,4}")
[[1]]
[1] "AA"   "AAA"  "AAAA"

При использовании квантификатора +, выдаются группы с одной или более:

str_extract_all(test, "A+")
[[1]]
[1] "A"    "AA"   "AAA"  "AAAA"

Относительная позиция

Выражает требования к тому, что идет до или после комбинации. НАпример, чтобы извлечь предложения, “два числа, за которыми идет точка” (""). (?<=\.)\s(?=[A-Z])

str_extract_all(test, "")
[[1]]
 [1] "A" "-" "A" "A" "-" "A" "A" "A" "-" "A" "A" "A" "A"
Заявление о позиции Соответствует
"(?<=b)a" “a” перед которой стоит “b”
"(?<!b)a" “a” перед которой НЕ стоит “b”
"a(?=b)" “a” после которой идет “b”
"a(?!b)" “a” после которой НЕ идет “b”

Группы

Учет групп в вашем регулярном выражении является способом получения более структурированных выходных данных после извлечения.

Примеры регулярных выражений

Ниже представлен свободный текст примера. Мы попробуем извлечь из него полезную информацию, используя поиск с помощью регулярного выражения.

pt_note <- "Patient arrived at Broward Hospital emergency ward at 18:00 on 6/12/2005. Patient presented with radiating abdominal pain from LR quadrant. Patient skin was pale, cool, and clammy. Patient temperature was 99.8 degrees farinheit. Patient pulse rate was 100 bpm and thready. Respiratory rate was 29 per minute."

Данное выражение ищет совпадения со всеми словами (любой символ, пока не наткнется на не-символ, например, пробел):

str_extract_all(pt_note, "[A-Za-z]+")
[[1]]
 [1] "Patient"     "arrived"     "at"          "Broward"     "Hospital"   
 [6] "emergency"   "ward"        "at"          "on"          "Patient"    
[11] "presented"   "with"        "radiating"   "abdominal"   "pain"       
[16] "from"        "LR"          "quadrant"    "Patient"     "skin"       
[21] "was"         "pale"        "cool"        "and"         "clammy"     
[26] "Patient"     "temperature" "was"         "degrees"     "farinheit"  
[31] "Patient"     "pulse"       "rate"        "was"         "bpm"        
[36] "and"         "thready"     "Respiratory" "rate"        "was"        
[41] "per"         "minute"     

Выражение "[0-9]{1,2}" ищет совпадения с последовательно расположенными цифрами, длиной 1 или 2 знака. Его можно также записать как "\\d{1,2}", либо "[:digit:]{1,2}".

str_extract_all(pt_note, "[0-9]{1,2}")
[[1]]
 [1] "18" "00" "6"  "12" "20" "05" "99" "8"  "10" "0"  "29"

Вы можете просмотреть полезный список выражений regex и советы на странице 2 данной шпаргалки

Также посмотрите этот самоучитель.

10.9 Ресурсы

Справочный лист по функциям stringr можно найти тут

Виньетку по stringr можно найти тут