Источник: Nuances of Programming
Сейчас легко найти данные. Но вот найти высококачественные оказывается весьма проблематично. Одна из характерных черт низкопробных данных в том, что они запутаны и редко точны. Сколько бы мы, профессионалы в этой сфере, не говорили об алгоритмах и проверке моделей, большую часть времени занимает именно очистка данных.
В этом смысле работа со строками требует несколько иного набора навыков, чем работа с теми же списками или data.frame. В текущей статье, как вы уже поняли, мы будем учиться максимально эффективно управлять строками. Начнем!
Вставка и разделение
Вставка и разделение частей строк — это две из наиболее типичных задач, с которыми мы встречаемся.
Для них у нас есть две простые функции: paste() и strsplit().
Общее число символов и разделение
У R и Python есть немало общего. Оба этих языка легки в освоении и за счет своих гибких библиотек становятся типичным выбором среди статистов, практиков машинного обучения, да и любых интересующихся наукой о данных людей. Если же вы пришли в R из Python, то примеры, которые я планирую показать, могут показаться вам несколько странными.
Например, для нахождения общего количества символов интуитивным выбором будет lengh(). Но в R это происходит не так, как в Python.
Разделение здесь тоже отличается. Для этого у нас есть две функции: substr() и substring(). Они работают абсолютно одинаково, если указать параметры начала и завершения. Тем не менее у substring() есть предустановленное стоп-значение, а у substr() нет.
Вот что происходит, если не передать аргумент в параметр stop:
regexec() , gregexpr() и grep()
Я уже слышу ваш вопрос: “А откуда нам знать индексы для передачи аргументов?”. Что ж, когда у вас всего один фрагмент строки, то это несложно подсчитать на пальцах, но в случае миллионов, да даже десятков, строк данных такой подход не сгодится. К счастью, у нас есть для этого две прекрасные функции.
Первая из них, regexec(), используется для поиска первого вхождения подстроки внутри большой строки.
При этом gregexpr() находит все вхождения подстроки.
Функция grep() получает второй аргумент, который отражает уже не фрагмент строки, а вектор строк. В ответ же она возвращает индексы элементов, которые содержат искомую подстроку. Если дополнительно установить значение параметра на T (TRUE), функция вернет сами элементы.
sub() и gsub()
sub() и gsub() идут еще дальше и заменяют часть большой строки, соответствующую заданной подстроке, строкой, переданной в качестве аргумента.
Если вы заметили, что слово “America” осталось прежним, то дело в том, что sub() заменяет только первое вхождение подстроки. Для замены всех мы используем gsub().
Регулярные выражения (REGEX)
До этого момента мы искали простые фрагменты подстрок внутри других строк. Но искомая подстрока может быть не такой простой, как в примерах. Мы даже можем не знать, какую конкретно подстроку нужно найти, и искать такую, которая будет вписываться в определенную модель. В подобных случаях мы задействуем регулярные выражения, или regex.
Эти выражения встречаются во многих языках программирования и несколько отличаются в реализации. Их основная задача — искать паттерн строки в заданной строке большего размера.
Регулярные выражения не получают точную подстроку, которую нужно найти. Вместо этого они ищут такие подстроки, которые вписываются в переданный им паттерн. Для этого у них есть собственный мини-язык и метасимволы. Я подробно объясню все метасимволы и правила, которые определяют работу регулярного выражения.
Метасимволы
Первым делом нужно сказать, что символы мини-языка регулярных выражений называются метасимволами и представляют собой их каркас.
- “$”
- “*”
- “+”
- “.”
- “?”
- “[ ]”
- “^”
- “{ }”
- “|”
- “( )”
- “\ ”
Далее я объясню действие каждого из этих метасимволов.
Квантификаторы
Среди метасимволов знаки “?” , “*” , “+” и “{ }” называются квантификаторами, потому что указывают, сколько раз мы хотим увидеть заданный паттерн.
- “*”: предыдущий элемент должен встречаться 0 или более раз.
- “+”: предыдущий элемент должен встречаться 1 или более раз.
- “?”: предыдущий элемент должен встречаться 0 или 1 раз.
- “{ ,m}”: предыдущий элемент должен встречаться m или меньше раз.
- “{n, }”: предыдущий элемент должен встречаться n или более раз.
- “{n , m}”: предыдущий элемент должен встречаться от n до m раз.
- “{m}”: предыдущий элемент должен встречаться ровно m раз.
Метасимволы начала и завершения
Символы “^” и “$” представляют начало и конец строки соответственно. Иногда их еще называют якорями. При этом они никаким символам не соответствуют.
Плейсхолдер
Следующий метасимвол — это “.”, который соответствует любому символу в том месте, где используется. В примере ниже ищется любой паттерн, начинающийся с “C”, заканчивающийся на “A” и имеющий между этими символами любые два символа.
Последовательности
Метасимвол “\” при использовании с набором ключевых букв служит для определения конкретной последовательности символов в строке и сопоставляется с этой последовательностью при использовании в функциях для строк. Ниже приводится список ключевых букв, которые часто используются с этим метасимволом:
- “\d” = цифра;
- “\D” = не цифра;
- “\w” = словесный символ (a-z, A-Z, 0???9);
- “\W” = не словесный символ;
- “\s” = пробельный символ;
- “\S” = не пробельный символ;
- “\b” = граница слова;
- “\B” = не граница слова.
Вот примеры:
Символьные классы
Еще один метасимвол — это “[ ]”, который зачастую служит для формирования комплексных паттернов при анализе сложных и неструктурированных текстовых данных. В эти квадратные скобки можно передать несколько символов, в результате чего будут найдены только они, но не вместе, а по-отдельности, порядок при этом значения не имеет. Также можно указывать диапазон искомых символов с помощью дефиса.
В определенных случаях можно заключать в квадратные скобки встроенные имена классов. Ниже приводится полный список таких имен.
- [:alnum:] = буквенно-цифровые символы: [:alpha:] и [:digit:].
- [:alpha:]= буквенные символы: [:lower:] и [:upper:].
- [:blank:]= пустые символы: пробелы и табуляции, а также определяемые языковым стандартом, например неразрывный пробел.
- [:cntrl:] = управляющие символы в ASCII. Эти символы имеют восьмеричные коды от 000 до 037 и 177 (DEL). В других наборах символов они являются равнозначными, если присутствуют.
- [:digit:] = цифры: 0 1 2 3 4 5 6 7 8 9.
- [:graph:] = графические символы: [:alnum:] и [:punct:].
- [:lower:] = буквы нижнего регистра в текущем языковом стандарте.
- [:print:] = печатные символы: [:alnum:], [:punct:] и пробел.
- [:punct:] = знаки препинания: ! “ # $ % & ’ ( ) * + , — / : ; < = > ? @ [ ] ^ _ ` { | } ~. “.
- [:space:] = пробельные символы: табуляция, новая строка, вертикальная табуляция, возврат каретки, перевод страницы, а также другие символы, определяемые различными языковыми стандартами.
- [:upper:] = буквы верхнего регистра в текущем языковом стандарте.
- [:xdigit:] = шестнадцатеричные цифры: 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f.
Группировка и оператор ИЛИ
Последними метасимволами у нас идут “()” и “|”, которые обычно используются вместе. С помощью скобок мы обособляем нужные наборы символов, а оператор ИЛИ позволяет указывать на возможность выбора между ними. Вот примеры:
Экранирование
Мы рассмотрели все метасимволы, но что, если искомый паттерн содержит один из них?
Это очевидный и ожидаемый вопрос. В таком случае нужно сообщить R, что этот символ нужно рассматривать не как метасимвол, а как обычный знак.
Для этого мы добавляем тот же обратный слэш “\” перед нужным метасимволом, экранируя его. А поскольку обратный слэш сам является метасимволом, мы добавляем к нему еще один слэш для экранирования. Вот примеры:
В этой статье мы разобрали все функции, которые используем в работе со строками. После этого мы рассмотрели одиннадцать метасимволов, с помощью которых можем создавать сложные паттерны для поиска.
Читайте также:
Перевод статьи Uğurcan Demir: A Concise Guide for Strings and Regular Expressions in R