Основные функции

library('stringr')

На прошлом семинаре мы разбирали функции из пакета stringr. Однако, внутри R есть встроенные функции, которые работают похожим образом. Сегодня мы будем использовать их, а также познакомимся с регулярными выражениями.

Хотим:

  • Идентифицировать совпадения в строке нашему шаблону: grep(..., value=FALSE), stringr::str_detect()
  • Вытащить совпадения в строке нашему шаблону: grep(..., value=TRUE), stringr::str_extract(), stringr::str_extract_all()
  • Найти, на каком месте находится шаблон внутри строки: regexpr(), gregexpr(), stringr::str_locate(), string::str_locate_all()
  • Вытащить сами строки, которые соответствуют шаблону: regmatches()
  • Заменить найденный шаблон: sub(), gsub(), stringr::str_replace(), stringr::str_replace_all()
  • Разбить строку, опираясь на заданный шаблон: strsplit(), stringr::str_split()

Регулярные выражения

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

Символ Описание
. один любой символ, кроме новой строки
? 0 или 1 вхождение шаблона слева
+ 1 и более вхождений шаблона слева
* 0 и более вхождений шаблона слева
\w любая цифра или буква
\W всё, кроме цифры или буквы
\d любая цифра
\D всё, кроме цифры
\s любой пробельный символ
\S любой непроблеьный символ
\b граница слова
[..] Один из символов в скобках
[^..] Любой символ, кроме тех что в скобках
\ Экранирование специальных символов (точки плюсы и тп)
^; $ Начало и конец строки соответственно
{n,m} От n до m вхождений
{,m} От 0 до m вхождений
a \| b a или b
( ) Группирует выражение и возвращает найденный текст
\t; \n табуляция, новая строка

Вперед к практике!

Создадим глупую строку и посмотрим, как будут работать наши функции.

our_str <- c('I love apple and apple pie', 'Apple ipad', 'apple ipod')

grep()

grep('apple', our_str)
## [1] 1 3

Видим, что заглавные буквы и строчные функция различает. Если мы не хотим этого, то в аргумент ignore.case нужно передать TRUE

grep('apple', our_str, ignore.case=TRUE)
## [1] 1 2 3

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

grep('apple', our_str, value=TRUE)
## [1] "I love apple and apple pie" "apple ipod"

Найдем каждую строку, начинающуюся с любого символа, за которым следует ‘pple’.

grep('^.p{2}le', our_str, value = TRUE)
## [1] "Apple ipad" "apple ipod"

Строки, которые содержат p перед d и любой один символ между

grep('p.d', our_str, value = TRUE)
## [1] "Apple ipad" "apple ipod"

Строки, которые содержат любую заглавную букву:

grep('[A-Z]', our_str, value = TRUE)
## [1] "I love apple and apple pie" "Apple ipad"

Строки, которые содержат любую заглавную или строчную букву

grep('[A-Za-z]', our_str, value = TRUE)
## [1] "I love apple and apple pie" "Apple ipad"                
## [3] "apple ipod"

Строки, содержащие pad или pod:

grep('p(a|o)d', our_str, value = TRUE)
## [1] "Apple ipad" "apple ipod"

regexpr() и gregexpr()

Находим стартовые индексы нашего шаблона в строке. Если -1, значит совпадений не найдено.

regexpr('apple', our_str)
## [1]  8 -1  1
## attr(,"match.length")
## [1]  5 -1  5
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE

gregexpr() выдает тоже самое, но уже для каждого совпадения в каждой строке. Также теперь на выход идет list(), а не массив!

gregexpr('apple', our_str)
## [[1]]
## [1]  8 18
## attr(,"match.length")
## [1] 5 5
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE
## 
## [[2]]
## [1] -1
## attr(,"match.length")
## [1] -1
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE
## 
## [[3]]
## [1] 1
## attr(,"match.length")
## [1] 5
## attr(,"index.type")
## [1] "chars"
## attr(,"useBytes")
## [1] TRUE

regmatches()

Вытаскиваем все значения строк, которые нашли с помощью regexpr()

m <- regexpr("apple", our_str)
regmatches(our_str, m)
## [1] "apple" "apple"

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

m <- regexpr("apple", our_str)
regmatches(our_str, m) <- 'orange'
our_str
## [1] "I love orange and apple pie" "Apple ipad"                 
## [3] "orange ipod"

sub() и gsub()

Заменяем по шаблону:

our_str <- c('I love apple and apple pie', 'Apple ipad', 'apple ipod')

Заменяем первое совпадение:

x <- sub("apple", "orange", our_str)
x
## [1] "I love orange and apple pie" "Apple ipad"                 
## [3] "orange ipod"

Заменяем все совпадения:

x2 <- gsub("apple", "orange", our_str)
x2
## [1] "I love orange and orange pie" "Apple ipad"                  
## [3] "orange ipod"

strsplit()

Разделяем каждую строку в массиве по какому-то шаблону. Например, можем разделить по словам с помощью \\W

x <- c("I love apple and apple pie", "Apple ipad", "apple ipod")
strsplit(x, split = "\\W")
## [[1]]
## [1] "I"     "love"  "apple" "and"   "apple" "pie"  
## 
## [[2]]
## [1] "Apple" "ipad" 
## 
## [[3]]
## [1] "apple" "ipod"

Еще примеры на регулярки

  1. Найти слова, которые состоят более, чем из двух букв
text <- 'AV is largest Analytics community of India'
# пишем сам запрос
m <- gregexpr('\\w\\w\\w+', text, perl=TRUE)
# выводим совпадения
regmatches(text, m)
## [[1]]
## [1] "largest"   "Analytics" "community" "India"
# либо так
str_extract_all(text, '\\w\\w\\w+')
## [[1]]
## [1] "largest"   "Analytics" "community" "India"
  1. Вернуть в таких словах 2 первые буквы:
str_match_all(text, '(\\w\\w)\\w+')
## [[1]]
##      [,1]        [,2]
## [1,] "largest"   "la"
## [2,] "Analytics" "An"
## [3,] "community" "co"
## [4,] "India"     "In"
  1. Вернуть список доменов из списка адресов электронной почты
email <-  'abc.test@4gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz'

regmatches(email, gregexpr('@\\w+', email, perl=TRUE))
## [[1]]
## [1] "@4gmail"          "@test"            "@analyticsvidhya" "@rest"
  1. Извлечь дату из строки
dates <-  'Amit 34-3456 12-05-2007, XYZ 56-4532 11-11-2011, ABC 67-8945 12-01-2009'
regmatches(dates, gregexpr('\\d+-\\d+-\\d+', dates, perl=TRUE))
## [[1]]
## [1] "12-05-2007" "11-11-2011" "12-01-2009"
  1. Отфильтровать весь мусор и оставить только буквы
trash <-  'Маша-/ .,съелhttps:а _=парёу .пирогов 22се.годня'
gsub('[^А-Яа-я ]','', trash)
## [1] "Маша съела пару пирогов сегодня"
  1. Очистка от html мусора в виде тегов
text <-  '<div class="wall_post_text">«Индексы в IntelliJ IDEA зависят только от содержимого одного файла. С одной стороны, это очень удобно. С другой стороны, это накладывает большие ограничения на то, что можно поместить в индекс».<br><br> О том, как в IntelliJ IDEA ищут лямбда-выражения: <a href="/away.php?to=http%3A%2F%2Famp.gs%2F4iBG&amp;post=-20629724_1179782&amp;cc_key=" target="_blank" rel="noopener">#разработка@habr</a></div>'

gsub('<[^<]+?>', '', text)
## [1] "«Индексы в IntelliJ IDEA зависят только от содержимого одного файла. С одной стороны, это очень удобно. С другой стороны, это накладывает большие ограничения на то, что можно поместить в индекс». О том, как в IntelliJ IDEA ищут лямбда-выражения: #разработка@habr"