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

Базовые операции

Задание 1

Переведите в нижний регистр все названия месяцев из вектора month.name.

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

Задание 2

Переведите в верхний регистр все названия месяцев из вектора month.name.

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

Задание 3

Напишите выражение, которое будет возвращать строку "Текущие дата и время: x", где на месте x будет подставляться, собственно, текущие дата и время.

paste('Текущие дата и время:', Sys.time())
## [1] "Текущие дата и время: 2022-02-12 22:57:09"

Задание 4

В векторе month.name, добавьте через _ каждому месяцу его номер, например, January_1.

paste(month.name, 1:12, sep = '_')
##  [1] "January_1"   "February_2"  "March_3"     "April_4"     "May_5"      
##  [6] "June_6"      "July_7"      "August_8"    "September_9" "October_10" 
## [11] "November_11" "December_12"

Задание 5

В векторе month.name, добавьте через _ каждому месяцу его номер и квартал, например, January_1_Q1.

Можно воспользоваться логикой повтора вектора до достижения длины максимально длинного вектора среди всех конкатенируемых. Так, векторы month.name и 1:12 имеют длину 12, соответственно, все остальные векторы в выражении должны быть такой же длины. С элементами _ и _Q нет проблем, они просто будут повторены 12 раз. Сложность может возникнуть с идентификатором квартала - всего кварталов 4, то есть, надо прямо указать, что каждый идентификатор должен быть повторен три раза, чтобы в результате получился вектор длиной в 12 элементов.

paste0(month.name, '_', 1:12, '_Q', rep(1:4, each = 3))
##  [1] "January_1_Q1"   "February_2_Q1"  "March_3_Q1"     "April_4_Q2"    
##  [5] "May_5_Q2"       "June_6_Q2"      "July_7_Q3"      "August_8_Q3"   
##  [9] "September_9_Q3" "October_10_Q4"  "November_11_Q4" "December_12_Q4"

Задание 6

Напишите выражение, которое будет возвращать строку "Доля респондентов с высшим образованием в волне x: y%", где x - любое натуральное число, а у - доля, записанная в виде процентов, округленная до первого знака после запятой. X и Y могут задаваться в отдельных переменных.

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

# решение через paste()
paste('Доля респондентов с высшим образованием в волне ', 5, ': ', round(0.333 * 100, 2), '%', sep = '')
## [1] "Доля респондентов с высшим образованием в волне 5: 33.3%"
# более простое решение через sprintf()
sprintf('Доля респондентов с высшим образованием в волне %d: %.2f%%', 5, 0.333 * 100)
## [1] "Доля респондентов с высшим образованием в волне 5: 33.30%"

Задание 7

Объедините первые пять элементов вектора letters в один, с разделителем в виде -.

paste(letters[1:5], collapse = '-')
## [1] "a-b-c-d-e"

Задание 8

Подсчитайте количество символов в каждом названии месяца из вектора month.name.

nchar(month.name)
##  [1] 7 8 5 5 3 4 4 6 9 7 8 8

Задание 9

Обрежьте каждое название месяца из вектора month.name до трех букв.

strtrim(month.name, 3)
##  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"

Задание 10

Из строки На деревню дедушке, Константину Макаровичу извлеките адрес.

adr <- 'На деревню дедушке, Константину Макаровичу'
strsplit(adr, ',')[[1]][1]
## [1] "На деревню дедушке"

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

Задание 1

Выберите из вектора month.name все месяцы, в названии которых встречается буква m или M.

В функции grep() есть аргумент ignore.case, которые позволяет игнорировать различия в регистре букв паттерна. Так же есть аргумент value, которые задает, что в результате функции возвращаются не номера элементов вектора, по которому идет поиск, а сами значения этого вектора.

grep('m', month.name, ignore.case = TRUE, value = TRUE)
## [1] "March"     "May"       "September" "November"  "December"
# решение без value
month.name[grep('m', month.name, ignore.case = TRUE)]
## [1] "March"     "May"       "September" "November"  "December"

Задание 2

Выберите из вектора month.name все месяцы, в названии которых встречается буква m или M, или буквосочетание ry.

При создани паттерна можно пользоваться логическими операторами, в частности |, или.

grep('m|ry', month.name, ignore.case = TRUE, value = TRUE)
## [1] "January"   "February"  "March"     "May"       "September" "November" 
## [7] "December"

Задание 3

Выберите из вектора month.name все месяцы, в названии которых встречается буква u и после нее n, независимо от регистра.

В регулярных выражениях реализовать оператор и в явном виде сложно и нередко бессмысленно. Поэтому проще воспользоваться символами подстановки и мультипликатором - .* означает `любоё знак.

grep('u.*n', month.name, ignore.case = TRUE, value = TRUE)
## [1] "June"

Задание 4

Выберите из вектора month.name все месяцы, в названии которых встречается буква u и n, независимо от регистра и порядка букв.

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

grep('(u.*n)|(n.*u)', month.name, ignore.case = TRUE, value = TRUE)
## [1] "January" "June"

Задание 5

У вас есть вектор mtcars_names (создается как mtcars_names <- rownames(mtcars)) названий машин из таблицы mtcars. Используя классы символов или POSIX символьные классы, выберите все названия, в которых встречаются цифры.

mtcars_names <- rownames(mtcars)

# решение через классы символов
grep('[0-9]', mtcars_names, value = TRUE)
##  [1] "Mazda RX4"      "Mazda RX4 Wag"  "Datsun 710"     "Hornet 4 Drive"
##  [5] "Duster 360"     "Merc 240D"      "Merc 230"       "Merc 280"      
##  [9] "Merc 280C"      "Merc 450SE"     "Merc 450SL"     "Merc 450SLC"   
## [13] "Fiat 128"       "Camaro Z28"     "Fiat X1-9"      "Porsche 914-2" 
## [17] "Volvo 142E"
# решение через POSIX-классы символов
grep('[[:digit:]]', mtcars_names, value = TRUE)
##  [1] "Mazda RX4"      "Mazda RX4 Wag"  "Datsun 710"     "Hornet 4 Drive"
##  [5] "Duster 360"     "Merc 240D"      "Merc 230"       "Merc 280"      
##  [9] "Merc 280C"      "Merc 450SE"     "Merc 450SL"     "Merc 450SLC"   
## [13] "Fiat 128"       "Camaro Z28"     "Fiat X1-9"      "Porsche 914-2" 
## [17] "Volvo 142E"

Задание 6

Выберите из списка mtcars_names все названия, которые начинаются с M.

grep('^M', mtcars_names, value = TRUE)
##  [1] "Mazda RX4"     "Mazda RX4 Wag" "Merc 240D"     "Merc 230"     
##  [5] "Merc 280"      "Merc 280C"     "Merc 450SE"    "Merc 450SL"   
##  [9] "Merc 450SLC"   "Maserati Bora"

Задание 7

Выберите из списка mtcars_names все названия, которые начинаются с M и заканчиваются на число и букву.

grep('^M.+([0-9][a-z])$', mtcars_names, ignore.case = TRUE, value = TRUE)
## [1] "Merc 240D" "Merc 280C"

Задание 8

Придумайте регулярное выражение, которое поможет выбрать следующие два названия из спиcка mtcars_names: "Fiat X1-9" и "Porsche 914-2".

grep('(-[0-9]*)$', mtcars_names, ignore.case = TRUE, value = TRUE)
## [1] "Fiat X1-9"     "Porsche 914-2"

Задание 9

Придумайте регулярное выражение, которое поможет выбрать название "Valiant" из спиcка mtcars_names.

Valiant - единственное название, в котором нет пробелов. Выделять паттерном все слова, в кторых нет пробелов (т.е., через отрицание) может быть достаточно долго. Поэтому проще выбрать все названия, в которых пробел встречается, и потом на уровне индекса извлечь все прочие элементы вектора, для которых grepl('\\s', mtcars_names) вернет FALSE.

mtcars_names[!grepl('\\s', mtcars_names)]
## [1] "Valiant"

Задание 10

В векторе mtcars_names замените все пробелы на знак _. Перезаписывать вектор не надо.

gsub('\\s+', '_', mtcars_names)
##  [1] "Mazda_RX4"           "Mazda_RX4_Wag"       "Datsun_710"         
##  [4] "Hornet_4_Drive"      "Hornet_Sportabout"   "Valiant"            
##  [7] "Duster_360"          "Merc_240D"           "Merc_230"           
## [10] "Merc_280"            "Merc_280C"           "Merc_450SE"         
## [13] "Merc_450SL"          "Merc_450SLC"         "Cadillac_Fleetwood" 
## [16] "Lincoln_Continental" "Chrysler_Imperial"   "Fiat_128"           
## [19] "Honda_Civic"         "Toyota_Corolla"      "Toyota_Corona"      
## [22] "Dodge_Challenger"    "AMC_Javelin"         "Camaro_Z28"         
## [25] "Pontiac_Firebird"    "Fiat_X1-9"           "Porsche_914-2"      
## [28] "Lotus_Europa"        "Ford_Pantera_L"      "Ferrari_Dino"       
## [31] "Maserati_Bora"       "Volvo_142E"

Задание 11

В векторе mtcars_names с помощью регулярных выражений удалите все символы, кроме первого слова (фактически останется только название производителя). Перезаписывать вектор не надо.

gsub('\\s.+', '', mtcars_names)
##  [1] "Mazda"    "Mazda"    "Datsun"   "Hornet"   "Hornet"   "Valiant" 
##  [7] "Duster"   "Merc"     "Merc"     "Merc"     "Merc"     "Merc"    
## [13] "Merc"     "Merc"     "Cadillac" "Lincoln"  "Chrysler" "Fiat"    
## [19] "Honda"    "Toyota"   "Toyota"   "Dodge"    "AMC"      "Camaro"  
## [25] "Pontiac"  "Fiat"     "Porsche"  "Lotus"    "Ford"     "Ferrari" 
## [31] "Maserati" "Volvo"

Задание 12

Скройте последние четыре цифры в номерах телефонов ('+7-812-999-9999', '+7-3832-66-6666', '+7-499-555-7878') знаками XXXX.

phones <- c('+7-812-999-9999', '+7-3832-66-6666', '+7-499-555-7878')
gsub('[0-9]{4}$', 'XXXX', phones)
## [1] "+7-812-999-XXXX" "+7-3832-66-XXXX" "+7-499-555-XXXX"

Задание 13

У вас есть вектор номеров телефонов в разном формате. Необходимо все привести к одному формату вида +7-(xxx)-xxx-xxxx. С учетом кода страны длина номера составляет ровно 11 цифр, значение фиксировано независимо от формата.

Самым простым решение будет сделать все в два этапа - сначала удалить все символы -, а потом переразбить полученную строку через группы. Так как мы точно знаем, что у нас в каждом номере 11 знаков, мы можем их изначально разбить по группам с квантификаторами, чтобы потом эти группы пересобрать в нужном формате.

phones <- c('+7-916-999-9999', '+7-905-5557878', '+7-923-233-23-23', '+7-962-6666-666', '+79119000000')

phones <- gsub('-', '', phones)
gsub('\\+7([0-9]{3})([0-9]{3})([0-9]{4})', '+7-(\\1)-\\2-\\3', phones)
## [1] "+7-(916)-999-9999" "+7-(905)-555-7878" "+7-(923)-233-2323"
## [4] "+7-(962)-666-6666" "+7-(911)-900-0000"

Задание 14

Удалите из строки meta:chara.*cters лишние знаки.

Так как символы . и * являются мета-символами, управляющими конструкциями, их необходимо экранировать двумя обратными слэшами \\.

gsub(':|\\.|\\*', '', 'meta:chara.*cters')
## [1] "metacharacters"