Rad sa stringovima može se pojaviti u svim fazama jednog Data Science projekta. Nekada će biti potrebno da se očiste neuredni ulazni stringovi pre analize, izvuku promenljive koje se nalaze u tekstu, ili numerički rezultati prenesu u rečenice koje će biti uključene u izveštaj. Nekada će i sami stringovi biti predmet analize (NLP), pa će zadatak biti pronalaženje i analiza šablona u stringovima. Za rad sa znakovnim nizovima koristićemo paket stringr
. Da bi bolje iskoristili njegove mogućnosti radićemo i sa regularnim izrazima, jezikom za opisivanje šablona u stringovima. Za konstrukciju regularnih izraza koristićemo paket rebus
.
Primeri upotrebe ovih paketa u manipulaciji sa stringovima:
Znakovne nizove možete staviti u navodnike ili polunavodnike. Za razliku od drugih jezika, u R-u nema razlike u ponašanju ova dva oblika znakovnih nizova. Preporučujemo da uvek koristite navodnike ("
), osim ako pravite znakovni niz koji sadrži navodnike. Drugačije rečeno:
string1 <- "Ovo je znakovni niz"
string2 <- 'Da biste u znakovni niz dodali "citat", koristite polunavodnike'
Ukoliko zaboravite da zatvorite navodnik, pojaviće se znak +
, tj. znak za nastavljanje koda (engl. continuation character). Ako vam se to dogodi, pritisnite Esc i pokušajte ponovo.
"Ovo je znakovni niz bez završnog navodnika
+
+
UPOMOĆ ZAGLAVIO SAM SE
Da biste u znakovni niz uneli baš polunavodnik ili navodnik (kao doslovni znak – literal), možete koristiti obrnutu kosu crtu (\)
, tj. izlaznu sekvencu (engl. escape sequence):
single_quote = '\'' # ili "'"
double_quote = "\"" # ili '"'
To znači da ako želimo da uključimo samu kosu crtu, moramo pisati:
"\\"
[1] "\\"
Treba da imamo na umu da štampani prikaz stringa nije isti kao sam string, jer štampani prikaz prikazuje i escape karaktere. Da bismo štampali samo sadržaj stringa, koristimo writeLines()
. Takođe funkcija cat
se može koristiti umesto writelines
, s tim što koristi space
kao podrazumevani separator i pokušaće da pretvori ne-znakovne objekte u string.
# Define line1
line1 <- "The table was a large one, but the three were all crowded together at one corner of it:"
# Define line2
line2 <- '"No room! No room!" they cried out when they saw Alice coming.'
# Define line3
line3 <- "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."
print(line1)
[1] "The table was a large one, but the three were all crowded together at one corner of it:"
print(line2)
[1] "\"No room! No room!\" they cried out when they saw Alice coming."
print(line3)
[1] "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."
# Putting lines in a vector
lines <- c(line1, line2, line3)
# Print lines
lines
[1] "The table was a large one, but the three were all crowded together at one corner of it:"
[2] "\"No room! No room!\" they cried out when they saw Alice coming."
[3] "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."
# Use writeLines() on lines
writeLines(lines)
The table was a large one, but the three were all crowded together at one corner of it:
"No room! No room!" they cried out when they saw Alice coming.
"There's plenty of room!" said Alice indignantly, and she sat down in a large arm-chair at one end of the table.
Postoji mnoštvo specijalnih znakova. Najčešći su “”, novi red, i “, tab, a možemo videti kompletnu listu tako što zatražimo pomoć za”: ?‘“’, ili ?”’".
\n newline
\r carriage return
\t tab
\b backspace
\a alert (bell)
\f form feed
\v vertical tab
\\ backslash \
\' ASCII apostrophe '
\" ASCII quotation mark "
\
ASCII grave accent (backtick) ``\nnn character with given octal code (1, 2 or 3 digits)
\xnn character with given hex code (1 or 2 hex digits)
\unnnn Unicode character with given code (1--4 hex digits)
\Unnnnnnnn Unicode character with given code (1--8 hex digits)
Ponekad ćete sretati i znakovne nizove kao što je “\u00b5”
, kojima se predstavljaju znakovi izvan engleskog alfabeta a koji rade na svim platformama.
# Write lines with a space separator
writeLines(lines, sep = " ")
The table was a large one, but the three were all crowded together at one corner of it: "No room! No room!" they cried out when they saw Alice coming. "There's plenty of room!" said Alice indignantly, and she sat down in a large arm-chair at one end of the table.
miznak = "\u00b5"
miznak
[1] "µ"
# Use writeLines() on the string "hello\n\U1F30D"
writeLines("hello\n\U1F30D")
hello
<U+0001F30D>
# Should display: To have a \ you need \\
writeLines("To have a \\ you need \\\\")
To have a \ you need \\
# Should display:
# This is a really
# really really
# long string
writeLines("This is a really \nreally really \nlong string")
This is a really
really really
long string
# Use writeLines() with
# "\u0928\u092e\u0938\u094d\u0924\u0947 \u0926\u0941\u0928\u093f\u092f\u093e"
writeLines("\u0928\u092e\u0938\u094d\u0924\u0947 \u0926\u0941\u0928\u093f\u092f\u093e")
<U+0928><U+092E><U+0938><U+094D><U+0924><U+0947> <U+0926><U+0941><U+0928><U+093F><U+092F><U+093E>
Stringovi se često koriste za izveštavanje o numeričkim rezultatima, recimo kada je reč o valutama. Broj možete uvek pretvoriti u string korišćenjem funkcije as.character
, ali R neće formatirati i zaokružiti rezultat. Umesto toga koristićemo funkcije format()
, koja kontroliše da li se brojevi prikazuju u scientific
formatu, i formatC()
, kojom se kontroliše formatiranje brojeva na sličan način kao u jeziku C
.
Najbitnije stavke za korišćenje format
funkcije za brojeve:
scientific
argument kontroliše da li je format broja naučni ili fixed.digits
argument kontroliše broj cifara pre eksponenta (ako je scientific=TRUE
). Ukoliko je nije, ovaj argument kontroliše broj signifikantnih cifara u najmanjem broju (u reprezentaciji). Ovo znači da broj decimala zavisi od brojeva koje formatirate!format
će podrazumevano padovati string sa razmacima tako da će decimalne tačke brojeva biti vertikalno poravnate ako ih prikazujete u koloni. Sa trim=TRUE
možete ukloniti ove razmake.big.interval
i big.mark
.Argument format
kod funkcije formatC
prima kod koji reprezentuje željeni format, recimo:
Za razliku od funkcije format
, kod formatC
argument digits
argument definiše broj cifara nakon decimalne tačke (kada se koristi format="f"
). formatC
takođe formatira brojeve individualno, što znači da uvek dobijate isti izlaz bez obzira na ostale brojeve u vektoru. Flag
argument vam omogućava da navedete modifikatore, kao recimo +
koji forsira prikaz znaka, -
za levo poravnjanje, ili ‘0’ za padding brojeva sa vodećim nulama (koliko nula je definisano sa argumentom width
).
# Some vectors of numbers
percent_change <- c(4, -1.91, 3.00, -5.002)
income <- c(72.19, 1030.18, 10291.93, 1189192.18)
p_values <- c(0.12, 0.98, 0.0000191, 0.00000000002)
# Format c(0.0011, 0.011, 1) with digits = 1
format(c(0.0011, 0.011, 1), digits = 1)
[1] "0.001" "0.011" "1.000"
# Format c(1.0011, 2.011, 1) with digits = 1
format(c(1.0011, 2.011, 1), digits = 1)
[1] "1" "2" "1"
# Format percent_change to one place after the decimal point
format(percent_change, digits = 2)
[1] " 4.0" "-1.9" " 3.0" "-5.0"
# Format income to whole numbers
format(income, digits = 2)
[1] " 72" " 1030" " 10292" "1189192"
# Format p_values in fixed format
format(p_values, scientific = FALSE)
[1] "0.12000000000" "0.98000000000" "0.00001910000" "0.00000000002"
formatted_income <- format(income, digits = 2)
# Print formatted_income
formatted_income
[1] " 72" " 1030" " 10292" "1189192"
# Call writeLines() on the formatted income
writeLines(formatted_income)
72
1030
10292
1189192
# Define trimmed_income
trimmed_income <- format(income, digits = 2, trim = TRUE)
# Call writeLines() on the trimmed_income
writeLines(trimmed_income)
72
1030
10292
1189192
# Define pretty_income
pretty_income <- format(income, digits = 2, big.mark = ",")
# Call writeLines() on the pretty_income
writeLines(pretty_income)
72
1,030
10,292
1,189,192
x <- c(0.0011, 0.011, 1)
y <- c(1.0011, 2.011, 1)
# formatC() on x with format = "f", digits = 1
formatC(x, format = "f", digits = 1)
[1] "0.0" "0.0" "1.0"
# formatC() on y with format = "f", digits = 1
formatC(y, format = "f", digits = 1)
[1] "1.0" "2.0" "1.0"
# Format percent_change to one place after the decimal point
formatC(percent_change, format = "f", digits = 1)
[1] "4.0" "-1.9" "3.0" "-5.0"
# percent_change with flag = "+"
formatC(percent_change, format = "f", digits = 1, flag = "+")
[1] "+4.0" "-1.9" "+3.0" "-5.0"
# Format p_values using format = "g" and digits = 2
formatC(p_values, format = "g", digits = 2)
[1] "0.12" "0.98" "1.9e-05" "2e-11"
xx <- pi * 10^(-5:4)
formatC(xx)
[1] "3.142e-05" "0.0003142" "0.003142" "0.03142" "0.3142" "3.142"
[7] "31.42" "314.2" "3142" "3.142e+04"
formatC(xx, width = 9, flag = "-")
[1] "3.142e-05" "0.0003142" "0.003142 " "0.03142 " "0.3142 " "3.142 "
[7] "31.42 " "314.2 " "3142 " "3.142e+04"
formatC(xx, digits = 5, width = 8, format = "f", flag = "0")
[1] "00.00003" "00.00031" "00.00314" "00.03142" "00.31416"
[6] "03.14159" "31.41593" "314.15927" "3141.59265" "31415.92654"
formatC(xx, digits = 4, format = "fg")
[1] "0.00003142" "0.0003142" "0.003142" "0.03142" "0.3142"
[6] "3.142" "31.42" "314.2" " 3142" "31416"
formatC(xx, digits = 3, width=9, format = "f", flag = "0+")
[1] "+0000.000" "+0000.000" "+0000.003" "+0000.031" "+0000.314"
[6] "+0003.142" "+0031.416" "+0314.159" "+3141.593" "+31415.927"
Funkcija paste
uzima stringove kao argumente i spaja ih. paste
se često koristi kada je potrebno da od mešavine fiksnih stringova i varijabli napravite komplikovanije izlaze.
Argument sep
je karakter koji će se koristiti da bi se odvojili gradivni delovi (definisani argumentima funkcije paste) svakog od elemenata u finalnom string vektoru. / Argument collapse
je karakter koji će se koristiti da bi se odvojili elementi u finalnom string vektoru.
Svaki vektor koji se prosleđuje funkciji paste
se reciklira
do dužine najdužeg vektora. Recikliranje je ponašanje u R-u koje vektor ponavlja do specifikovane dužine. Stringovi se spajaju element-wise (operacija se izvodi nad svakim elementom vektora).
paste("E", "I", "E", "I", "O")
[1] "E I E I O"
paste("E", "I", "E", "I", "O", sep = "-")
[1] "E-I-E-I-O"
paste(c("Here", "There", "Everywhere"), "a")
[1] "Here a" "There a" "Everywhere a"
animal_goes <- "moo"
paste(c("Here", "There", "Everywhere"), "a", animal_goes)
[1] "Here a moo" "There a moo" "Everywhere a moo"
paste(c("Here", "There", "Everywhere"), "a", animal_goes, collapse = ", ")
[1] "Here a moo, There a moo, Everywhere a moo"
paste(c("Here", "There", "Everywhere"), "a", animal_goes, sep="-", collapse = ",")
[1] "Here-a-moo,There-a-moo,Everywhere-a-moo"
old_mac <- function(animal, animal_goes){
eieio <- paste("E", "I", "E", "I", "O", sep = "-")
old_mac <- "Old MacDonald had a farm"
writeLines(c(
old_mac,
eieio,
paste("And on his farm he had a", animal),
eieio,
paste(c("Here", "There", "Everywhere"), "a",
c(animal_goes, animal_goes,
paste(rep(animal_goes, 2), collapse = "-")),
collapse = ", "),
old_mac,
eieio))
}
old_mac("duck","quack")
Old MacDonald had a farm
E-I-E-I-O
And on his farm he had a duck
E-I-E-I-O
Here a quack, There a quack, Everywhere a quack-quack
Old MacDonald had a farm
E-I-E-I-O
Ključne stavke za funkciju paste
:
sep
argumenta.collapse
koristite samo ukoliko želite da izlaz bude jedan string.sep=''
postoji dodatna funkcija paste0
koja spaja elemente bez separatora.years <- 2010:2013
pretty_income <- format(income, digits = 2, big.mark = ",", trim = TRUE)
pretty_percent <- formatC(percent_change, format = "f", digits = 1, flag = "+")
# Add $ to pretty_income
paste("$", pretty_income, sep = "")
[1] "$72" "$1,030" "$10,292" "$1,189,192"
# Add % to pretty_percent
paste(pretty_percent, "%", sep = "")
[1] "+4.0%" "-1.9%" "+3.0%" "-5.0%"
# Create vector with elements like 2010: +4.0%`
year_percent <- paste(years, ": ", pretty_percent, "%", sep = "")
# Collapse all years into single string
paste(year_percent, collapse = ", ")
[1] "2010: +4.0%, 2011: -1.9%, 2012: +3.0%, 2013: -5.0%"
# Define the names vector
income_names <- c("Year 0", "Year 1", "Year 2", "Project Lifetime")
# Create pretty_income
pretty_income <- format(income, digits = 2, big.mark = ",")
# Create dollar_income
dollar_income <- paste("$", pretty_income, sep = "")
# Create formatted_names
formatted_names <- format(income_names, justify = "right")
# Create rows
rows <- paste(formatted_names, dollar_income, sep = " ")
# Write rows
writeLines(rows)
Year 0 $ 72
Year 1 $ 1,030
Year 2 $ 10,292
Project Lifetime $1,189,192
Ukoliko biste želeli da znak za dolar bude odmah pored brojeva, mogli biste da formatirate prihode sa trim = TRUE
, pejstujete tako formatirane prihode na $ znak, a zatim ponovo formatirate string sa justify = "right"
.
Nasumično biramo tri priloga, a zatim pomoću paste funkcije pravimo narudžbinu za picu.
toppings <- c("anchovies", "artichoke", "bacon", "breakfast bacon", "Canadian bacon",
"cheese", "chicken", "chili peppers", "feta", "garlic", "green peppers",
"grilled onions", "ground beef", "ham", "hot sauce", "meatballs",
"mushrooms", "olives", "onions", "pepperoni", "pineapple", "sausage",
"spinach", "sun-dried tomato", "tomatoes")
# Randomly sample 3 toppings
my_toppings <- sample(toppings, size = 3)
# Print my_toppings
my_toppings
[1] "green peppers" "anchovies" "mushrooms"
# Paste "and " to last element: my_toppings_and
my_toppings_and <- paste(c("", "", "and "), my_toppings, sep = "")
# Collapse with comma space: these_toppings
these_toppings <- paste(my_toppings_and, collapse = ", ")
# Add rest of sentence: my_order
my_order <- paste("I want to order a pizza with ", these_toppings, ".", sep = "")
# Order pizza with writeLines()
writeLines(my_order)
I want to order a pizza with green peppers, anchovies, and mushrooms.
Bazni R-a sadrži mnoge funkcije za rad sa stringovima, ali mi ćemo ih izbeći, jer mnogu biti nekonzistentne što ih čini teškim za pamćenje. Umesto njih, koristićemo funkcije iz stringr
paketa. One imaju intuituvnija imena i sve počinju sa str_.
Na primer, str_length()
nam govori koliko ima karaktera u stringu.
str_length(c("a", "R for data science", NA))
[1] 1 18 NA
Uobičajeni str_
prefiks je naročito koristan ako koristimo RStudio, jer, kao što znamo, kucanje str_
će pokrenuti automatsko dovršavanje, dozvoljavajući nam da vidimo sve stringr
funkcije:
Za kombinovanje dva ili više stringa, koristimo str_c():
str_c("x", "y")
[1] "xy"
str_c("x", "y", sep = ", ")
[1] "x, y"
my_toppings <- sample(toppings, size = 3)
paste(c("", "", "and "), my_toppings, sep = "")
[1] "artichoke" "sun-dried tomato" "and Canadian bacon"
str_c(c("", "", "and "), my_toppings)
[1] "artichoke" "sun-dried tomato" "and Canadian bacon"
# str_c Join multiple strings into a single string
library(stringr)
my_toppings <- c("cheese", NA, NA)
my_toppings_and <- paste(c("", "", "and "), my_toppings, sep = "")
# Print my_toppings_and
my_toppings_and
[1] "cheese" "NA" "and NA"
# Use str_c() instead of paste(): my_toppings_str
my_toppings_str <- str_c(c("", "", "and "), my_toppings)
# Print my_toppings_str
my_toppings_str
[1] "cheese" NA NA
# paste() my_toppings_and with collapse = ", "
paste(my_toppings_and, collapse = ", ")
[1] "cheese, NA, and NA"
# str_c() my_toppings_str with collapse = ", "
str_c(my_toppings_str, collapse = ", ")
[1] NA
Ponašanje iz gornjeg primera je korisno jer vam brzo omogućava otkrivanje nedostajućih vrednosti koje vam kasnije mogu praviti problem. Još jedna stringr funkcija koja je korisna ukoliko postoje nedostajuće vrednosti jeste str_replace_na
koja menja nedostajuće vrednosti (NA) sa bilo kojim stringom po vašem izboru.
U R-u, nedostajuće vrednosti su uglavnom „prenosive“. Ako želite da se prikazuju u obliku “NA”, koristite funkciju gore pomenutu funkciju str_replace_na
:
x <- c("abc", NA)
str_c("|-", x, "-|")
[1] "|-abc-|" NA
str_c("|-", str_replace_na(x), "-|")
[1] "|-abc-|" "|-NA-|"
Funkcija str_length()
uzima kao ulaz vektor stringova i vraća broj znakova u svakom stringu. Vrlo je slična baznoj funkciji nchar
, ali radi sa faktorima na intuitivan način, za razliku od nchar
koja bi vratila grešku!
Koristićemo babynames dataset (samo 2014. godinu). Najpre nas zanima da li su muška imena duža od ženskih.
# str_length The length of a string.
library(stringr)
library(babynames)
library(dplyr)
str_length(c("Srdjan", "Obradovic"))
[1] 6 9
# Extracting vectors for boys' and girls' names
babynames_2014 <- filter(babynames, year == 2014)
boy_names <- filter(babynames_2014, sex == "M")$name
girl_names <- filter(babynames_2014, sex == "F")$name
# Take a look at a few boy_names
head(boy_names)
[1] "Noah" "Liam" "Mason" "Jacob" "William" "Ethan"
# Find the length of all boy_names
boy_length <- str_length(boy_names)
# Take a look at a few lengths
head(boy_length)
[1] 4 4 5 5 7 5
# Find the length of all girl_names
girl_length <- str_length(girl_names)
# Find the difference in mean length
mean(girl_length) - mean(boy_length)
[1] 0.3374758
# Confirm str_length() works with factors
head(str_length(factor(boy_names)))
[1] 4 4 5 5 7 5
# The average length of the girls' names in 2014 is about 1/3 of a character longer.
# Just be aware this is a naive average where each name is counted once,
# not weighted by how many babies received the name.
# A better comparison might be an average weighted by the n column in babynames.
U gornjem primeru prosečna dužina imena devojčica je za oko 1/3 duža od istog proseka za dečake. Imajte u vidu da je ovo naivni prosek gde se svako ime broji samo jednom i ne ponderiše sa brojem beba koje imaju to ime (što bi bilo pametnije).
Delove znakovnog niza možete izdvojiti pomoću funkcije str_sub(). Baš kao i znakovni niz, str_sub() uzima argumente start i end koji definišu položaj znakovnog podniza (engl. substring)
x <- c("Apple", "Banana", "Pear")
str_sub(x, 1, 3)
[1] "App" "Ban" "Pea"
# negative numbers count backwards from end
str_sub(x, -3, -1)
[1] "ple" "ana" "ear"
Imajte u vidu da funkcija str_sub() neće zatajiti ni ako je znakovni niz prekratak; vratiće onoliko znakova koliko je moguće:
str_sub("a", 1, 5)
[1] "a"
Funkciju str_sub() možete koristiti i u obliku namenjenom za dodelu da biste izmenili znakovne nizove:
str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))
x
[1] "apple" "banana" "pear"
Ranije smo koristili funkciju str_to_lower()
da bismo pretvorili tekst u mala slova. Možete koristiti i funkcije str_to_upper()
ili str_to_title()
. Međutim, promena veličine slova komplikovanija je nego što deluje na prvi pogled, zato što su pravila za tu promenu različita u različitim jezicima. Skup pravila koji ćete koristiti možete izabrati tako što ćete zadati lokalne parametre (objekat locale
):
# Turkish has two i's: with and without a dot, and it
# has a different rule for capitalising them:
str_to_upper(c("i", "ı"))
[1] "I" "I"
str_to_upper(c("i", "ı"), locale = "tr")
[1] "I" "I"
# U konzoli se dobija "İ" "İ"
locale
se zadaje kao kôd jezika po standardu ISO 639 – tj. dvoslovna ili troslovna skraćenica. Ako ne znate kôd za svoj jezik, dobru listu naći ćete na Vikipediji (http://bit.ly/ISO639-1). Ukoliko objekat locale
ostavite prazan, koristiće se tekući lokalni parametri – oni koji važe za vaš operativni sistem.
Druga važna operacija na koju utiču lokalni parametri jeste sortiranje. Funkcije order()
i sort()
iz osnovnog R-a sortiraju znakovne nizove koristeći tekuće lokalne parametre. Ako želite da se vaš program ponaša robusno na različitim računarima, možda ćete se odlučiti za funkcije str_sort()
i str_order()
, koje prihvataju dodatni argument locale
:
x <- c("apple", "eggplant", "banana")
str_sort(x, locale = "en") # English
[1] "apple" "banana" "eggplant"
str_sort(x, locale = "haw") # Hawaiian
[1] "apple" "eggplant" "banana"
Pogledajmo sad popularna početna i poslednja slova u imenima dece u babynames datasetu.
# str_sub Extract and replace substrings from a character vector.
str_sub(c("Srdjan", "Obradovic"), 2, 3)
[1] "rd" "br"
#from left to right index counting from right direction
str_sub(c("Srdjan", "Obradovic"), -5, -2)
[1] "rdja" "dovi"
# Extract first letter from boy_names
boy_first_letter <- str_sub(boy_names, 1, 1)
# Tabulate occurrences of boy_first_letter
table(boy_first_letter)
boy_first_letter
A B C D E F G H I J K L M N O P
1454 651 770 998 549 185 334 403 235 1390 1291 537 914 424 207 230
Q R S T U V W X Y Z
56 778 806 771 43 160 174 56 252 379
# Extract the last letter in boy_names, then tabulate
boy_last_letter <- str_sub(boy_names, -1, -1)
table(boy_last_letter)
boy_last_letter
a b c d e f g h i j k l m n o p
421 104 92 436 1148 66 82 583 705 57 349 945 389 4672 730 32
q r s t u v w x y z
19 1011 826 292 81 71 34 86 697 119
# Extract the first letter in girl_names, then tabulate
girl_first_letter <- str_sub(girl_names, 1, 1)
table(girl_first_letter)
girl_first_letter
A B C D E F G H I J K L M N O P
3101 699 946 810 933 209 345 469 373 1430 1694 1122 1746 752 143 303
Q R S T U V W X Y Z
38 831 1369 683 28 214 85 62 294 502
# Extract the last letter in girl_names, then tabulate
girl_last_letter <- str_sub(girl_names, -1, -1)
table(girl_last_letter)
girl_last_letter
a b c d e f g h i j k l m n o p
6632 20 13 81 3114 8 21 1942 1581 12 31 450 115 2608 105 3
q r s t u v w x y z
2 291 326 208 59 6 17 50 1435 51
library(ggplot2)
qplot(girl_last_letter)
“A” je najpopularnije prvo slovo u imenima dečaka i devočica, i najpopularnije poslednje slovo u imenima devojčica. Najpopularnije poslednje slovo u imenima dečaka je “n” .
U poređenju sa substr
funkcijom iz baznog R-a, velika prednost str_sub
funkcije jeste mogućnost korišćenja negativnih indeksa koji broje od desnog kraja stringa.
Ove funkcije imaju isti pattern
argument, ali se razlikuju u vrsti izlaza koje vraćaju:
Da biste ustanovili odgovara li neki string vektor zadatom šablonu, najpre možete koristiti funkciju str_detect
. Ona vraća logički vektor iste dužine kao i ulazni.
x <- c("apple", "banana", "pear")
str_detect(x, "e")
[1] TRUE FALSE TRUE
#> [1] TRUE FALSE TRUE
# str_detect Detect the presence or absence of a pattern in a string.
pizzas <- c("cheese", "pepperoni",
"sausage and green peppers")
# Which orders contain the pattern "pepper"
#returns a logical vector of the same length as the input vector
str_detect(pizzas, pattern = fixed("pepper"))
[1] FALSE TRUE TRUE
#returns only strings that contain the pattern
str_subset(string = pizzas, pattern = fixed("pepper"))
[1] "pepperoni" "sausage and green peppers"
#returns the number of times the pattern occurred in each string
str_count(string = pizzas, pattern = fixed("pepper"))
[1] 0 1 1
Tražimo šablone u datasetu babynames
— imena dečaka čije ime sadrži šablon zz
# str_detect Detect the presence or absence of a pattern in a string.
boy_df <- filter(babynames_2014, sex == "M")
# Look for pattern "zz" in boy_names
contains_zz <- str_detect(boy_names, fixed("zz"))
# Examine str() of contains_zz
str(contains_zz)
logi [1:14047] FALSE FALSE FALSE FALSE FALSE FALSE ...
# How many names contain "zz"?
sum(contains_zz)
[1] 16
# Which names contain "zz"?
boy_names[contains_zz]
[1] "Uzziah" "Ozzie" "Ozzy" "Jazz" "Uzziel" "Chazz"
[7] "Izzy" "Azzam" "Izzac" "Izzak" "Fabrizzio" "Jazziel"
[13] "Azzan" "Izzaiah" "Muizz" "Yazziel"
# Which rows in boy_df have names that contain "zz"?
boy_df[contains_zz, ]
# A tibble: 16 x 5
year sex name n prop
<dbl> <chr> <chr> <int> <dbl>
1 2014 M Uzziah 67 0.0000328
2 2014 M Ozzie 62 0.0000303
3 2014 M Ozzy 57 0.0000279
4 2014 M Jazz 21 0.0000103
5 2014 M Uzziel 21 0.0000103
6 2014 M Chazz 17 0.00000832
7 2014 M Izzy 16 0.00000783
8 2014 M Azzam 14 0.00000685
9 2014 M Izzac 13 0.00000636
10 2014 M Izzak 8 0.00000391
11 2014 M Fabrizzio 7 0.00000342
12 2014 M Jazziel 6 0.00000293
13 2014 M Azzan 5 0.00000245
14 2014 M Izzaiah 5 0.00000245
15 2014 M Muizz 5 0.00000245
16 2014 M Yazziel 5 0.00000245
U gornjem primeru demonstrirana je jedna česta primena str_detect
funkcije—filtriranje redova dataframe-a na osnovu na osnovu zadatog šablona koji bi trebalo da sadrže vrednosti u određenoj koloni.
Da bismo izdvojili samo one stringove koji sadrže šablon, koristimo str_subset()
. Tražimo:
# Find boy_names that contain "zz"
str_subset(boy_names, fixed("zz"))
[1] "Uzziah" "Ozzie" "Ozzy" "Jazz" "Uzziel" "Chazz"
[7] "Izzy" "Azzam" "Izzac" "Izzak" "Fabrizzio" "Jazziel"
[13] "Azzan" "Izzaiah" "Muizz" "Yazziel"
# Find girl_names that contain "zz"
str_subset(girl_names, fixed("zz"))
[1] "Izzabella" "Jazzlyn" "Jazzlynn" "Lizzie" "Izzy"
[6] "Lizzy" "Mazzy" "Izzabelle" "Jazzmine" "Jazzmyn"
[11] "Jazzelle" "Jazzmin" "Izzah" "Jazzalyn" "Jazzmyne"
[16] "Izzabell" "Jazz" "Mazzie" "Alyzza" "Izza"
[21] "Izzie" "Jazzlene" "Lizzeth" "Jazzalynn" "Jazzy"
[26] "Alizzon" "Elizzabeth" "Jazzilyn" "Jazzlynne" "Jizzelle"
[31] "Izzabel" "Izzabellah" "Izzibella" "Jazzabella" "Jazzabelle"
[36] "Jazzel" "Jazzie" "Jazzlin" "Jazzlyne" "Aizza"
[41] "Brizza" "Ezzah" "Fizza" "Izzybella" "Rozzlyn"
# Find girl_names that contain "U"
starts_U <- str_subset(girl_names, fixed("U"))
starts_U
[1] "Unique" "Uma" "Unknown" "Una" "Uriah" "Ursula" "Unity"
[8] "Umaiza" "Urvi" "Ulyana" "Ula" "Udy" "Urwa" "Ulani"
[15] "Umaima" "Umme" "Ugochi" "Ulyssa" "Umika" "Uriyah" "Ubah"
[22] "Umaira" "Umi" "Ume" "Urenna" "Uriel" "Urijah" "Uyen"
# Find girl_names that contain "U" and "z"
str_subset(starts_U, fixed("z"))
[1] "Umaiza"
Pomoću str_count
brojimo koliko se puta šablon pojavljuje u svakom stringu iz string vektora. Radimo sledeće:
# Count occurrences of "a" in girl_names
number_as <- str_count(girl_names, fixed("a"))
# Count occurrences of "A" in girl_names
number_As <- str_count(girl_names, fixed("A"))
# Histograms of number_as and number_As
hist(number_as)
hist(number_As)
# Find total "a" + "A"
total_as <- number_as + number_As
# girl_names with more than 4 a's
girl_names[total_as > 4]
[1] "Aaradhana"
Koristimo str_split()
da izdelimo string na delove. Pošto svaka komponenta može da sadrži različit broj delova, ova funkcija vraća listu, ali takođe može vratiti matricu pomoću argumenta simplify
. Ukoliko želite da ograničite broj delova koji funkcija vraća koristite argument n
. Ukoliko želimo recimo da prebrojimo na koliko delova je podeljen svaki string u ulaznom vektoru to možemo ostvariti recimo sa lapply
funkcijom, ili map
funkcijama iz paketa purrr.
Na primer, možemo izdeliti:
sentences %>%
head(5) %>%
str_split(" ")
[[1]]
[1] "The" "birch" "canoe" "slid" "on" "the" "smooth"
[8] "planks."
[[2]]
[1] "Glue" "the" "sheet" "to" "the"
[6] "dark" "blue" "background."
[[3]]
[1] "It's" "easy" "to" "tell" "the" "depth" "of" "a" "well."
[[4]]
[1] "These" "days" "a" "chicken" "leg" "is" "a"
[8] "rare" "dish."
[[5]]
[1] "Rice" "is" "often" "served" "in" "round" "bowls."
str_split(string = "Tom & Jerry", pattern = " & ")
[[1]]
[1] "Tom" "Jerry"
str_split("Alvin & Simon & Theodore", pattern = " & ")
[[1]]
[1] "Alvin" "Simon" "Theodore"
str_split("Alvin & Simon & Theodore", pattern = " & ", n = 2)
[[1]]
[1] "Alvin" "Simon & Theodore"
chars <- c("Tom & Jerry",
"Alvin & Simon & Theodore")
str_split(chars, pattern = " & ")
[[1]]
[1] "Tom" "Jerry"
[[2]]
[1] "Alvin" "Simon" "Theodore"
chars <- c("Tom & Jerry",
"Alvin & Simon & Theodore")
str_split(chars, pattern = " & ", simplify = TRUE)
[,1] [,2] [,3]
[1,] "Tom" "Jerry" ""
[2,] "Alvin" "Simon" "Theodore"
chars <- c("Tom & Jerry",
"Alvin & Simon & Theodore")
split_chars <- str_split(chars, pattern = " & ")
split_chars
[[1]]
[1] "Tom" "Jerry"
[[2]]
[1] "Alvin" "Simon" "Theodore"
lapply(split_chars, length)
[[1]]
[1] 2
[[2]]
[1] 3
str_split()
se često koristi za ekstrakciju promenljivih iz sirovih podataka, recimo datuma iz datumskih opsega. U drugom primeru zadatak je da se iz vektora imena i prezimena, posebno izvuku vektor imena i vektor prezimena.
# str_split Split up a string into pieces.
# Some date data
date_ranges <- c("23.01.2017 - 29.01.2017", "30.01.2017 - 06.02.2017")
# Split dates using " - "
split_dates <- str_split(date_ranges, fixed(" - "))
split_dates
[[1]]
[1] "23.01.2017" "29.01.2017"
[[2]]
[1] "30.01.2017" "06.02.2017"
# Some date data
date_ranges <- c("23.01.2017 - 29.01.2017", "30.01.2017 - 06.02.2017")
# Split dates with n and simplify specified
split_dates_n <- str_split(date_ranges, fixed(" - "), n = 2, simplify = TRUE)
split_dates_n
[,1] [,2]
[1,] "23.01.2017" "29.01.2017"
[2,] "30.01.2017" "06.02.2017"
start_dates <- split_dates_n[ , 1]
# Split start_dates into day, month and year pieces
str_split(start_dates, fixed("."), n = 3, simplify = TRUE)
[,1] [,2] [,3]
[1,] "23" "01" "2017"
[2,] "30" "01" "2017"
both_names <- c("Box, George", "Cox, David")
# Split both_names into first_names and last_names
both_names_split <- str_split(both_names, fixed(", "), n = 2, simplify = TRUE)
# Get first names
first_names <- both_names_split[, 2]
# Get last names
last_names <- both_names_split[, 1]
# Use the simplify = TRUE
# argument when you want to split each string into the same number of pieces.
Koristite argument simplify = TRUE kada želite da razdvojite svaki string u isti broj delova.
U ovom primeru cilj je da se da se izračuna koliko ima reči u svakoj liniji teksta koji predstavlja prvo poglavlje Alise u zemlji čuda, i prosečna dužina svake linije teksta.
# str_split Split up a string into pieces.
# Split lines into words
words <- str_split(lines, " ")
# Number of words per line
lapply(words, length)
[[1]]
[1] 18
[[2]]
[1] 12
[[3]]
[1] 21
# Number of characters in each word
word_lengths <- lapply(words, str_length)
# Average word length per line
lapply(word_lengths, mean)
[[1]]
[1] 3.888889
[[2]]
[1] 4.25
[[3]]
[1] 4.380952
Izračunate dužine reči nisu ispravne jer nismo uzeli u obzir interpunkcijske znakove. Ovo bimo mogli rešiti njihovom zamenom pomoću funkcije str_replace
, koju ćemo obraditi u nastavku.
str_replace
funkcija menja podudaranja za zadati šablon u ulaznom stringu sa zadatim zamenskim stringom. Ova funkcija menja samo prvo podudaranje sa šablonom. Postoji funkcija str_replace_all
koja menja sva podudaranja. Funkcija takođe radi sa vektorima.
str_replace("Tom & Jerry",
pattern = "&",
replacement = "and")
[1] "Tom and Jerry"
str_replace("Alvin & Simon & Theodore",
pattern = "&",
replacement = "and")
[1] "Alvin and Simon & Theodore"
str_replace_all("Alvin & Simon & Theodore",
pattern = "&",
replacement = "and")
[1] "Alvin and Simon and Theodore"
chars <- c("Tom & Jerry",
"Alvin & Simon & Theodore")
str_replace_all(chars,
pattern = "&",
replacement = "and")
[1] "Tom and Jerry" "Alvin and Simon and Theodore"
U ovoj vežbi menjamo delove stringova koji ne predstavljaju broj i menjamo separator brojeva u brojevima telefona.
ids <- c("ID#: 192", "ID#: 118", "ID#: 001")
# Replace "ID#: " with ""
id_nums <- str_replace(ids, "ID#: ", "")
# Turn id_nums into numbers
id_ints <- as.numeric(id_nums)
# Some (fake) phone numbers
phone_numbers <- c("510-555-0123", "541-555-0167")
# Use str_replace() to replace "-" with " "
str_replace(phone_numbers, fixed("-"), " ")
[1] "510 555-0123" "541 555-0167"
# Use str_replace_all() to replace "-" with " "
str_replace_all(phone_numbers, fixed("-"), " ")
[1] "510 555 0123" "541 555 0167"
# Turn phone numbers into the format xxx.xxx.xxxx
str_replace_all(phone_numbers, fixed("-"), ".")
[1] "510.555.0123" "541.555.0167"
Ukoliko bi vam bilo potrebno da zamenite više znakova mogli biste više puta koristiti funkciju str_replace_all
, međutim postoji bolji način putem regularnih izraza, što ćemo kasnije videti.
Imamo podatke o tri DNK sekvence za tri odgovarajuća gena. Svaki string predstavlja jedan gen, a svaki znak predstavlja određeni nukleotid: Adenin, Citozin, Guanin, ili Timin.
Zadatak:
A
u svakom genuA
"_"#reading R’s custom binary format called RDS
genes <- readRDS(here::here("data-raw","dna.rds"))
# Find the number of nucleotides in each sequence
str_length(genes)
[1] 441 462 993
# Find the number of A's occur in each sequence
str_count(genes, fixed("A"))
[1] 118 117 267
# Return the sequences that contain "TTTTTT"
str_subset(genes, fixed("TTTTTT"))
[1] "TTAAGGAACGATCGTACGCATGATAGGGTTTTGCAGTGATATTAGTGTCTCGGTTGACTGGATCTCATCAATAGTCTGGATTTTGTTGATAAGTACCTGCTGCAATGCATCAATGGATTTACACATCACTTTAATAAATATGCTGTAGTGGCCAGTGGTGTAATAGGCCTCAACCACTTCTTCTAAGCTTTCCAATTTTTTCAAGGCGGAAGGGTAATCTTTGGCACTTTTCAAGATTATGCCAATAAAGCAGCAAACGTCGTAACCCAGTTGTTTTGGGTTAACGTGTACACAAGCTGCGGTAATGATCCCTGCTTGCCGCATCTTTTCTACTCTTACATGAATAGTTCCGGGGCTAACAGCGAGGTTTTTGGCTAATTCAGCATAGGGTGTGCGTGCATTTTCCATTAATGCTTTCAGGATGCTGCGATCGAGATTATCGATCTGATAAATTTCACTCAT"
# Replace all the "A"s in the sequences with a "_"
str_replace_all(genes, fixed("A"), "_")
[1] "TT_G_GT___TT__TCC__TCTTTG_CCC___TCTCTGCTGG_TCCTCTGGT_TTTC_TGTTGG_TG_CGTC__TTTCT__T_TTTC_CCC__CCGTTG_GC_CCTTGTGCG_TC__TTGTTG_TCC_GTTTT_TG_TTGC_CCGC_G___GTGTC_T_TTCTG_GCTGCCT___CC__CCGCCCC___GCGT_CTTGGG_T___TC_GGCTTTTGTTGTTCG_TCTGTTCT__T__TGGCTGC__GTT_TC_GGT_G_TCCCCGGC_CC_TG_GTGG_TGTC_CG_TT__CC_C_GGCC_TTC_GCGT__GTTCGTCC__CTCTGGGCC_TG__GT_TTTCTGT_G____CCC_GCTTCTTCT__TTT_TCCGCT___TGTTC_GC__C_T_TTC_GC_CT_CC__GCGT_CTGCC_CTT_TC__CGTT_TGTC_GCC_T"
[2] "TT__GG__CG_TCGT_CGC_TG_T_GGGTTTTGC_GTG_T_TT_GTGTCTCGGTTG_CTGG_TCTC_TC__T_GTCTGG_TTTTGTTG_T__GT_CCTGCTGC__TGC_TC__TGG_TTT_C_C_TC_CTTT__T___T_TGCTGT_GTGGCC_GTGGTGT__T_GGCCTC__CC_CTTCTTCT__GCTTTCC__TTTTTTC__GGCGG__GGGT__TCTTTGGC_CTTTTC__G_TT_TGCC__T___GC_GC___CGTCGT__CCC_GTTGTTTTGGGTT__CGTGT_C_C__GCTGCGGT__TG_TCCCTGCTTGCCGC_TCTTTTCT_CTCTT_C_TG__T_GTTCCGGGGCT__C_GCG_GGTTTTTGGCT__TTC_GC_T_GGGTGTGCGTGC_TTTTCC_TT__TGCTTTC_GG_TGCTGCG_TCG_G_TT_TCG_TCTG_T___TTTC_CTC_T"
[3] "_TG______C__TTT_TCC_____C__C__C___TC_GCTTCGT____TC_TTCTTTTCCCGCC__TT_G_GC__C__CTTGGCTTG_TCG__GTCC_GGCTCCT_TTTTG_GCCGTGTGGGTG_TGG__CCC__G_T__CCTTTCTGGTTCTG_G___GCGGT_C_GGT____GTT__GTC_TTGCCGG_TTC__CTTTTG__GTTGT_C_TTC_TT_GCG__GTGG___CGT____CCTT_GGGCGTTTTG_TTTTGGTGCTG_CC__GGGGTGT_T_CCC_T_TG___GC_TTGCGCCC_G_TG__G_TCGCCTG_GTGCT_TTC_TTCTGT_T_TGT_G_TC_GTGGG_TTGGG__CGGGTT_TGGGGG_CGGTG__CGT__CCTGGCTT_CCTG___TCG_CTGTT__C__G_TTT_TGC_GCG_TT___G___CTG__GCGGCG_TC_GTGCTG_GTTTGGTGTG__GCCTTTCCTGCCGG_TC_T_TTC_GTTT_TCC_C_GTG___GCCTGCGGGCC_G_TTCCCTG_TTT_G_TGCT___GGCCGTG__CGTGC__TTGCC___G_GTT_GGTGCTGTCTTCCTT_T_GGG_TTGGTGGC___TTGGC_G_TGGTC__TCCC_TG_TGTTCGTGCGCC_G_TT_TG_TG_TTGG_CCTCTCCG_GTGCGG__GGTTTCTCTGG_TT___CGGCG_C_TT_TTGTCTGG__CCC__T_TTGG__G_TGCCTTTG_G_T_TCTTCT_TGGG__TTCGTGTTG_TGCCG__GCTCTT__GCGTC_GTT_GCCCTG_CTGGCG_TG__G_CCGCTTGG__CTGG__TGGC_TC__TC_CTGTTGCGCGGTG___TGCC_C___CT_TCGGGGG_GGT_TTGGTC_GTCCCGCTT_GTG_TGTT_TTGCTGC_G___C__C_T_TTGGTC_GGTGC__TGTGGTGTTTGGGGCCCTG___TC_GCG_G___GTTG_TGGCCTGCTGT__"
U sledećem zadatku potrebno je sva imena i prezimena u vektoru npr. “Bruce Wayne” pretvoriti u skraćenu varijantu “B. Wayne”.
U drugom zadatku potrebno je uporediti koliko dečaka i devojčica iz dataseta babynames
ima imena koja se završavaju sa “ee”.
# Define some full names
names <- c("Diana Prince", "Clark Kent")
# Split into first and last names
names_split <- str_split(names, fixed(" "), simplify = TRUE)
# Extract the first letter in the first name
abb_first <- str_sub(names_split[, 1], 1, 1)
# Combine the first letter ". " and last name
str_c(abb_first, ". ", names_split[, 2])
[1] "D. Prince" "C. Kent"
# Use all names in babynames_2014
all_names <- babynames_2014$name
# Get the last two letters of all_names
last_two_letters <- str_sub(all_names, -2, -1)
# Does the name end in "ee"?
ends_in_ee <- str_detect(last_two_letters, "ee")
# Extract rows and "sex" column
sex <- babynames_2014$sex[ends_in_ee]
# Display result as a table
table(sex)
sex
F M
572 84
Paket rebus
pruža lakši način rada sa regularnim izrazima, odnosno konstrukcije regex
objekata u R-u.
Specijalni znak %R%
koji pruža rebus omogućava konstrukciju komplikovanih regularnih izraza od jednostavnijih delova. Kada čitate rebus kod, znak %R%
možete protumačiti kao prilog onda/nakon/zatim.
Rebus pruža START
i END
prečice da bi se specifikovali regularni izrazi koji pronalaze podudarnost sa krajem ili početkom stringa. Ove prečice su takođe poznate pod imenom sidra (eng. anchors).
Pozivanjem rebus izraza u konzoli dobićete odgovarajući regularni izraz. Izraz ima prefiks <regex>
.
library(rebus)
library(stringr)
START
<regex> ^
START %R% "c"
<regex> ^c
START %R%
ANY_CHAR %R%
one_or_more(DGT)
<regex> ^.[\d]+
str_detect(c("R2-D2", "C-3P0"),
pattern = START %R%
ANY_CHAR %R%
one_or_more(DGT))
[1] TRUE FALSE
str_detect(c("R2-D2", "C-3P0"), pattern = "^.\\d+")
[1] TRUE FALSE
# Some strings to practice with
x <- c("cat", "coat", "scotland", "tic toc")
# Print END
END
<regex> $
# str_view - View HTML rendering of regular expression match.
str_view(x, pattern = START %R% "c")
# Match the strings that start with "co"
str_view(x, pattern = START %R% "co")
# Match the strings that end with "at"
str_view(x, pattern = "at" %R% END)
# Match the string that is exactly "cat"
str_view(x, pattern = START %R% "cat" %R% END)
str_view(x, pattern = exactly("cat"))
U regularnim izrazima možete koristiti wildcard znak kada vam treba podudarnost sa bilo kojim pojedinačnim znakom. U rebusu wildcard se specifikuje sa ANY_CHAR
.
##################################
"c" %R% ANY_CHAR %R% "t"
<regex> c.t
# will look for patterns like "c_t" where the blank can be any character
str_view(c("cat", "coat", "scotland", "tic toc"),
pattern = "c" %R% ANY_CHAR %R% "t")
x <- c("cat", "coat", "scotland", "tic toc")
# Match any character followed by a "t"
str_view(x, pattern = ANY_CHAR %R% "t")
# Match two characters, where the second is a "t"
str_view(x, pattern = ANY_CHAR %R% "t")
# Match a "t" followed by any character
str_view(x, pattern = "t" %R% ANY_CHAR)
# Match two characters
str_view(x, pattern = ANY_CHAR %R% ANY_CHAR)
# Match a string with exactly three characters
str_view(x, pattern = START %R% ANY_CHAR %R% ANY_CHAR %R% ANY_CHAR %R% END)
Regularni izrazi mogu se koristiti kao pattern
argument u svim stringr
funkcijama. Dakle, da ponovimo:
str_detect
koristimo da bismo dobili logički vektor koji indikuje pozicije u vektoru stringova gde postoji podudarnost sa šablonom.str_subset
koristimo da bismo dobili samo one stringove iz vektora stringova u kojima postoji podudarnost sa zadatim šablonom.str_count
koristimo da bismo prebrojali broj podudarnosti u vektorima gde postoji podudarnost.U repertoar stringr
funkcija sada ćemo dodati i str_extract
funkciju. Ova funkcija vraća deo stringa koji se podudara sa zadatim šablonom.
pattern <- "q" %R% ANY_CHAR
# Find names that have the pattern
names_with_q <- str_subset(boy_names, pattern)
# How many names were there?
length(names_with_q)
[1] 96
# Find part of name that matches pattern
part_with_q <- str_extract(boy_names, pattern)
# Get a table of counts
table(part_with_q)
part_with_q
qa qe qi qm qo qu
1 1 2 2 1 89
# Did any names have the pattern more than once?
count_of_q <- str_count(boy_names, pattern)
# Get a table of counts
table(count_of_q)
count_of_q
0 1
13951 96
# Which babies got these names?
with_q <- str_detect(boy_names, pattern)
# What fraction of babies got these names?
mean(with_q)
[1] 0.006834199
Pre nego što nastavimo napravićemo komični odušak navođenjem jednog citata Džejmija Zavinskog (https://en.wikipedia.org/wiki/Jamie_Zawinski):
Kada se suoče sa problemom, neki ljudi ljudi pomisle: „Znam upotrebiću regularne izraze.” Onda imaju dva problema.
Kao upozorenje, navodimo primer regularnog izraza koji proverava ispravnost jedne e-mail adrese:
(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
\t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\]
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\]
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
\t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)
Ukoliko u regularnom izrazu želite da specifikujete “nađi podudarnost sa šablonom A ili šablonom B” koristite alternaciju. U rebusu u tu svrhu se koristi funkcija or
.
greys_ex <- c("grey sky", "gray elephant")
str_view(greys_ex, pattern = or("grey", "gray"))
# Since these two words only differ by one character you could equivalently specify this match with
# "gr" %R% or("e", "a") %R% "y",
# that is “a gr followed by, an e or an a, then a y”.
# Match Jeffrey or Geoffrey
whole_names <- or("Jeffrey", "Geoffrey")
str_view(boy_names, pattern = whole_names, match = TRUE)
# Match Jeffrey or Geoffrey, another way
common_ending <- or("Je", "Geo") %R% "ffrey"
str_view(boy_names, pattern = common_ending, match = TRUE)
# Match with alternate endings
by_parts <- or("Je", "Geo") %R% "ff" %R% or("ry", "ery", "rey", "erey")
str_view(boy_names, pattern = by_parts, match = TRUE)
# Match names that start with Cath or Kath
ckath <- or("C", "K") %R% "ath"
str_view(girl_names, pattern = ckath, match = TRUE)
Znakovne klase podsećaju na ANY_CHAR
, s tim što su dozvoljeni samo znakovi koji pripadaju određenoj znakovnoj klasi. U rebusu se za definisanje znakovnih klasa koristi funkcija char_class
kojoj se prosleđuje niz znakova za koje želimo da tražimo podudarnost. Prepoznaje se tačno jedan od znakova iz klase.
Negirana znakovna klasa specifikuje da želimo podudarnost sa bilo kojim znakom koji se ne nalazi u negiranoj znakovnoj klasi. Za definisanje negirane znakovne klase u rebusu koristimo funkciju negated_char_class
. Prepoznaje se tačno jedan znak koji se ne nalazi u klasi.
U okviru znakovnih klasa ne morate da eskejpujete znakove koji bi u regularnim izrazima imali specijalno značenje. Na primer, . tačku možete direktno navesti. Minus - međutim, mora doći prvi u znakovnoj klasi!
# In regular expressions a character class is a way of specifying
# “match one (and only one) of the following characters”. In rebus
# you can specify the set of allowable characters using the function char_class().
# This is another way you could specify an alternate spelling,
# for example, specifying “a gr followed by, either an a or e, followed by a y”
x <- c("grey sky", "gray elephant")
str_view(x, pattern = "gr" %R% char_class("ae") %R% "y")
# Create character class containing vowels
vowels <- char_class("aeiouAEIOU")
# Print vowels
vowels
<regex> [aeiouAEIOU]
# See vowels in x with str_view()
str_view(x, pattern = vowels)
# See vowels in x with str_view_all()
str_view_all(x, pattern = vowels)
# Number of vowels in boy_names
num_vowels <- str_count(boy_names, pattern = vowels)
# Number of characters in boy_names
name_length <- str_length(boy_names)
# Calc mean number of vowels
mean(num_vowels)
[1] 2.385563
# Calc mean fraction of vowels per name
mean(num_vowels / name_length)
[1] 0.4000596
Postoji nekoliko načina za specifikaciju šablona koji se ponavlja.
x <- c("ow", "ooh", "yeeeah!", "shh")
str_view(x, pattern = one_or_more(vowels))
str_view(x, pattern = zero_or_more(vowels))
# Vowels from last exercise
vowels <- char_class("aeiouAEIOU")
# See names with only vowels
str_view(boy_names,
pattern = exactly(one_or_more(vowels)),
match = TRUE)
# Use `negated_char_class()` for everything but vowels
not_vowels <- negated_char_class("aeiouAEIOU")
# See names with no vowels
str_view(boy_names,
pattern = exactly(one_or_more(not_vowels)),
match = TRUE)
Recimo da želite da specifikujete patern dolar znak zatim cifra. Mogli biste da specifikujete dolar znak koji prati znakovna klasa koja uključuje cifre 0-9. Pošto je ovo čest problem, regularni izrazi pružaju nekoliko prečica. Najpre možete specifikovati opseg u okviru znakovne klase.
DGT
konstanta u rebusu
, ili backslash “d
” u regularnom izrazu specifikuje bilo koju cifru.
WRD
u rebusu
, ili backslash “w
” u regularnom izrazu specifikuje bilo koji znak iz reči (bilo koje malo ili veliko slovo, cifra, ili donja crta).
SPC
u rebusu, ili backslash “s
” u regularnom izrazu specifikuje bilo koju pojedinačnu belinu.
contact <- c("Call me at 555-555-0191", "123 Main St",
"(555) 555 0191", "Phone: 555.555.0191 Mobile: 555.555.0192")
# Take a look at ALL digits
str_view_all(contact, pattern = DGT)
# Create a three digit pattern
three_digits <- DGT %R% DGT %R% DGT
# Test it
str_view_all(contact, pattern = three_digits)
# Create four digit pattern
four_digits <- three_digits %R% DGT
# Create a separator pattern
separator <- char_class("-.() ")
# Test it
str_view_all(contact, pattern = separator)
# Use these components
three_digits <- DGT %R% DGT %R% DGT
four_digits <- three_digits %R% DGT
separator <- char_class("-.() ")
# Create phone pattern
phone_pattern <- optional(OPEN_PAREN) %R%
three_digits %R%
zero_or_more(separator) %R%
three_digits %R%
zero_or_more(separator) %R%
four_digits
# Test it
str_view_all(contact, pattern = phone_pattern)
# Use this pattern
three_digits <- DGT %R% DGT %R% DGT
four_digits <- three_digits %R% DGT
separator <- char_class("-.() ")
phone_pattern <- optional(OPEN_PAREN) %R%
three_digits %R%
zero_or_more(separator) %R%
three_digits %R%
zero_or_more(separator) %R%
four_digits
# Extract phone numbers
str_extract(contact, phone_pattern)
[1] "555-555-0191" NA "(555) 555 0191" "555.555.0191"
# Extract ALL phone numbers
str_extract_all(contact, phone_pattern)
[[1]]
[1] "555-555-0191"
[[2]]
character(0)
[[3]]
[1] "(555) 555 0191"
[[4]]
[1] "555.555.0191" "555.555.0192"
Što se tiče prečica rebus takođe pruža funkcije number_range
kada želite ekstrakciju brojeva u određenom opsegu, i datetime
da bi se specifikovali česti šabloni u radu sa datumima.
Sledeći zadatak je ekstrakcija godina i pola iz narativnih opisa dobijenih iz sledećeg skupa podataka.
narratives <- readRDS(here::here("data-raw","narratives.rds"))
# Pattern to match one or two digits
age <- DGT %R% optional(DGT)
# Test it
str_view(narratives, pattern = age)
# Pattern to match units
unit <- optional(SPC) %R% or("YO", "YR", "MO")
# Test pattern with age then units
str_view(narratives, pattern = age %R% unit)
# Pattern to match gender
gender <- optional(SPC) %R% or("M", "F")
# Test pattern with age then units then gender
str_view(narratives, pattern = age %R% unit %R% gender)
# Extract age, unit, gender
str_extract(narratives, age %R% unit %R% gender)
[1] "19YOM" "31 YOF" "82 YOM" "33 YOF" "10YOM" "53 YO F" "13 MOF"
[8] "14YR M" "55YOM" "5 YOM"
# dgt(1, 2) matches one or two digits
Sada ćemo godine i pol parsirati u delove.
age <- dgt(1, 2)
# Extract age_gender, take a look
age_gender <- str_extract(narratives, pattern = age %R% unit %R% gender)
age_gender
[1] "19YOM" "31 YOF" "82 YOM" "33 YOF" "10YOM" "53 YO F" "13 MOF"
[8] "14YR M" "55YOM" "5 YOM"
# age_gender, age, gender, unit are pre-defined
# ls.str()
# Extract age and make numeric
as.numeric(str_extract(age_gender, age))
[1] 19 31 82 33 10 53 13 14 55 5
# Replace age and units with ""
genders <- str_remove(age_gender, pattern = age %R% unit)
# Replace extra spaces
str_remove_all(genders, pattern = one_or_more(SPC))
[1] "M" "F" "M" "F" "M" "F" "F" "M" "M" "M"
# Numeric ages, from previous step
ages_numeric <- as.numeric(str_extract(age_gender, age))
# Extract units
time_units <- str_extract(age_gender, unit)
# Extract first word character
time_units_clean <- str_extract(time_units, WRD)
# Turn ages in months to years
ifelse(time_units_clean == "Y", ages_numeric, ages_numeric / 12)
[1] 19.000000 31.000000 82.000000 33.000000 10.000000 53.000000 1.083333
[8] 14.000000 55.000000 5.000000
Obradićemo dve napredne metode korišćenja regularnih izraza sa stringr
—selektovanje delova podudaranja (hvatanje, eng. capturing), i referenciranje na prethodno uhvaćen tekst (referenciranje unazad, eng. back-referencing).
Capturing je jednostavno način da se zajedno grupišu delovi šablona. U rebusu, treba omotati deo šablona koji želite da uhvatite sa funkcijom capture
. U regularnom izrazu, capturing specifikujete zatvaranjem dela šablona koji želite da uhvatite u zagrade.
Capturing ne menja šablon za koji se traži podudarnost, već jednostavno označava da želite nešto da uradite sa delom šablona.
string
funkcija str_match
je dizajnirana da radi sa šablonima koji sadrže capture-e.
str_match
vraća matricu, gde svaki red odgovara ulaznom stringu.
Prva kolona predstavlja kompletno podudaranje, koje biste dobili sa str_extract()
. Zatim, dobijate kolonu koju sadrži svaku uhvaćenu grupu. Drugim rečima str_extract
daje nam potpunu podudarnost, dok str_match
daje i svaku pojedinačnu komponentu.
Ovo je korisno kada ste sastavili komplikovan šablon i želite da koristite/pristupite njegovim različitim delovima.
Pogledajmo sledeći šablon:
znak za dolar, nakon koga sledi cifra, zatim opciona cifra, zatim tačka, i zatim dve cifre
.
Ovaj šablon traži iznose u dolarima ispod 100 dolara.
pattern <- DOLLAR %R%
DGT %R% optional(DGT) %R%
DOT %R%
dgt(2)
str_view(c("$5.50", "$32.00"), pattern = pattern)
Mogli biste recimo da odvojite dolare od centi. To radite tako što hvatate deo koji hvata dolare, i deo koji hvata cente. Dobijate dolare u jednoj koloni, i cente u drugoj koloni. Hvatanja se broje sa leva desno.
pattern <- DOLLAR %R%
capture(DGT %R% optional(DGT)) %R%
DOT %R%
capture(dgt(2))
str_match(c("$5.50", "$32.00"), pattern = pattern)
[,1] [,2] [,3]
[1,] "$5.50" "5" "50"
[2,] "$32.00" "32" "00"
U donjem primeru, or
funkcija nam je dala regularni izraz gde u zagradi imamo ?:
. Ovo ukazuje da se ovde radi o non-capturing grupi, tj. da ne treba čuvati reference (capture-e).
kontakti <- c("(peraperic@gmail.com)", "anagajic@outlook.org", "miodragmarkicevic@yahoo.com")
# Capture parts between @ and . and after .
email <- capture(one_or_more(WRD)) %R%
"@" %R% capture(one_or_more(WRD)) %R%
DOT %R% capture(one_or_more(WRD))
# Check match hasn't changed
str_view(kontakti, email)
# If you want to capture the part before the @, you simply wrap that part of the regular expression in capture():
email <- capture(one_or_more(WRD)) %R%
"@" %R% one_or_more(WRD) %R%
DOT %R% one_or_more(WRD)
str_view("(anagajic@outlook.org)", pattern = email)
# The part of the string that matches hasn't changed, but if
# we pull out the match with str_match() we get access to the captured piece:
str_match("(anagajic@outlook.org)", pattern = email)
[,1] [,2]
[1,] "anagajic@outlook.org" "anagajic"
# Pattern from previous step
email <- capture(one_or_more(WRD)) %R%
"@" %R% capture(one_or_more(WRD)) %R%
DOT %R% capture(one_or_more(WRD))
# Check match hasn't changed
str_view(kontakti, email)
# Pull out match and captures
email_parts <- str_match(kontakti, email)
email_parts
[,1] [,2] [,3] [,4]
[1,] "peraperic@gmail.com" "peraperic" "gmail" "com"
[2,] "anagajic@outlook.org" "anagajic" "outlook" "org"
[3,] "miodragmarkicevic@yahoo.com" "miodragmarkicevic" "yahoo" "com"
# Save host
host <- email_parts[, 3]
host
[1] "gmail" "outlook" "yahoo"
Ekstrakcija delova iz broja telefona.
# View text containing phone numbers
contact
[1] "Call me at 555-555-0191"
[2] "123 Main St"
[3] "(555) 555 0191"
[4] "Phone: 555.555.0191 Mobile: 555.555.0192"
# Add capture() to get digit parts
phone_pattern <- capture(three_digits) %R% zero_or_more(separator) %R%
capture(three_digits) %R% zero_or_more(separator) %R%
capture(four_digits)
# Pull out the parts with str_match()
phone_numbers <- str_match(contact, phone_pattern)
# Put them back together
str_c(
"(",
phone_numbers[, 2],
") ",
phone_numbers[, 3],
"-",
phone_numbers[, 4])
[1] "(555) 555-0191" NA "(555) 555-0191" "(555) 555-0191"
Ako želite izdvojiti više od prvog telefonskog broja, npr. drugi telefonski broj u zadnjem stringu, možete koristiti str_match_all
. Međutim, poput str_split
, ova funkcija će vratiti listu sa jednom komponentom za svaki ulazni string, tako da se za obradu rezultata mora koristiti lapply.
Ekstrakcija godina i pola (ponovo).
# narratives has been pre-defined
narratives
[1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS "
[2] "31 YOF FELL FROM TOILET HITITNG HEAD SUSTAINING A CHI "
[3] "ANKLE STR. 82 YOM STRAINED ANKLE GETTING OUT OF BED "
[4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"
[5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "
[6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "
[7] "13 MOF TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
[8] "14YR M PLAYING FOOTBALL; DX KNEE SPRAIN "
[9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "
[10] "5 YOM ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"
# Add capture() to get age, unit and sex
pattern <- capture(optional(DGT) %R% DGT) %R%
optional(SPC) %R% capture(or("YO", "YR", "MO")) %R%
optional(SPC) %R% capture(or("M", "F"))
# Pull out from narratives
str_match(narratives, pattern)
[,1] [,2] [,3] [,4]
[1,] "19YOM" "19" "YO" "M"
[2,] "31 YOF" "31" "YO" "F"
[3,] "82 YOM" "82" "YO" "M"
[4,] "33 YOF" "33" "YO" "F"
[5,] "10YOM" "10" "YO" "M"
[6,] "53 YO F" "53" "YO" "F"
[7,] "13 MOF" "13" "MO" "F"
[8,] "14YR M" "14" "YR" "M"
[9,] "55YOM" "55" "YO" "M"
[10,] "5 YOM" "5" "YO" "M"
# Edit to capture just Y and M in units
pattern2 <- capture(optional(DGT) %R% DGT) %R%
optional(SPC) %R% capture(or("Y", "M")) %R% optional(or("O","R")) %R%
optional(SPC) %R% capture(or("M", "F"))
# Check pattern
str_view(narratives, pattern2)
# Pull out pieces
str_match(narratives, pattern2)
[,1] [,2] [,3] [,4]
[1,] "19YOM" "19" "Y" "M"
[2,] "31 YOF" "31" "Y" "F"
[3,] "82 YOM" "82" "Y" "M"
[4,] "33 YOF" "33" "Y" "F"
[5,] "10YOM" "10" "Y" "M"
[6,] "53 YO F" "53" "Y" "F"
[7,] "13 MOF" "13" "M" "F"
[8,] "14YR M" "14" "Y" "M"
[9,] "55YOM" "55" "Y" "M"
[10,] "5 YOM" "5" "Y" "M"
Pozivanje na uhvaćeni deo šablona poznato je kao backreference (povratna referenca). Pošto može biti više od jednog capture-a, regularni izrazi koriste prebrojavanje sa leva na desno da bi se pozvali na različite capture-e/reference.
Prvi capture može da se referencira sa REF1
, drugi sa REF2
, …, sve do REF9
.
Povratne reference mogu se koristiti direktno u šablonu, kako bi se omogućili ponovljeni šabloni.
Ovaj šablon traži reč tražeći razmak, a zatim jedan ili više znakova iz reči, a zatim drugi razmak. Ako uhvatimo deo koji predstavlja jedan ili više znakova iz reči, i na njega se kasnije pozovemo, šablon sada hvata ponovljene reči.
sablon1 <- SPC %R%
one_or_more(WRD) %R%
SPC
str_view("Paris in the the spring",
sablon1)
sablon2 <- SPC %R%
capture(one_or_more(WRD)) %R%
SPC %R%
REF1
str_view("Paris in the the spring",
sablon2)
Povratne reference se takođe mogu koristiti u replacement
argumentu funkcije str_replace
. replacement
argument mora biti string, pa ćemo koristiti funkciju str_c
da bismo spojili povratnu referencu i fiksne znakove.
str_replace("Paris in the the spring",
pattern = SPC %R%
capture(one_or_more(WRD)) %R%
SPC %R%
REF1,
replacement = str_c(" ", REF1))
[1] "Paris in the spring"
Sledeći šablon hvata ponovljena mala slova.
capture(LOWER) %R% REF1
<regex> ([:lower:])\1
str_view(c("hello", "sweet", "kitten","l33t"),
pattern = capture(LOWER) %R% REF1)
#using boy_names again... but lowercased
boy_names <- tolower(boy_names)
# Names with three repeated letters
repeated_three_times <- capture(LOWER) %R% REF1 %R% REF1
# Test it
str_view(boy_names, pattern = repeated_three_times, match = TRUE)
# Names with a pair of repeated letters
pair_of_repeated <- capture(LOWER %R% LOWER) %R% REF1
# Test it
str_view(boy_names, pattern = pair_of_repeated, match = TRUE)
# Names with a pair that reverses
pair_that_reverses <- capture(LOWER) %R% capture(LOWER) %R% REF2 %R% REF1
# Test it
str_view(boy_names, pattern = pair_that_reverses, match = TRUE)
# Four letter palindrome names
four_letter_palindrome <- exactly(
capture(LOWER) %R% capture(LOWER) %R% REF2 %R% REF1
)
# Test it
str_view(boy_names, pattern = four_letter_palindrome, match = TRUE)
Što se tiče zamene sa regularnim izrazima, koristićemo funkciju str_replace
koja prima sledeće argumente:
string
, vektor stringova u kojima treba izvršiti zamenupattern
, koji identifikuje delove stringova koje treba zamenitireplacement
, može biti vektor iste dužine kao argument string
, gde svaki element specifikuje zamenu koja će se koristiti za svaki od elemenata iz string
argumenta.# View text containing phone numbers
contact
[1] "Call me at 555-555-0191"
[2] "123 Main St"
[3] "(555) 555 0191"
[4] "Phone: 555.555.0191 Mobile: 555.555.0192"
# Replace digits with "X"
str_replace(contact, DGT, "X")
[1] "Call me at X55-555-0191"
[2] "X23 Main St"
[3] "(X55) 555 0191"
[4] "Phone: X55.555.0191 Mobile: 555.555.0192"
# Replace all digits with "X"
str_replace_all(contact, DGT, "X")
[1] "Call me at XXX-XXX-XXXX"
[2] "XXX Main St"
[3] "(XXX) XXX XXXX"
[4] "Phone: XXX.XXX.XXXX Mobile: XXX.XXX.XXXX"
# Replace all digits with different symbol
str_replace_all(contact, DGT, c("X", ".", "*", "_"))
[1] "Call me at XXX-XXX-XXXX"
[2] "... Main St"
[3] "(***) *** ****"
[4] "Phone: ___.___.____ Mobile: ___.___.____"
#Notice how now each string uses a different replacement character.
Kod zamene sa povratnim referencama, povratne reference se koriste u replacement
argumentu.
x <- c("hello", "sweet", "kitten")
str_replace(x, capture(ANY_CHAR), str_c(REF1, REF1))
[1] "hhello" "ssweet" "kkitten"
adverbs <- readRDS(here::here("data-raw","adverbs.rds"))
# Build pattern to match words ending in "ING"
pattern <- one_or_more(WRD) %R% "ING"
str_view(narratives, pattern)
# Test replacement
str_replace(narratives, capture(pattern),
str_c("CARELESSLY", REF1, sep = " "))
[1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE CARELESSLY PLAYING FOOTBALL W/ FRIENDS "
[2] "31 YOF FELL FROM TOILET HITITNG HEAD CARELESSLY SUSTAINING A CHI "
[3] "ANKLE STR. 82 YOM STRAINED ANKLE CARELESSLY GETTING OUT OF BED "
[4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"
[5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "
[6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "
[7] "13 MOF CARELESSLY TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
[8] "14YR M CARELESSLY PLAYING FOOTBALL; DX KNEE SPRAIN "
[9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "
[10] "5 YOM CARELESSLY ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"
# One adverb per narrative
adverbs_10 <- sample(adverbs, 10)
# Replace "***ing" with "adverb ***ly"
str_replace(narratives,
capture(pattern),
str_c(adverbs_10, REF1, sep = " "))
[1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE POSITIVELY PLAYING FOOTBALL W/ FRIENDS "
[2] "31 YOF FELL FROM TOILET HITITNG HEAD SOFTLY SUSTAINING A CHI "
[3] "ANKLE STR. 82 YOM STRAINED ANKLE ELEGANTLY GETTING OUT OF BED "
[4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"
[5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "
[6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "
[7] "13 MOF ENTHUSIASTICALLY TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
[8] "14YR M CARELESSLY PLAYING FOOTBALL; DX KNEE SPRAIN "
[9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "
[10] "5 YOM BROADLY ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"
Unicode standard asocira svako slovo ili simbol sa heksadecimalnim kodom tog znaka.
Heksadecimalni kod za određeni znak se može dobiti korišćenjem utf8ToInt
sa as
.
U regularnim izrazima treba specifikovati “\u
” a zatim heksadecimalni kod znaka.
Unicode takođe ima nekoliko načina za specifikaciju kolekcija: kategorije, skripte i blokovi.
Neki znakovi mogu biti specifikovani na dva načina, na primer è može biti specifikovan sa \u00e8
ili \u0065\u0300
(kombinacijom e i akcenta). Paket stringi
sadrži funkcije za konvertovanje između ove dve forme. stri_trans_nfc
komponuje dva znaka u jedan znak, dok stri_trans_nfd
dekomponuje jedan znak u dva znaka. U Unicode-u akcenat se naziva diakritičko Unicode svojsto, i gore spomenuti akcenat može se u rebusu koristiti sa UP_DIACRITIC
. Jedan od jezika koji često koristi dijakritike jeste Vijetnamski. U nastavku ćemo posmatrati vektor koji sadrži imena nekoliko Vijetnamskih vladara.
#Ovi znakovi izgledaju isto
x <- c("\u00e8", "\u0065\u0300")
writeLines(x)
è
e`
# Međutim sa jednim code pointom izvući ćemo samo jednu verziju...
str_view(x, "\u00e8")
as.hexmode(utf8ToInt(stri_trans_nfd("\u00e8")))
[1] "065" "300"
as.hexmode(utf8ToInt(stri_trans_nfc("\u0065\u0300")))
[1] "e8"
library(stringi)
# Names with builtin accents
tay_son_builtin <- c(
"Nguy\u1ec5n Nh\u1ea1c",
"Nguy\u1ec5n Hu\u1ec7",
"Nguy\u1ec5n Quang To\u1ea3n"
)
# Convert to separate accents
tay_son_separate <- stri_trans_nfd(tay_son_builtin)
#Verify that the string prints the same
tay_son_separate
[1] "Nguye^~n Nha<U+0323>c" "Nguye^~n Hue<U+0323>^" "Nguye^~n Quang Toa<U+0309>n"
# Match all accents
str_view_all(tay_son_separate, UP_DIACRITIC)
Povezani problem jeste pronalaženje podudarnosti sa pojedinačnim znakom. ANY_CHAR
koji smo do sada koristili, će tražiti podudarnost sa samo jednim code pointom. U donjem primeru regularni izraz će matchovati samo dva elementa vektora jer u trećem postoji è
koje je reprezentovano sa dva code pointa. Unicode standard sadrži i koncept grafema koji predstavlja pojedinačni display znak koji može biti predstavljen sa dva code pointa. U rebusu koristimo GRAPHEME
.
x <- c("Adele", "Ad\u00e8le", "Ad\u0065\u0300le")
writeLines(x)
Adele
Adèle
Ade`le
str_view(x, "Ad" %R% ANY_CHAR %R% "le")
str_view(x, "Ad" %R% GRAPHEME %R% "le")
# tay_son_separate has been pre-defined
tay_son_separate
[1] "Nguye^~n Nha<U+0323>c" "Nguye^~n Hue<U+0323>^" "Nguye^~n Quang Toa<U+0309>n"
# View all the characters in tay_son_separate
str_view_all(tay_son_separate, ANY_CHAR)
# View all the graphemes in tay_son_separate
str_view_all(tay_son_separate, GRAPHEME)
# Combine the diacritics with their letters
tay_son_builtin <- stri_trans_nfc(tay_son_separate)
tay_son_builtin
[1] "Nguy<U+1EC5>n Nh<U+1EA1>c" "Nguy<U+1EC5>n Hu<U+1EC7>" "Nguy<U+1EC5>n Quang To<U+1EA3>n"
# View all the graphemes in tay_son_builtin
str_view_all(tay_son_builtin, GRAPHEME)
Zadatak je prebrojati koliko svaki lik/uloga ima linija.
library(stringi)
library(stringr)
# Read play in using stri_read_lines()
earnest <- stri_read_lines(here::here("data-raw","importance-of-being-earnest.txt"))
# Detect start and end lines
start <- str_which(earnest, fixed("START OF THE PROJECT"))
end <- str_which(earnest, fixed("END OF THE PROJECT"))
# Get rid of gutenberg intro text
earnest_sub <- earnest[(start + 1):(end - 1)]
# Detect first act
lines_start <- str_which(earnest_sub, fixed("FIRST ACT"))
# Set up index
intro_line_index <- 1:(lines_start - 1)
# Split play into intro and play
intro_text <- earnest_sub[intro_line_index]
play_text <- earnest_sub[-intro_line_index]
# Take a look at the first 20 lines
writeLines(play_text[1:20])
FIRST ACT
SCENE
Morning-room in Algernon's flat in Half-Moon Street. The room is
luxuriously and artistically furnished. The sound of a piano is heard in
the adjoining room.
[Lane is arranging afternoon tea on the table, and after the music has
ceased, Algernon enters.]
Algernon. Did you hear what I was playing, Lane?
Lane. I didn't think it polite to listen, sir.
Algernon. I'm sorry for that, for your sake. I don't play
accurately--any one can play accurately--but I play with wonderful
expression. As far as the piano is concerned, sentiment is my forte. I
# Get rid of empty strings
empty <- stri_isempty(play_text)
play_lines <- play_text[!empty]
play_lines[10:15]
[1] "Algernon. I'm sorry for that, for your sake. I don't play"
[2] "accurately--any one can play accurately--but I play with wonderful"
[3] "expression. As far as the piano is concerned, sentiment is my forte. I"
[4] "keep science for Life."
[5] "Lane. Yes, sir."
[6] "Algernon. And, speaking of the science of Life, have you got the"
# Pattern for start, word then .
pattern_1 <- START %R% one_or_more(WRD) %R% DOT
# Test pattern_1
str_view(play_lines, pattern_1, match = TRUE)
str_view(play_lines, pattern_1, match = FALSE)
# Pattern for start, capital, word then .
pattern_2 <- START %R% ascii_upper() %R% one_or_more(WRD) %R% DOT
# Test pattern_2
str_view(play_lines, pattern_2, match = TRUE)
str_view(play_lines, pattern_2, match = FALSE)
# Pattern from last step
pattern_2 <- START %R% ascii_upper() %R% one_or_more(WRD) %R% DOT
# Get subset of lines that match
lines <- str_subset(play_lines, pattern_2)
# Extract match from lines
who <- str_extract(lines, pattern_2)
# Let's see what we have
unique(who)
[1] "Algernon." "Lane." "Jack." "Cecily." "Ernest."
[6] "University." "Gwendolen." "July." "Chasuble." "Merriman."
[11] "Sunday." "Mr." "London." "Cardew." "Opera."
[16] "Markby." "Oxonian."
# it looks like your pattern wasn't 100% successful.
# It missed Lady Bracknell, and picked up lines starting with University., July. and a few others.
# Let's try a slightly different strategy.
Drugi pokušaj.
# Create vector of characters
characters <- c("Algernon", "Jack", "Lane", "Cecily", "Gwendolen", "Chasuble",
"Merriman", "Lady Bracknell", "Miss Prism")
# Match start, then character names then .
pattern_3 <- START %R% or1(characters) %R% DOT
# View matches of pattern_3
str_view(play_lines, pattern_3, match = TRUE)
# View non-matches of pattern_3
str_view(play_lines, pattern_3, match = FALSE)
# Variables from previous step
characters <- c("Algernon", "Jack", "Lane", "Cecily", "Gwendolen", "Chasuble",
"Merriman", "Lady Bracknell", "Miss Prism")
pattern_3 <- START %R% or1(characters) %R% DOT
# Pull out matches
lines <- str_subset(play_lines, pattern_3)
# Extract match from lines
who <- str_extract(lines, pattern_3)
# Let's see what we have
unique(who)
[1] "Algernon." "Lane." "Jack." "Cecily."
[5] "Gwendolen." "Lady Bracknell." "Miss Prism." "Chasuble."
[9] "Merriman."
# Count lines per character
table(who)
who
Algernon. Cecily. Chasuble. Gwendolen. Jack.
201 154 42 102 219
Lady Bracknell. Lane. Merriman. Miss Prism.
84 21 17 41
catcidents <- readRDS(here::here("data-raw","catcidents.rds"))
# catcidents has been pre-defined
head(catcidents)
[1] "79yOf Fractured fingeR tRiPPED ovER cAT ANd fell to FlOOr lAst nIGHT AT HOME*"
[2] "21 YOF REPORTS SUS LACERATION OF HER LEFT HAND WHEN SHE WAS OPENING A CAN OF CAT FOOD JUST PTA. DX HAND LACERATION%"
[3] "87YOF TRIPPED OVER CAT, HIT LEG ON STEP. DX LOWER LEG CONTUSION "
[4] "bLUNT CHest trAUma, R/o RIb fX, R/O CartiLAgE InJ To RIB cAge; 32YOM walKiNG DOG, dog took OfF aFtER cAt,FelL,stRucK CHest oN STepS,hiT rIbS"
[5] "42YOF TO ER FOR BACK PAIN AFTER PUTTING DOWN SOME CAT LITTER DX: BACK PAIN, SCIATICA"
[6] "4YOf DOg jUst hAd PUpPieS, Cat TRIED 2 get PuPpIes, pT THru CaT dwn stA Irs, LoST foOTING & FELl down ~12 stePS; MInor hEaD iNJuRY"
# Construct pattern of DOG in boundaries
whole_dog_pattern <- whole_word("DOG")
# See matches to word DOG
str_view(catcidents, pattern = whole_dog_pattern, match = TRUE)
# From previous step
whole_dog_pattern <- whole_word("DOG")
# Transform catcidents to upper case
catcidents_upper <- str_to_upper(catcidents)
# View matches to word "DOG" again
str_view(catcidents_upper, pattern = whole_dog_pattern, match = TRUE)
# From previous steps
whole_dog_pattern <- whole_word("DOG")
catcidents_upper <- str_to_upper(catcidents)
# Which strings match?
has_dog <- str_detect(catcidents_upper,
pattern = whole_dog_pattern)
# Pull out matching strings in original
catcidents[has_dog]
[1] "bLUNT CHest trAUma, R/o RIb fX, R/O CartiLAgE InJ To RIB cAge; 32YOM walKiNG DOG, dog took OfF aFtER cAt,FelL,stRucK CHest oN STepS,hiT rIbS"
[2] "4YOf DOg jUst hAd PUpPieS, Cat TRIED 2 get PuPpIes, pT THru CaT dwn stA Irs, LoST foOTING & FELl down ~12 stePS; MInor hEaD iNJuRY"
[3] "unhelmeted 14yof riding her bike with her dog when she saw a cat and sw erved c/o head/shoulder/elbow pain.dx: minor head injury,left shoulder"
[4] "Rt Shoulder Strain.26Yof Was Walking Dog On Leash And Dot Saw A Cat And Pulled Leash."
[5] "67 YO F WENT TO WALK DOG, IT STARTED TO CHASE CAT JERKED LEASH PULLED H ER OFF PATIO, FELL HURT ANKLES. DX BILATERAL ANKLE FRACTURES"
[6] "46yof taking dog outside, dog bent her fingers back on a door. dog jerk ed when saw cat. hand holding leash caught on door jamb/ct hand"
[7] "PUSHING HER UTD WITH SHOTS DOG AWAY FROM THE CAT'S BOWL&BITTEN TO FINGE R>>PW/DOG BITE"
[8] "DX R SH PN: 27YOF W/ R SH PN X 5D. STATES WAS YANK' BY HER DOG ON LEASH W DOG RAN AFTER CAT; WORSE' PN SINCE. FULL ROM BUT VERY PAINFUL TO MOVE"
[9] "39Yof dog pulled her down the stairs while chasing a cat dx: rt ankle inj"
[10] "44Yof Walking Dog And The Dof Took Off After A Cat And Pulled Pt Down B Y The Leash Strained Neck"