2 Работа со строками

Сейчас мы немного вспомним о работе со строками в R. Мы посмотрим, как выглядят строки в R, как их можно соединять-разделять, какие функции есть для работы с ними (например,сортировка, поиск и замена подстроки, изменение регистра, транслитеряция и пр). Также мы потрогаем несколько удобных библиотек для работы со строковыми данными. Поехали!

Работать со строками можно с помощью base R, но чаще всего удобнее пользоваться специальными пакетами: * stringr, который входит в tidyverse (‘str_’) * stringi (‘stri_’)

library(tidyverse)
library(stringi)

2.1 Запись строк

Для начала посмотрим, как можно получить строку.

  • Присвоение

Надо вот так:

string1 <- "Я строка"
string2 <- 'И я строка'
string1
## [1] "Я строка"
string2
## [1] "И я строка"

Вот так вот не надо:

string3 <- "Я тоже своего рода "строка"
string3
## Error: <text>:1:33: unexpected symbol
## 1: string3 <- "Я тоже своего рода "строка
##                                     ^

NB! Следите за кавычками

string4 <- "Ну вот 'так' вроде норм"
string5 <- 'И "так" тоже'
string4
## [1] "Ну вот 'так' вроде норм"
string5
## [1] "И \"так\" тоже"

Экранирование специальных символов:

string6 <- "Экранирование \"лишних\" кавычек"
string6
## [1] "Экранирование \"лишних\" кавычек"

R (как и другие языки) может читать текст вместе со специальными управляющими символами (такими, например, являются кавычки ' и " или бэкслэш \), а может читать просто как текст. Если символов мало, то их можно экранировать, добавив перед ними бэкслэш. Если много, можно использовать специальную функцию для отключения экранинования и переведения всех символов в “сырые” (=читаются как текст) R"()" - см. подсказки в справке ?Quotes.

  • Печать строки

Функции для печати строк вам, скорее всего, известны: print, paste, paste0, cat, noquote, format

format(13.495902982, digits=5)
## [1] "13.496"
  • Преобразование
typeof(4:7)
## [1] "integer"
as.character(4:7)
## [1] "4" "5" "6" "7"
string7 <- (1:9) ^ 2
toString (string7)
## [1] "1, 4, 9, 16, 25, 36, 49, 64, 81"

Преобразование таблиц в строки

NB! Функции data.frame(), read.csv(), read.csv2(), read.table() из base R до последней версии R по-умолчанию превращали строки в факторы. Если ваша версия R младше 4.0.0, то чтобы избежать превращения строк в факторы, необходимо указывать аргумент stringsAsFactors = FALSE.

str(data.frame(letters[1:5], LETTERS[1:5], stringsAsFactors = TRUE))
## 'data.frame':    5 obs. of  2 variables:
##  $ letters.1.5.: Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
##  $ LETTERS.1.5.: Factor w/ 5 levels "A","B","C","D",..: 1 2 3 4 5
str(data.frame(letters[1:5], LETTERS[1:5], stringsAsFactors = FALSE))
## 'data.frame':    5 obs. of  2 variables:
##  $ letters.1.5.: chr  "a" "b" "c" "d" ...
##  $ LETTERS.1.5.: chr  "A" "B" "C" "D" ...
  • Генерация

Генерация пустых строк

character(3)
## [1] "" "" ""

Генерация рандомных строк

set.seed(42)
stri_rand_strings(n = 10, length = 5:14)
##  [1] "uwHpd"          "Wj8ehS"         "ivFSwy7"        "TYu8zw5V"      
##  [5] "OuRpjoOg0"      "p0CubNR2yQ"     "xtdycKLOm2k"    "fAGVfylZqBGp"  
##  [9] "gE28DTCi0NV0a"  "9MemYE55If0Cvv"

Генерация псевдорандомных строк (классический текст-заполнитель Lorem ipsum)

stri_rand_lipsum(nparagraphs = 2)
## [1] "Lorem ipsum dolor sit amet, non, consectetur aliquam mollis velit accumsan condimentum sit sed. Eu dapibus habitant faucibus interdum vel? Libero amet lacus aliquam ac sit, porta? Leo leo dolor enim eu rutrum volutpat posuere, pulvinar, inceptos bibendum aliquam quis. Laoreet vulputate inceptos posuere eu, id vel pretium eu eu. Ut in urna ac laoreet scelerisque hendrerit. Metus integer purus nec, purus parturient dolor eget quis pharetra tristique. Donec in lacus vehicula orci, ligula faucibus per. Sem amet tempus nascetur, tincidunt euismod pellentesque sit? In vitae ligula nec pellentesque sed hac. Curabitur ut semper lectus justo ut suspendisse ut at faucibus dolor. Nec non neque senectus donec sit nunc. Urna sed, ultricies, ac pharetra orci luctus, iaculis ac."
## [2] "Tincidunt cum neque eu semper at sociosqu hendrerit eu aliquet lacus, eu hendrerit. Donec aliquam eros risus nibh quam in sit facilisi ipsum. Amet sem sed donec sed molestie, scelerisque tincidunt nisl. Donec et facilisis interdum non sed dolor purus in ipsum, dignissim torquent velit. Nec aliquam pellentesque, ac adipiscing neque et at torquent. Vestibulum ullamcorper ad dictumst. Enim velit non nulla, felis habitant egestas placerat consectetur. Dictum nostra sed nec. Erat phasellus dolor libero aliquam viverra, vestibulum leo et suscipit. Egestas in in montes sapien gravida conubia purus varius ut nec. Feugiat, risus eleifend magnis neque diam suspendisse ullamcorper nulla adipiscing, malesuada massa nisi."
  • встроенные векторы
letters
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
## [20] "t" "u" "v" "w" "x" "y" "z"
LETTERS
##  [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
## [20] "T" "U" "V" "W" "X" "Y" "Z"
month.name
##  [1] "January"   "February"  "March"     "April"     "May"       "June"     
##  [7] "July"      "August"    "September" "October"   "November"  "December"
  • чтение из файла

Чтение таблицы Чтение таблицы Функция read_csv взята из пакета readr, который является частью tidyverse. Для чтения таблиц также есть функции read_csv2(), read_tsv() и read_delim(). В каких случаях они используются вы можете узнать в справке, добавив перед именем функции вопросительный знак: ?read_csv2()

sample <- read_csv("https://github.com/agricolamz/2021.02.21-23_icedone/raw/master/data/data_ficbook_untamed_sample.csv")
head(sample)

Чтение текста Прочитать файл полностью можно с помощью функций из пакета readr: read_file() и read_lines(). NB! read_file() будет работать очень долго, если размер текста большой.

my_file <- read_file("https://raw.githubusercontent.com/agricolamz/2021.02.21-23_icedone/master/data/Cox_Forshaw_WHy_E_equals_mc_squared_ocr.txt")
my_file
## [1] "ПОМI N1% /;\r\nIII г2’»\r\nпо\tона может принимать разные\r\n„ труднее определить.\t„„ происходило, общее к„.\r\nЭнергию Р> тоЧНо очевиден.\tаТЬСя неизменным не-\r\n,;.г,ои процессе\tэмми №тер дала нам\r\nзависимо от изменения других\tп<)Т0Му, что законы физики\r\nглубокое объяснение. Энергия ^\tутверЖде„ие не означает, что\r\nис изменяются с течением прем\tосто бессмысленно. На самом\r\n„„чего не происходит - это Ьъ спрапедлипы сегодня, деле оно означает, что если уН»\tВы можете заменить ело-\r\nто „ни должны быть 2»™Л;В“ла,, любым другим фундаментальным\r\nвосочетание «уравнения Максвелл\tмер.\r\nзаконом физики-постулатами Эинште\tимпульса, закон со-\r\nВместе с тем, как и в случае закона сохранения . У ^\t„\r\nхранения энергии был открыт экспериментальным )\t•\r\nоткрытия восходит к промышленной революции. Все началось с раоо эксиериментаторов-практиков, которые обнаружили множество с. „„четких и химических явлений в поисках промышленного Иерусалима-К числу таких людей относился и несчастный граф Румфорд Баварский (рожденный под именем Бенджамин Томпсон в Массачусетсе в 1753 году I, работа которого состояла в высверливании каналов в пушечных стволах для армии герцога Баварии. В процессе работы он обратил внимание, что металл пушечного ствола и сверло нагреваются, и справедливо предположил, что вращательное движение сверла превращается в тепло под воздействием трения. Это прямо противоположно тому, что происходит в паровом двигателе, где тепло преобразуется во вращательное движение колес поезда. Казалось вполне естественным связать некую общую величину с теплом и вращательным движением, поскольку, как выяснилось эти две на первый взгляд совершенно разные вещи взаимозаменяемы Эта величина - энергия. Румфорда называли несчастным, потому что\r\nон женился на вдове другого великого ученого, Антуана Л тш,-\r\n’\t7 ,ав>лзье, после\r\nтого как тот во время Французской революции сложил голову н » п\r\nтине. Румфорд ошибочно решил, что эта женщина будет делать дЛ},\r\nто же, что и для Лавуазье, прилежно записывая все результаты СГо *\" °\r\nботы и повинуясь ему, как полагалось хорошей жене в XVIII ст,„1СПи Но оказалось, она проявляла кроткость только под давлением железног\r\nра-\r\nолетиц.\r\nпо\r\n«ЕМ*\r\nЖ*\r\n(. тс*7\r\nII\r\n, л..у>»'- в св°ей ммеЧаТе\"ЬНОЙ книге 1 Ьс Оием Гог АЬзо1ц1с 2его-Мендельсон писал, что эта женщина превратила жизнь графа Ру„.\r\nВ1\r\nКУРТ\r\n„ „ яп Но главное не это, а то, что энергия всегда соуп™ ,\r\nхорда в ад-\t_\tг ^да сохраняется, имен-\r\n* п^тШу она вызывает такой интерес.\r\nяо ПОЭ 7\r\nПопросите кого-либо на улице объяснить, что такое энергия, -й получите либо осмысленный ответ, либо кучу всякого вздора в духе „ью-эйдж” В массовой культуре существует много разных значений слова «энергия», поскольку оно употребляется очень широко. Следует отметить, однако, что на самом деле есть точное определение энергии, которое нельзя использовать для объяснения лей-линий***, исцеления кристаллами, жизни после смерти или реинкарнации. Здравомыслящий человек мог бы сказать, что энергию можно хранить внутри аккумуляторной батареи, где она находится в состоянии ожидания до тех пор, пока кто-то не «замкнет цепь». Кто-то другой, возможно, возразит, что энергия — это показатель количества движения и что быстро движущиеся объекты обладают большей энергией, чем более медленные. Энергия, которую содержит море или ветер, — вот еще примеры определений. Вам могут также сказать, что горячие объекты содержат больше энергии, чем холодные. Гигантский маховик, который находится вн\\ три электростанции, может накапливать энергию, которая высвобожда ется затем в электросеть для удовлетворения потребностей населения в электроэнергии. Кроме того, энергия выделяется в процессе деления .томного ядра. Это только несколько примеров присутствия энергии в повседневной жизни. Во всех этих сяучаях физик,, могут представить\r\nгЬпоме и использовать эту информацию для энергию в количественной фор«\t^\t,ффеИ\r\nподведения\t“менным общее количество энергии,\r\nлюбого процесса сохран\r\n--------------- Мендельсон К. А. Г. На пути к абсолютному нулю: Вве-\r\n* ИЗДаНЗ «'у низких температур. М.: Атомиздат. 1971. пение в физику ”\r\nм\t^\tокупности различных мистических течении и движе-\r\nОбщее название со УгноГО, эзотерического и синкретического характера.\r\nНИЙ. В ОСНОВНОМ окк>\r\nПрим, ред-\t^ места на поверхности Земли, которым свойственно прояв-\r\n... Предположителен _ энеррии>> Прим авт_\r\nление\r\n112\r\nсохранения энергии в действу\r\n. . . .у-П 1\\|« 111\r\nДпя того чтобы увидеть закон сохр,,,.^...\r\nследпии раз вернемся к сталкивающимся билья '\r\n-»\"«й из них обладает определенной\t«ч-*\r\nтог\r\nв ПОС\r\nм\r\n:д.\r\nю№ 11\t«***•«*з........,«* ««у»\t««««^\r\n“;„Г«ое,о да»*'»»\"' Некого языка слоя., «кинетический. ^\r\nили возникающий вследствие яви*..,,\r\nделястся как «обусло\r\n— * и\r\nтермин. Ранее мы исходили из того, что д„а 111а^ ____стью и имеют одинаковую массу. Затем <*,\r\nтак что это правильный\t....\r\nс одинаковом скор0^ -^ ^ ^ с равной скоростью в проп\r\n— - -ал»* 11/ЭПЛ Г* ПТЧП !«!/>.\r\nДВИЖУТСЯ С ОДИН» --- •\r\nсталкиваются и отскакивают И —■ ^ значите/1ЬНОЙ мере продиктова\r\nположных направлениях. .\tтщательный анализ ситуации па\r\n.законом сохранения импульса. Б««™*\tле\r\nзлоллет определить, что скорость движения шар\r\nнемного ...к. их скорости „о столкновения. Это объясняется темли\r\nчаек начальной энергии рассеялась во время столкновения. Наиболее\r\nочевидное рассеяние энергии — переход ее части в звук. Когда бильярд\r\nные шары сталкиваются друг с другом, они воздействуют на молек.\r\nвоздуха, и это возмущение достигает наших ушей. Таким образом, час:\r\nначальной энергии теряется, из-за чего у шаров после столкновения оста\r\nется меньше энергии. С точки зрения темы данной книги нам на само *\r\nделе не нужно знать, как измерить энергию во всех ее проявлениях, хот-\r\nфьрмул. кинетической энергии нал, „се же пригодится немного позже\r\nКаждый, кто изучал л средней школе физику, навсегда запомнил эту фор мулу, кинетическая энергия = в п\tД запомнил ЭТ\\ ^ г\r\nвыразить в количественной ф„р2„е одним чисдТмТ ЧТ° ЭНЧ>\"\"“ лмчество энергии в системе всегда о г .\tТ“КЖе ЧТО ООЩсе\r\nА \"\"Ч\"- вернемся к нашему разговор’\" К'иам*«\"ЫМ.\r\n‘ \" к'1,4 1ве примера величины, котоиа\tЫ \"Паи1 кониепцию импуль\r\nс гиер'ией практическая польз. ймпу„ьёГ“й“*“*\"* “ектоР°М Наряд)\r\n'Управлении движения о *НИз> на юго-ВОСТо 5 СЧетУ вектор им\r\nсклонность разделять \"\"\" \"Рц1ы«У1ЧУЮ глав! \" '\"\"Йом «РУ™ -Ипм-У«ныстрел^.Г:;;е^;Р»пст1........... лр^ «окаэшиГи.\r\n' ““\"“‘■«ы» четыре ЭТО«6лУжде-\r\nНапРпвле„„ях\r\nч\r\n\r\nЖЕ *\r\n\r\nII .4\r\nПГ1\r\nнства-времени, в противном случае мы так и не сможем соста-лГнламеИтальные уравнения с учетом теории Эйнштейна. Позволь-■Г^овторнтв еще раз: фундаментальные уравнения должны включать г* ,я объекты, существующие в пространстве-времени, а не объекты, * ествующие отдельно в пространстве или во времени, поскольку ь^ъекты такого типа носят субъективный характер. Если вы помните, [в*размер объекта в пространстве, ни промежуток времени между двумя мбытиями нельзя отнести к категории величин, со значением которых\r\nсогласятся все без исключения. Именно это мы имеем в виду, утверждая, что такие объекты носят субъективный характер. Импульс также представляет собой вектор, направленный куда-то только в пространстве. Такое предубеждение против времени сеет семена его разрушения. Предве щает ли пространство-время крушение этого самого фундаментального из всех законов физики? Вновь открытая структура пространства-времени действительно сеет семена разрушения, но она указывает нам так же дальнейший путь: нам необходимо найти инвариантную величину, которая сможет занять место устаревшего трехмерного импульса. А вот и ключевой момент нашего повествования: такая величина существует Давайте внимательнее взглянем на трехмерный вектор импульса. На рис. 11 он представлен в виде стрелки, которая может отображать рас стояние, на которое откатывается шар, перемещаясь по столу*. Если опи-\r\nсывать ситуацию точнее, то предположим, что в полдень шар находи гем\r\nУ одного конца этой стрелки, а через две секунды — у другого. Если шар перемещается на сантиметр каждую секунду, тогда длина стрелки равна Двум сантиметрам. Получить вектор импульса не составляет проблем Он представляет собой стрелку, указывающую абсолютно в том же на\r\nправлении, что и на рис. 11, но ее длина другая и равна скорости нашего Шара (в данном случае один сантиметр в секунду), умноженной на его массу, которая составляет, к примеру, десять граммов. Физики сказали бы, что вектор импульса этого шара имеет длину десять грамм сантиметров в секунду (в краткой форме они записали бы это гак: 10 I' • см/с). Здесь снова целесообразно ввести абстрактные символы, вместо юго чтобы\r\n■ В выборе шара нет ничего особенного. Вместо него можно было бы выбрать любой другой объект.\r\n"

2.2 Количество символов

  • Подсчет

Аналогом функции nchar() из базового R в пакете stringr является функция str_length. NB! Эта функция может давать неверные значения, так как подсчитывает количество кодовых точек, используемых для кодирования символа. Чаще всего одному символу соответствует одна кодовая точка, но может быть и больше.

str_length(c("Hello", "", NA))
## [1]  5  0 NA

Также для подсчета количества знаков в строке можно использовать функцию str_count, которая позволяет также подсчитывать количество паттернов (об этом см. пункт о поиске подстроки ниже). Таблица tibble - аналог data.frame, используемый в библиотеке tidyverse.

tibble(mn = month.name) %>% 
  mutate(n_charactars = str_count(mn))
  • Приведение к желаемому количеству

Иногда нам нужно сократить строку до определенного количества символов. Это легко сделать с помощью функции str_trunc(). Например, можно сократить количество знаков в строке до 6 (по умолчанию индикатор сокращения - ellipsis - это многоточие):

tibble(mn = month.name) %>% 
  mutate(mn_new = str_trunc(mn, width=6))

Функция позволяет также изменить индикатор сокращения и определить, сокращаем мы строку за счет ее начала, середины или конца:

tibble(mn = month.name) %>% 
  mutate(mn_new = str_trunc(mn, 6, ellipsis = "-", side = "left"))

Обратная функция для “раздувания” строки - str_pad(). По умолчанию это делается за счет пробелов " "

tibble(mn = month.name) %>% 
  mutate(mn_new = str_pad(mn, 10))

Аргументы аналогичны:

tibble(mn = month.name) %>% 
  mutate(mn_new = str_pad(mn, 10,  pad = ".", side = "both"))

Для форматирования строки в соответствии с определенной шириной и отступами используют функцию str_wrap(). Например, попрорбуем представить первый параграф Lorem ipsum в виде строк с шириной не более 30 знаков (без переноса слов)

some_txt <- stri_rand_lipsum(nparagraphs = 1)
cat(str_wrap(some_txt, width = 30))
## Lorem ipsum dolor sit
## amet, metus integer iaculis
## tincidunt ac orci convallis.
## Metus duis neque ligula
## natoque nostra vel montes
## fringilla. Efficitur arcu
## dolor vel et diam, tincidunt,
## rhoncus, ad donec. Rutrum
## mauris ornare suspendisse
## justo, vehicula id diam,
## mollis ut. Velit et eu, mauris
## nunc vestibulum sapien. Augue
## dignissim vulputate montes
## ipsum rutrum, eu, eu. Porta
## faucibus maximus ac fusce
## placerat. In imperdiet libero
## cum conubia lacus. Ornare
## tempor vel nam, pulvinar,
## imperdiet sit, vestibulum
## ipsum orci.

Можно, например, указать отступ:

cat(str_wrap(some_txt, width = 30, indent = 2), "\n")
##   Lorem ipsum dolor sit
## amet, metus integer iaculis
## tincidunt ac orci convallis.
## Metus duis neque ligula
## natoque nostra vel montes
## fringilla. Efficitur arcu
## dolor vel et diam, tincidunt,
## rhoncus, ad donec. Rutrum
## mauris ornare suspendisse
## justo, vehicula id diam,
## mollis ut. Velit et eu, mauris
## nunc vestibulum sapien. Augue
## dignissim vulputate montes
## ipsum rutrum, eu, eu. Porta
## faucibus maximus ac fusce
## placerat. In imperdiet libero
## cum conubia lacus. Ornare
## tempor vel nam, pulvinar,
## imperdiet sit, vestibulum
## ipsum orci.

Иногда в начале и/или конце строки присутствуют лишные пробелы, от которых нам хотелось бы избавиться. Для быстрого “отсечения” пробелов используют функцию str_trim():

raw_strings <- c("Эта ", " строка ", ", ", " имеет", " кучу", " пробелов ")

#Можно указать, с какой стороны убирать пробелы
str_trim(raw_strings, side = "left")
## [1] "Эта "      "строка "   ", "        "имеет"     "кучу"      "пробелов "
#А можно убрать с обоих концов
str_trim(raw_strings, side = "both")
## [1] "Эта"      "строка"   ","        "имеет"    "кучу"     "пробелов"

2.3 Сортировка

Вопрос выбора метода сортировки строк - не из самых простых. Алгоритмов сортировки существует множество, скорость их работы отличается в зависимости от формата данных и способа их хранения. Для выбора алгоритма следует учитывать два аспекта: скорость его работы и объем необходимой памяти.

(с) [BigOcheatsheet] (https://www.bigocheatsheet.com/)

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

(с) [BigOcheatsheet] (https://www.bigocheatsheet.com/)

Стандартное время работы алгоритмов сортировки - логарифмическое, т.е. O(nlogn), где n - это количество наблюдений (длина массива), однако для сортировки строк есть и более быстрые решения, например MSD radix sort с линейной скоростью работы O(kn), где n - это количество наблюдений, а k - количество разрядов. Визуализацию работы популярных алгоритмов сортировки можно увидеть здесь. В этом видео можно узнать, как начать разбираться в алгоритмах, если вам интересно.

Таким образом, для сортировки больших массивов строковых данных в базовом R можно использовать функцию sort() с указанием метода radix sort: sort(..., method = "radix")

В пакете stringrдля сортировки есть функции str_sort() и str_order(). Встроенный алгоритм сортировки в них не изменить, но зато можно задать локаль.

unsorted_strings <- c("a", "b", "ba", "e", "i", "б", "я")

#english
str_sort(unsorted_strings, locale = "en")
## [1] "a"  "b"  "ba" "e"  "i"  "б"  "я"
#hawaiian
str_sort(unsorted_strings, locale = "haw")
## [1] "a"  "e"  "i"  "b"  "ba" "б"  "я"
#default
str_sort(unsorted_strings)
## [1] "a"  "b"  "ba" "e"  "i"  "б"  "я"
#ukranian
str_sort(unsorted_strings, locale = "ru_UA")
## [1] "б"  "я"  "a"  "b"  "ba" "e"  "i"

NB! Знайте локаль своего компьютера, от нее зависит порядок сортировки. Функция stringi::stri_locale_list() покажет вам список локалей на компьютере. Ознакомьтесь также с stringi::stri_locale_info и stringi::stri_locale_set

2.4 Конкатенация

Конкатенация - это операция склеивания, сцепления. Функция str_c() позволяет склеивать строки в одну строку.

str_c("h", "e", "l", "l", "o")
## [1] "hello"

Можно указать разделитель аргументом sep:

str_c("h", "e", "l", "l", "o", sep="_")
## [1] "h_e_l_l_o"

NB! NA выскакивают как NA, а пустые строки опускаются. Чтобы NA превратить в строку используйте str_replace_na():

x <- c("a", NA)
str_c("префикс-", x, "-суффикс")
## [1] "префикс-a-суффикс" NA
str_c("префикс-", str_replace_na(x), "-суффикс")
## [1] "префикс-a-суффикс"  "префикс-NA-суффикс"

Чтобы вектор строк превратить в одну строку воспользуйтесь аргументом collapse:

x <- c("h", "e", "l", "l", "o")
str_c(x)
## [1] "h" "e" "l" "l" "o"
str_c(x, collapse = "_")
## [1] "h_e_l_l_o"

Обратная операция - разделение строк. Для этого можно использовать функции separate() или str_split().

Используйте separate() для разбиения колонки из таблицы на несколько колонок

head(sample) %>% 
  separate(col = author_link, into = c("column_1", "column_2", "author_id"), sep = "/")

Для преобразование строки в список из разделенных строк используйте str_split(). Например, если нам нужно узнать домен почты:

dummy_emails <- c("andan@gmail.com", "icedone@mail.ru", "imdone@un.org")
str_split(dummy_emails, "@")
## [[1]]
## [1] "andan"     "gmail.com"
## 
## [[2]]
## [1] "icedone" "mail.ru"
## 
## [[3]]
## [1] "imdone" "un.org"

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

dummy_books <- c("1974_Carrie_Doubleday", "1975_Salem's_lot_Doubleday", "1977_The_Shining_Doubleday")
#хотим список книг СМтивена Кингана 2 части
str_split_fixed(dummy_books, pattern="_", n=2)
##      [,1]   [,2]                   
## [1,] "1974" "Carrie_Doubleday"     
## [2,] "1975" "Salem's_lot_Doubleday"
## [3,] "1977" "The_Shining_Doubleday"

Но это не всегда хорошо работает (поэтому существуют регулярные выражения):

str_split_fixed(dummy_books, pattern="_", n=3)
##      [,1]   [,2]      [,3]               
## [1,] "1974" "Carrie"  "Doubleday"        
## [2,] "1975" "Salem's" "lot_Doubleday"    
## [3,] "1977" "The"     "Shining_Doubleday"

2.5 Работа с подстрокой

  • Поиск подстроки

Функция str_detect() ищет подстроку в строке и возвращает TRUE или FALSE:

tibble(mn = month.name) %>% 
  mutate(has_r = str_detect(mn, "r"))

А функция str_which() возвращает индекс элемента, в котором есть совпадение с подстрокой, которую ищут:

str_which(month.name, "r")
## [1]  1  2  3  4  9 10 11 12
#фильтр по наличию подстроки
tibble(mn = month.name) %>% 
  slice(str_which(mn, "r"))

Если нужно вернуть не индекс, а сам элемент, в котором есть подстрока, можно также использовать str_subset():

str_subset(month.name, "r")
## [1] "January"   "February"  "March"     "April"     "September" "October"  
## [7] "November"  "December"

str_count() возвращает количество вхождений данной подстроки в строке:

str_count(month.name, "r")
##  [1] 1 2 1 1 0 0 0 0 1 1 1 1

Также в зависимости от задачи, вам могут пригодиться функции str_locate() и str_locate_all()

2.6 Изменение строки

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

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

Все просто, для приведения всех букв к заглавным используется str_to_upper()

str_to_upper(month.name)
##  [1] "JANUARY"   "FEBRUARY"  "MARCH"     "APRIL"     "MAY"       "JUNE"     
##  [7] "JULY"      "AUGUST"    "SEPTEMBER" "OCTOBER"   "NOVEMBER"  "DECEMBER"

Чаще всего все буквы приводят к прописным:

str_to_lower(month.name)
##  [1] "january"   "february"  "march"     "april"     "may"       "june"     
##  [7] "july"      "august"    "september" "october"   "november"  "december"

Можно и так

str_to_title(some_txt)
## [1] "Lorem Ipsum Dolor Sit Amet, Metus Integer Iaculis Tincidunt Ac Orci Convallis. Metus Duis Neque Ligula Natoque Nostra Vel Montes Fringilla. Efficitur Arcu Dolor Vel Et Diam, Tincidunt, Rhoncus, Ad Donec. Rutrum Mauris Ornare Suspendisse Justo, Vehicula Id Diam, Mollis Ut. Velit Et Eu, Mauris Nunc Vestibulum Sapien. Augue Dignissim Vulputate Montes Ipsum Rutrum, Eu, Eu. Porta Faucibus Maximus Ac Fusce Placerat. In Imperdiet Libero Cum Conubia Lacus. Ornare Tempor Vel Nam, Pulvinar, Imperdiet Sit, Vestibulum Ipsum Orci."
  • Выделение подстроки

Выделить подстроку из строки можно, указав индексы (str_sub())…

tibble(mn = month.name) %>% 
  mutate(mutate = str_sub(mn, start = 1, end = 3))
tibble(mn = month.name) %>% 
  mutate(mutate = str_sub(mn, start = -3, end = -1))

… или указав сам паттерн (подстроку) (str_extract())

tibble(mn = month.name) %>% 
  mutate(mutate = str_extract(mn, "e"))

Как видите, в некоторых словах несколько букв e, но функция возвратила лишь одну. Это потому что str_extract() возвращает только первое вхождение. Для того, чтобы вернуть все вхождения подстроки, используйте str_extract_all(). Вам вернется список.

str_extract_all(month.name, "e")
## [[1]]
## character(0)
## 
## [[2]]
## [1] "e"
## 
## [[3]]
## character(0)
## 
## [[4]]
## character(0)
## 
## [[5]]
## character(0)
## 
## [[6]]
## [1] "e"
## 
## [[7]]
## character(0)
## 
## [[8]]
## character(0)
## 
## [[9]]
## [1] "e" "e" "e"
## 
## [[10]]
## [1] "e"
## 
## [[11]]
## [1] "e" "e"
## 
## [[12]]
## [1] "e" "e" "e"
  • замена подстроки

Функции str_replace() и str_replace_all() работают аналогично str_extract(), то есть первая заменчет только первое вхождение подстроки, а вторая заменяет все подстроки

str_replace("September", "e", "E")
## [1] "SEptember"
str_replace_all("September", "e", "E")
## [1] "SEptEmbEr"
  • Удаление подстроки

Аналогично работают str_remove() и str_remove_all()

str_remove("September", "e")
## [1] "Sptember"
str_remove_all("September", "e")
## [1] "Sptmbr"
  • Транслитерация строк

Пакет stringi позволяет производить транслитерацию строк. Методы можно посмотреть с помощью функции stri_trans_list()

x <- "фанфик"

#транслитерация на английский - заметьте, не перевод
stri_trans_general(x, "cyrillic-latin")
## [1] "fanfik"
#с английского на катакану
stri_trans_general("fanfic", "Latin-Katakana")
## [1] "ファンフィク"
#с английского на хирагану
stri_trans_general("fanfic", "Latin-Hiragana")
## [1] "ふぁんふぃく"
#с английского на хангыль
stri_trans_general("fanfic", "Latn-Hang")
## [1] "판핓"

2.7 Задания

Итак

Представьте, что у вас есть оцифрованный текст (тот, что вы уже загрузили в my_file). Вам необходимо произвести его очистку, пока что теми средствами, что вы успели изучить. Этот текст - отрывок из книги Брайана Кокса и Джеффа Форшоу “Что такое E=mc^2 и зачем нам это знать.” Текст на русском, почти не содержит формул(=ими можно пренебречь). в нем есть сноски и заголовки.

my_file
## [1] "ПОМI N1% /;\r\nIII г2’»\r\nпо\tона может принимать разные\r\n„ труднее определить.\t„„ происходило, общее к„.\r\nЭнергию Р> тоЧНо очевиден.\tаТЬСя неизменным не-\r\n,;.г,ои процессе\tэмми №тер дала нам\r\nзависимо от изменения других\tп<)Т0Му, что законы физики\r\nглубокое объяснение. Энергия ^\tутверЖде„ие не означает, что\r\nис изменяются с течением прем\tосто бессмысленно. На самом\r\n„„чего не происходит - это Ьъ спрапедлипы сегодня, деле оно означает, что если уН»\tВы можете заменить ело-\r\nто „ни должны быть 2»™Л;В“ла,, любым другим фундаментальным\r\nвосочетание «уравнения Максвелл\tмер.\r\nзаконом физики-постулатами Эинште\tимпульса, закон со-\r\nВместе с тем, как и в случае закона сохранения . У ^\t„\r\nхранения энергии был открыт экспериментальным )\t•\r\nоткрытия восходит к промышленной революции. Все началось с раоо эксиериментаторов-практиков, которые обнаружили множество с. „„четких и химических явлений в поисках промышленного Иерусалима-К числу таких людей относился и несчастный граф Румфорд Баварский (рожденный под именем Бенджамин Томпсон в Массачусетсе в 1753 году I, работа которого состояла в высверливании каналов в пушечных стволах для армии герцога Баварии. В процессе работы он обратил внимание, что металл пушечного ствола и сверло нагреваются, и справедливо предположил, что вращательное движение сверла превращается в тепло под воздействием трения. Это прямо противоположно тому, что происходит в паровом двигателе, где тепло преобразуется во вращательное движение колес поезда. Казалось вполне естественным связать некую общую величину с теплом и вращательным движением, поскольку, как выяснилось эти две на первый взгляд совершенно разные вещи взаимозаменяемы Эта величина - энергия. Румфорда называли несчастным, потому что\r\nон женился на вдове другого великого ученого, Антуана Л тш,-\r\n’\t7 ,ав>лзье, после\r\nтого как тот во время Французской революции сложил голову н » п\r\nтине. Румфорд ошибочно решил, что эта женщина будет делать дЛ},\r\nто же, что и для Лавуазье, прилежно записывая все результаты СГо *\" °\r\nботы и повинуясь ему, как полагалось хорошей жене в XVIII ст,„1СПи Но оказалось, она проявляла кроткость только под давлением железног\r\nра-\r\nолетиц.\r\nпо\r\n«ЕМ*\r\nЖ*\r\n(. тс*7\r\nII\r\n, л..у>»'- в св°ей ммеЧаТе\"ЬНОЙ книге 1 Ьс Оием Гог АЬзо1ц1с 2его-Мендельсон писал, что эта женщина превратила жизнь графа Ру„.\r\nВ1\r\nКУРТ\r\n„ „ яп Но главное не это, а то, что энергия всегда соуп™ ,\r\nхорда в ад-\t_\tг ^да сохраняется, имен-\r\n* п^тШу она вызывает такой интерес.\r\nяо ПОЭ 7\r\nПопросите кого-либо на улице объяснить, что такое энергия, -й получите либо осмысленный ответ, либо кучу всякого вздора в духе „ью-эйдж” В массовой культуре существует много разных значений слова «энергия», поскольку оно употребляется очень широко. Следует отметить, однако, что на самом деле есть точное определение энергии, которое нельзя использовать для объяснения лей-линий***, исцеления кристаллами, жизни после смерти или реинкарнации. Здравомыслящий человек мог бы сказать, что энергию можно хранить внутри аккумуляторной батареи, где она находится в состоянии ожидания до тех пор, пока кто-то не «замкнет цепь». Кто-то другой, возможно, возразит, что энергия — это показатель количества движения и что быстро движущиеся объекты обладают большей энергией, чем более медленные. Энергия, которую содержит море или ветер, — вот еще примеры определений. Вам могут также сказать, что горячие объекты содержат больше энергии, чем холодные. Гигантский маховик, который находится вн\\ три электростанции, может накапливать энергию, которая высвобожда ется затем в электросеть для удовлетворения потребностей населения в электроэнергии. Кроме того, энергия выделяется в процессе деления .томного ядра. Это только несколько примеров присутствия энергии в повседневной жизни. Во всех этих сяучаях физик,, могут представить\r\nгЬпоме и использовать эту информацию для энергию в количественной фор«\t^\t,ффеИ\r\nподведения\t“менным общее количество энергии,\r\nлюбого процесса сохран\r\n--------------- Мендельсон К. А. Г. На пути к абсолютному нулю: Вве-\r\n* ИЗДаНЗ «'у низких температур. М.: Атомиздат. 1971. пение в физику ”\r\nм\t^\tокупности различных мистических течении и движе-\r\nОбщее название со УгноГО, эзотерического и синкретического характера.\r\nНИЙ. В ОСНОВНОМ окк>\r\nПрим, ред-\t^ места на поверхности Земли, которым свойственно прояв-\r\n... Предположителен _ энеррии>> Прим авт_\r\nление\r\n112\r\nсохранения энергии в действу\r\n. . . .у-П 1\\|« 111\r\nДпя того чтобы увидеть закон сохр,,,.^...\r\nследпии раз вернемся к сталкивающимся билья '\r\n-»\"«й из них обладает определенной\t«ч-*\r\nтог\r\nв ПОС\r\nм\r\n:д.\r\nю№ 11\t«***•«*з........,«* ««у»\t««««^\r\n“;„Г«ое,о да»*'»»\"' Некого языка слоя., «кинетический. ^\r\nили возникающий вследствие яви*..,,\r\nделястся как «обусло\r\n— * и\r\nтермин. Ранее мы исходили из того, что д„а 111а^ ____стью и имеют одинаковую массу. Затем <*,\r\nтак что это правильный\t....\r\nс одинаковом скор0^ -^ ^ ^ с равной скоростью в проп\r\n— - -ал»* 11/ЭПЛ Г* ПТЧП !«!/>.\r\nДВИЖУТСЯ С ОДИН» --- •\r\nсталкиваются и отскакивают И —■ ^ значите/1ЬНОЙ мере продиктова\r\nположных направлениях. .\tтщательный анализ ситуации па\r\n.законом сохранения импульса. Б««™*\tле\r\nзлоллет определить, что скорость движения шар\r\nнемного ...к. их скорости „о столкновения. Это объясняется темли\r\nчаек начальной энергии рассеялась во время столкновения. Наиболее\r\nочевидное рассеяние энергии — переход ее части в звук. Когда бильярд\r\nные шары сталкиваются друг с другом, они воздействуют на молек.\r\nвоздуха, и это возмущение достигает наших ушей. Таким образом, час:\r\nначальной энергии теряется, из-за чего у шаров после столкновения оста\r\nется меньше энергии. С точки зрения темы данной книги нам на само *\r\nделе не нужно знать, как измерить энергию во всех ее проявлениях, хот-\r\nфьрмул. кинетической энергии нал, „се же пригодится немного позже\r\nКаждый, кто изучал л средней школе физику, навсегда запомнил эту фор мулу, кинетическая энергия = в п\tД запомнил ЭТ\\ ^ г\r\nвыразить в количественной ф„р2„е одним чисдТмТ ЧТ° ЭНЧ>\"\"“ лмчество энергии в системе всегда о г .\tТ“КЖе ЧТО ООЩсе\r\nА \"\"Ч\"- вернемся к нашему разговор’\" К'иам*«\"ЫМ.\r\n‘ \" к'1,4 1ве примера величины, котоиа\tЫ \"Паи1 кониепцию импуль\r\nс гиер'ией практическая польз. ймпу„ьёГ“й“*“*\"* “ектоР°М Наряд)\r\n'Управлении движения о *НИз> на юго-ВОСТо 5 СЧетУ вектор им\r\nсклонность разделять \"\"\" \"Рц1ы«У1ЧУЮ глав! \" '\"\"Йом «РУ™ -Ипм-У«ныстрел^.Г:;;е^;Р»пст1........... лр^ «окаэшиГи.\r\n' ““\"“‘■«ы» четыре ЭТО«6лУжде-\r\nНапРпвле„„ях\r\nч\r\n\r\nЖЕ *\r\n\r\nII .4\r\nПГ1\r\nнства-времени, в противном случае мы так и не сможем соста-лГнламеИтальные уравнения с учетом теории Эйнштейна. Позволь-■Г^овторнтв еще раз: фундаментальные уравнения должны включать г* ,я объекты, существующие в пространстве-времени, а не объекты, * ествующие отдельно в пространстве или во времени, поскольку ь^ъекты такого типа носят субъективный характер. Если вы помните, [в*размер объекта в пространстве, ни промежуток времени между двумя мбытиями нельзя отнести к категории величин, со значением которых\r\nсогласятся все без исключения. Именно это мы имеем в виду, утверждая, что такие объекты носят субъективный характер. Импульс также представляет собой вектор, направленный куда-то только в пространстве. Такое предубеждение против времени сеет семена его разрушения. Предве щает ли пространство-время крушение этого самого фундаментального из всех законов физики? Вновь открытая структура пространства-времени действительно сеет семена разрушения, но она указывает нам так же дальнейший путь: нам необходимо найти инвариантную величину, которая сможет занять место устаревшего трехмерного импульса. А вот и ключевой момент нашего повествования: такая величина существует Давайте внимательнее взглянем на трехмерный вектор импульса. На рис. 11 он представлен в виде стрелки, которая может отображать рас стояние, на которое откатывается шар, перемещаясь по столу*. Если опи-\r\nсывать ситуацию точнее, то предположим, что в полдень шар находи гем\r\nУ одного конца этой стрелки, а через две секунды — у другого. Если шар перемещается на сантиметр каждую секунду, тогда длина стрелки равна Двум сантиметрам. Получить вектор импульса не составляет проблем Он представляет собой стрелку, указывающую абсолютно в том же на\r\nправлении, что и на рис. 11, но ее длина другая и равна скорости нашего Шара (в данном случае один сантиметр в секунду), умноженной на его массу, которая составляет, к примеру, десять граммов. Физики сказали бы, что вектор импульса этого шара имеет длину десять грамм сантиметров в секунду (в краткой форме они записали бы это гак: 10 I' • см/с). Здесь снова целесообразно ввести абстрактные символы, вместо юго чтобы\r\n■ В выборе шара нет ничего особенного. Вместо него можно было бы выбрать любой другой объект.\r\n"
  1. Приведите все строки к нижнему регистру.

  2. Теперь надо разобраться с переносами строк, которые есть в печатных книгах, но нет в электронных текстах. В данном тексте можно просто удалить знаки переноса строки -\r\n, но в более объемных и “чистых” текстах для сохранения смысла можно применить следующую методику: Во-первых, надо разделить текст на параграфы по переносу строки -\n и -\r\n. Это можно сделать с помощью str_split(). Далее, можно использовать цикл if: если предыдущая строка оканчивается на “-,” то создай новую строку, склеенную из предыдущей и этой. В противном случае, создай новую строку из предыдущей и этой, разделенную пробелом. Не забудьте обработать крайние значения (первая и последняя строки). Теперь можно склеить текст обратно

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

  4. Вы точно удалили все ненужные символы? Составьте сортированный список всех символов. Это можно сделать, разделив все знаки по пустой строке "". У вас получится список, переделайте его в вектор! Из вектора можно составить таблицу частотности символов, а из нее вытащить список названий строк и отсортировать его.

  5. Удалите лишние пробелы. Вспомните по функцию str_trim(). Попробуйте удалить лишние пробелы внутри текста

Подготовим наши таблицы с фанфиками к обработке.

sample %>% print()
## # A tibble: 1,000 x 9
##    link   title    author_link  author   page fandom subtitle    text      likes
##    <chr>  <chr>    <chr>        <chr>   <dbl> <chr>  <chr>       <chr>     <dbl>
##  1 /read… Сгорая,… /authors/17… Mrs. L…    63 Неукр… Подкидыш    "Осенней…    27
##  2 /read… Объятья… /authors/10… Wales     131 Неукр… <NA>        "На<U+20…   864
##  3 /read… Волосы … /authors/51… Nightm…     1 Неукр… Введение    "Миг зап…    47
##  4 /read… Проверк… /authors/38… Cold-B…    52 Неукр… Семья       "На утро…   260
##  5 /read… Поменяв… /authors/57… Aiky        4 Неукр… Глава 9 CQ… "r / AmI…   282
##  6 /read… Пепел и… /authors/39… Sh.R       79 Неукр… Вместо эпи… "— Так, …   213
##  7 /read… Собстве… /authors/14… Aquama…    86 Неукр… Глава 15. … "Когда В…   587
##  8 /read… И тёмны… /authors/45… Notyou…    96 Неукр… <NA>        "Что ест…    29
##  9 /read… Я буду … /authors/54… Trisha…    20 Неукр… Часть 72    "Сичень …   357
## 10 /read… Мальчик… /authors/19… Lorie      33 Неукр… Гарри Потт… "Планы Д…  3141
## # … with 990 more rows
  1. Посмотрите на столбец authors_link. Нам же не нужен рудимент “/authors/,” оставшийся после скрэппинга? Удалите его.

  2. ID автора гораздо легче использовать, чем его полный никнейм. Но у нас разное количество символов в ID. Нужно привести их к общей схеме, расширив строку до максимальной длины с помощью нулей, которые нужно вставить в начало строки.

  3. Аналогичные манипуляции предлагаю проделать с ID работы из графы “link.” Если возникла ошибка, то проверьте экранирование. Иногда нужно экранировать дважды

Задание со звездочкой. Посмотрите на графу subtitle. Очевидно, у нас скачались отдельные главы фанфиков как отдельные фанфики. Нехорошо. Но! У нас также есть сборники фичков, которые и должны рассматриваться как отдельные фанфики (см. строку 13 в untamed_sample). Чем они отличаются? Наверное, наличием символов “/”, “" или ”|" в графе subtitle. Попробуйте соединить те тексты фичков, у которых совпадает ID и отсутствуют вышеперечисленные символы в подзаголовке. Количество лайков и страниц указывается для всего фанфика, а не для каждой главы, так что их можете не трогать. Присвойте теперь каждому фанфику новый ID.