Materiały pomocnicze:
https://en.wikipedia.org/wiki/Regular_expression
https://r4ds.had.co.nz/strings.html
https://www.regular-expressions.info/
https://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124
Dorzućmy sobie kilka narzędzi (pakietów) do pracy…
# installed.packages("stringr")
library(stringr)
# install.packages("dplyr")
library(dplyr)
# install.packages("babynames")
library(babynames)
# install.packages("rebus")
library(rebus)
Przypomnienie z rozruchowych zajęć (1 i 2). Do tworzenia
string’a, czy też ciągu znaków używamy: "..."
(cudzysłów), bądź '...' (apostrof). Możemy je również
łączyć i mieszać, jeśli ma to merytoryczne uzasadnienie, albo istnieje
taka konieczność…
# Przykład
print("Cześć")
## [1] "Cześć"
print("Hello \U1F30D") # <= Unicode characters
## [1] "Hello 🌍"
""Cześć" - powiedziałem jak tylko ją zobaczyłem" nie
zadziała, ponieważ jedne nawiasy wyłączają znaczenie drugich.
Rozwiązaniem może być …albo miks pojedynczych i podwójnych…
# Przykład
print('"Cześć" - powiedziałem jak tylko ją zobaczyłem') # wbił nam się ukośnik (an escape sequence) - nie przejmujemy się
## [1] "\"Cześć\" - powiedziałem jak tylko ją zobaczyłem"
cat( '"Cześć" - powiedziałem jak tylko ją zobaczyłem') # gdyż tak to się będzie printować w finalnych raportach, dashboard'ach, etc.
## "Cześć" - powiedziałem jak tylko ją zobaczyłem
…albo wyłączenie znaczenia znaku specjalnego poprzez jawne
użycie ukośnika (an escape sequence)
# Przykład
print("\"Cześć\" - powiedziałem jak tylko ją zobaczyłem")
## [1] "\"Cześć\" - powiedziałem jak tylko ją zobaczyłem"
cat( "\"Cześć\" - powiedziałem jak tylko ją zobaczyłem")
## "Cześć" - powiedziałem jak tylko ją zobaczyłem
Reguła (dobra praktyka) jest taka, że
używamy:
Co do zasady podwójnego
print("Cześć!")
## [1] "Cześć!"
cat( "Cześć!")
## Cześć!
Gdy cytujemy wewnątrz string’a, to w środku
podwójny, na zewnątrz pojedynczy
print('"Cześć!" - rzuciłem bez większego zaangażowania')
## [1] "\"Cześć!\" - rzuciłem bez większego zaangażowania"
cat( '"Cześć!" - rzuciłem bez większego zaangażowania')
## "Cześć!" - rzuciłem bez większego zaangażowania
Gdy musimy w środku string’a użyć i jednych i
drugi, wówczas wyłączajmy znaczenie specjalne nawiasów.
print("\"Cześć!\" - rzuciłem bez większego zaangażowania. \"Słuchasz rock\'n\'roll\'a?\"")
## [1] "\"Cześć!\" - rzuciłem bez większego zaangażowania. \"Słuchasz rock'n'roll'a?\""
cat( "\"Cześć!\" - rzuciłem bez większego zaangażowania. \"Słuchasz rock\'n\'roll\'a?\"")
## "Cześć!" - rzuciłem bez większego zaangażowania. "Słuchasz rock'n'roll'a?"
print()
Wyświetla obiekt w „standardowy”, sformatowany sposób używany przez R.
Nadaje się do debugowania i automatycznego wypisywania wyników (np. w konsoli, funkcjach).
Zwraca obiekt, który został wydrukowany.
x <- 6
print(x)
## [1] 6
cat()
Łączy i wypisuje tekst bez dodatkowego formatowania, zwykle w jednej linii.
Idealne do tworzenia komunikatów tekstowych, napisów, własnego formatowania.
Nie zwraca obiektu — wypisuje tylko tekst.
x <- 6
cat("Wartość x wynosi:", x, "!")
## Wartość x wynosi: 6 !
cat("┌───────────────┐\n│ R is awesome! │\n└───────────────┘\n")
## ┌───────────────┐
## │ R is awesome! │
## └───────────────┘
cat("✔ Sukces\n")
## ✔ Sukces
cat("✘ Błąd\n")
## ✘ Błąd
cat("➜ Dalej...\n")
## ➜ Dalej...
cat("R mówi: 😺📊📈\n")
## R mówi: 😺📊📈
print("R mówi: 😺📊📈\n") # zrobić w konsoli
## [1] "R mówi: 😺📊📈\n"
Podsumowanie:
print() do czytelnego wyświetlania obiektów
R, zaś cat() do wypisywania
tekstów/komunikatów.
format()
Zwraca obiekt (np. liczby, daty) jako łańcuch znaków w estetycznej formie. Służy do ustawiania szerokości, wyrównania, liczby miejsc po przecinku itp. – głównie do ładnego wyświetlania lub tworzenia tabel tekstowych.
x <- 3.1415926
format(x, digits = 4) # "3.142"
## [1] "3.142"
format(x, nsmall = 4) # "3.1416"
## [1] "3.141593"
formatC()
To niskopoziomowa, szybka funkcja do formatowania liczb w stylu języka C. Użyteczna gdy chcemy dokładnie kontrolować:
liczbę miejsc po przecinku,
szerokość pola,
wyrównanie,
notację (np. naukową).
x <- 3.1415926
formatC(x,
format = "f", # tzw. fixed format
digits = 2) # "3.14" <- zwykły ułamek dziesiętny
## [1] "3.14"
formatC(x,
format = "e", # tzw. scientific format
digits = 2) # "3.14e+00" <- notacja naukowa; mantysa × 10^(wykładnik)
## [1] "3.14e+00"
prettyNum()
Zmienia liczby na ładniejsze teksty, np. dodając separatory tysięcy albo kontrolując format naukowy. Służy do prezentacji liczb przyjemnie dla oka odbiorcy, np. w raportach:
prettyNum(1234567.89,
big.mark = " ",
decimal.mark = ",") # "1 234 567,89"
## [1] "1 234 568"
Po więcej info i bajerów: ?prettyNum(), bądź
?format(), albo też ?formatC()
Jak łączyć stringi? Użyjemy funkcji paste() lub
paste0(), które w międzyczasie używaliśmy kilkukrotnie w
różnych przykładach na poprzednich zajęciach.
paste()
Łączy (konkatenuje) elementy w jeden łańcuch znaków, wstawiając
separator między nimi (domyślnie spacja " ").
paste("W", "I", "T", "A", "M")
## [1] "W I T A M"
# (domyślny sep = " ")
paste("W", "I", "T", "A", "M",
sep = " * ")
## [1] "W * I * T * A * M"
paste("lot: ", 1:3) # z poprzednich zajęć, przykład tzw. recyclingu
## [1] "lot: 1" "lot: 2" "lot: 3"
x <- 1:12
paste(c("Zajęcia", x), collapse = ", ")
## [1] "Zajęcia, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12"
paste("Zajęcia", x, collapse = ", ")
## [1] "Zajęcia 1, Zajęcia 2, Zajęcia 3, Zajęcia 4, Zajęcia 5, Zajęcia 6, Zajęcia 7, Zajęcia 8, Zajęcia 9, Zajęcia 10, Zajęcia 11, Zajęcia 12"
paste0()
To skrót od paste(..., sep = "") – czyli łączenie bez
żadnego separatora. Gdy chcemy skleić coś „na styk”: nazwy plików,
identyfikatory, prefiksy, sufiksy itp.
paste0("ID_", 1:3) # "ID_1" "ID_2" "ID_3"
## [1] "ID_1" "ID_2" "ID_3"
folder <- "wyniki"
plik <- "dane.csv"
paste0(folder, "/", plik) # "wyniki/dane.csv"
## [1] "wyniki/dane.csv"
paste( "a", "b") # a b
## [1] "a b"
paste0("a", "b") # ab
## [1] "ab"
W raporcie możemy chcieć dodać jednostkę do wartości
# przykład z dolarem
wartosc = 354
paste("$", wartosc, sep = "")
## [1] "$354"
# przykład z procentem
wartosc = 7.8
paste(wartosc, "%", sep = "")
## [1] "7.8%"
# przykład ze złotówkami
wartosci = c(234, 456, 756, 423, 456, 345)
paste(wartosci, "PLN", sep = "")
## [1] "234PLN" "456PLN" "756PLN" "423PLN" "456PLN" "345PLN"
Przykład mundialowego “pejsta”
polska = "Polska!"
cat(paste(c("Kto wygra mecz? ", "Kto? ", " Ktoooo?"), polska,
collapse = ", " ), rep("Do booojuuuu...", 3))
## Kto wygra mecz? Polska!, Kto? Polska!, Ktoooo? Polska! Do booojuuuu... Do booojuuuu... Do booojuuuu...
Wtręt, przedsmak pisania funkcji, poniżej przykład funkcji z Data.Camp.com, do śpiewania przyśpiewki..
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)) # ?writeLines()
}
# dwa argumenty, nazwa zwierzątka i wytwarzany przez nie dźwięk
old_mac("ratel miodożerny", "WrrrAgrrrr")
## Old MacDonald had a farm
## E-I-E-I-O
## And on his farm he had a ratel miodożerny
## E-I-E-I-O
## Here a WrrrAgrrrr, There a WrrrAgrrrr, Everywhere a WrrrAgrrrr-WrrrAgrrrr
## Old MacDonald had a farm
## E-I-E-I-O
Połączenie dotychczas poznanych funkcji format() i
paste() w celu stworzenia schludnej tabelki
(nazwy <- c("Rok 0", "Rok 1", "Rok 2", "Razem..."))
## [1] "Rok 0" "Rok 1" "Rok 2" "Razem..."
(nazwy <- format(nazwy, justify = "right"))
## [1] " Rok 0" " Rok 1" " Rok 2" "Razem..."
(dochod <- c(12.34, 2345.6701, 34567.807, 875432.5))
## [1] 12.34 2345.67 34567.81 875432.50
(dochod <- format(dochod, digits = 2, big.mark=","))
## [1] " 12" " 2,346" " 34,568" "875,433"
(dochod <- paste("$ kolumbijskie ", dochod, sep = ""))
## [1] "$ kolumbijskie 12" "$ kolumbijskie 2,346"
## [3] "$ kolumbijskie 34,568" "$ kolumbijskie 875,433"
wiersze <- paste(nazwy, dochod, sep=" ")
writeLines(wiersze)
## Rok 0 $ kolumbijskie 12
## Rok 1 $ kolumbijskie 2,346
## Rok 2 $ kolumbijskie 34,568
## Razem... $ kolumbijskie 875,433
for (i in 1:10) {
cat("\r", strrep("█", i), strrep("·", 10 - i), i * 10, "%")
flush.console()
Sys.sleep(0.1)
}
cat("\n")
{stringr}Pakiet
stringrwRjest częścią ekosystemutidyversei służy do manipulacji tekstem. Oferuje prosty i spójny zestaw funkcji do operacji na ciągach znaków
str_c()
Funkcja str_c() w pakiecie {stringr}
służy do łączenia (konkatenacji) ciągów znaków. Jest to odpowiednik
funkcji paste() w bazowym R, ale z bardziej
spójną składnią i dodatkowymi możliwościami.
nazwiska <- c("Kowalski", "Nowak", "Szymkiewicz", "Dziurda") # wektor nazwisk
paste(c("Pan", "Pani", rep("Pan", 2)), nazwiska, sep = " ")
## [1] "Pan Kowalski" "Pani Nowak" "Pan Szymkiewicz" "Pan Dziurda"
str_c(c("Pan", "Pani", rep("Pan", 2)), nazwiska, sep = " ") # bliźniacza funkcja z pakietu {stringr}
## [1] "Pan Kowalski" "Pani Nowak" "Pan Szymkiewicz" "Pan Dziurda"
Różnice (radzenie sobie z brakami danych)
(nazwiska <- c(NA, "Kowalski", "Nowak", NA, "Szymkiewicz", "Dziurda")) #
## [1] NA "Kowalski" "Nowak" NA "Szymkiewicz"
## [6] "Dziurda"
paste(c("Pan"), nazwiska, sep = " ") # "Pan NA"
## [1] "Pan NA" "Pan Kowalski" "Pan Nowak" "Pan NA"
## [5] "Pan Szymkiewicz" "Pan Dziurda"
str_c(c("Pan"), nazwiska, sep = " ") # a tu zwykły missing values, czują Państwo przewagę?
## [1] NA "Pan Kowalski" "Pan Nowak" NA
## [5] "Pan Szymkiewicz" "Pan Dziurda"
str_replace_na()str_replace_na(string = nazwiska,
replacement = "Brak danych")
## [1] "Brak danych" "Kowalski" "Nowak" "Brak danych" "Szymkiewicz"
## [6] "Dziurda"
str_length()
Funkcja str_length() w pakiecie
{stringr} służy do zwracania długości ciągów znaków. Jest
to bardzo przydatne, gdy chcesz szybko sprawdzić, ile znaków zawiera
dany ciąg.
nazwiska <- c("Kowalski", "Nowak", "Szymkiewicz", "Dziurda")
str_length(nazwiska) # zwrócił długość każdego elementu wektora
## [1] 8 5 11 7
babynames data set.
# install.packages("babynames")
# library(babynames)
data(babynames)
dplyr::glimpse(babynames)
## Rows: 1,924,665
## Columns: 5
## $ year <dbl> 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880,…
## $ sex <chr> "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", …
## $ name <chr> "Mary", "Anna", "Emma", "Elizabeth", "Minnie", "Margaret", "Ida",…
## $ n <int> 7065, 2604, 2003, 1939, 1746, 1578, 1472, 1414, 1320, 1288, 1258,…
## $ prop <dbl> 0.07238359, 0.02667896, 0.02052149, 0.01986579, 0.01788843, 0.016…
# ?babynames
babynames %>%
filter(year == 2014 & sex == "M") %>%
select(name) -> boy_names
boy_names <- as.vector(boy_names$name)
babynames %>%
filter(year == 2014 & sex == "F") %>%
select(name) -> girl_names
girl_names <- as.vector(girl_names$name)
# średnie długości imion w zbiorze przyciętym do roku 2014
# dla kobiet i mężczyzn -> różnica średnich
boy_length <- str_length(boy_names)
girl_length <- str_length(girl_names)
(mean(girl_length)- mean(boy_length))
## [1] 0.3374758
Przykładowy dashboard z sieci bazujący na danych (dla inspiracji)
https://staff.math.su.se/hoehle/blog/2017/03/01/morebabynames.html
str_sub()
Funkcja str_sub() w pakiecie {stringr}
służy do wyodrębniania podciągów z ciągów znaków na podstawie pozycji
początkowej i końcowej. Można również użyć tej funkcji do modyfikacji
części ciągu znaków.
boy_first_letter <- str_sub(string = boy_names,
start = 1,
end = 1) # pierwsze litery imiony chłopców
(janitor::tabyl(boy_first_letter))
## boy_first_letter n percent
## A 1454 0.103509646
## B 651 0.046344415
## C 770 0.054815975
## D 998 0.071047199
## E 549 0.039083078
## F 185 0.013170072
## G 334 0.023777319
## H 403 0.028689400
## I 235 0.016729551
## J 1390 0.098953513
## K 1291 0.091905745
## L 537 0.038228803
## M 914 0.065067274
## N 424 0.030184381
## O 207 0.014736243
## P 230 0.016373603
## Q 56 0.003986616
## R 778 0.055385492
## S 806 0.057378800
## T 771 0.054887165
## U 43 0.003061152
## V 160 0.011390332
## W 174 0.012386987
## X 56 0.003986616
## Y 252 0.017939774
## Z 379 0.026980850
boy_last_letter <- str_sub(boy_names,
start = -1,
end = -1) # ostatnie litery imion chłopców (numerowanie ze znakiem ujemnym)
(janitor::tabyl(boy_last_letter))
## boy_last_letter n percent
## a 421 0.029970812
## b 104 0.007403716
## c 92 0.006549441
## d 436 0.031038656
## e 1148 0.081725635
## f 66 0.004698512
## g 82 0.005837545
## h 583 0.041503524
## i 705 0.050188652
## j 57 0.004057806
## k 349 0.024845163
## l 945 0.067274151
## m 389 0.027692746
## n 4672 0.332597708
## o 730 0.051968392
## p 32 0.002278066
## q 19 0.001352602
## r 1011 0.071972663
## s 826 0.058802591
## t 292 0.020787357
## u 81 0.005766356
## v 71 0.005054460
## w 34 0.002420446
## x 86 0.006122304
## y 697 0.049619136
## z 119 0.008471560
girl_first_letter <- str_sub(girl_names,
start = 1
,end = 1)
janitor::tabyl(girl_first_letter)
## girl_first_letter n percent
## A 3101 0.161670403
## B 699 0.036442313
## C 946 0.049319639
## D 810 0.042229289
## E 933 0.048641885
## F 209 0.010896199
## G 345 0.017986549
## H 469 0.024451280
## I 373 0.019446327
## J 1430 0.074552943
## K 1694 0.088316563
## L 1122 0.058495386
## M 1746 0.091027579
## N 752 0.039205464
## O 143 0.007455294
## P 303 0.015796882
## Q 38 0.001981127
## R 831 0.043324123
## S 1369 0.071372713
## T 683 0.035608154
## U 28 0.001459778
## V 214 0.011156874
## W 85 0.004431469
## X 62 0.003232365
## Y 294 0.015327668
## Z 502 0.026171732
girl_last_letter <- str_sub(girl_names,
start = -1,
end = -1)
janitor::tabyl(girl_last_letter)
## girl_last_letter n percent
## a 6632 0.3457588238
## b 20 0.0010426985
## c 13 0.0006777540
## d 81 0.0042229289
## e 3114 0.1623481570
## f 8 0.0004170794
## g 21 0.0010948334
## h 1942 0.1012460247
## i 1581 0.0824253167
## j 12 0.0006256191
## k 31 0.0016161827
## l 450 0.0234607163
## m 115 0.0059955164
## n 2608 0.1359678849
## o 105 0.0054741671
## p 3 0.0001564048
## q 2 0.0001042699
## r 291 0.0151712632
## s 326 0.0169959856
## t 208 0.0108440644
## u 59 0.0030759606
## v 6 0.0003128096
## w 17 0.0008862937
## x 50 0.0026067463
## y 1435 0.0748136176
## z 51 0.0026588812
# ekwiwalent substr()
# więcej o tabyl() choćby tu:
# https://cran.r-project.org/web/packages/janitor/vignettes/tabyls.html
Wartości ujemne - koniec string’u.
# Wyodrębnianie ostatnich 6 znaków
str_sub("Hello, world!", -6, -1)
## [1] "world!"
Modyfikacja “podciągów”.
# Zmiana pierwszych 5 znaków
x <- "Hello, world!"
str_sub(x, 1, 5) <- "Hi"
str_detect()
Funkcja str_detect() w pakiecie
{stringr} służy do sprawdzania, czy dany wzorzec występuje
w ciągu znaków. Zwraca wartość logiczną (TRUE lub
FALSE) dla każdego elementu wektora, w zależności od tego,
czy wzorzec został znaleziony.
Które z postaci z poniższego wektora były ninja?
PowerRangers <- c("Power Rangers Time Force",
"Mighty Morphin Power Rangers 1",
"Mighty Morphin Super Alien Rangers",
"power Rangers Turbo",
"Power Rangerrs Ninja Storm",
"Power Rrangerrs Super Ninja Steel Ninja",
"power Rangers Wild Force") # https://pl.wikipedia.org/wiki/Power_Rangers
# Które ninja?
str_detect(string = PowerRangers, pattern = "Ninja")
## [1] FALSE FALSE FALSE FALSE TRUE TRUE FALSE
ninja <- str_detect(string = PowerRangers, pattern = "Ninja")
PowerRangers[ninja] # indeksowanie wektorem ninja
## [1] "Power Rangerrs Ninja Storm"
## [2] "Power Rrangerrs Super Ninja Steel Ninja"
# Które z postaci miały "moc"?
force <- str_detect(PowerRangers, "Force")
PowerRangers[force]
## [1] "Power Rangers Time Force" "power Rangers Wild Force"
# zwraca wektor logiczny, TRUE jest w miejscu wektora (na elemencie),
# gdzie znaleziona została szukana fraza
str_subset()
Funkcja str_subset() w pakiecie
{stringr} służy do filtrowania wektora ciągów znaków,
zwracając tylko te elementy, które pasują do określonego wzorca. Jest to
bardzo przydatne, gdy chcemy wyodrębnić podzbiór danych tekstowych na
podstawie określonego kryterium.
Zwraca te elementy wektora, które zawierają szukaną frazę
str_subset(string = PowerRangers, pattern = "Super")
## [1] "Mighty Morphin Super Alien Rangers"
## [2] "Power Rrangerrs Super Ninja Steel Ninja"
str_subset(string = PowerRangers, pattern = "Force")
## [1] "Power Rangers Time Force" "power Rangers Wild Force"
Filtrowanie ciągów zawierających cyfrę
str_subset(PowerRangers,
"\\d" # W wyrażeniach regularnych, \\d oznacza dowolną cyfrę od 0 do 9. Jest to skrót, który pozwala na łatwe wyszukiwanie cyfr w ciągach znaków
)
## [1] "Mighty Morphin Power Rangers 1"
str_count()
Funkcja str_count() z pakietu
{stringr} w R służy do liczenia liczby
wystąpień wzorca w ciągu znaków.
# liczy wystąpienia wzoru, w każdym elemencie wektora
str_count(string = PowerRangers, pattern = "Ninja")
## [1] 0 0 0 0 1 2 0
str_count(string = PowerRangers, pattern = "Force")
## [1] 1 0 0 0 0 0 1
Funkcja str_count() jest czuła na wielkość liter,
więc np. ‘a’ i ‘A’ będą traktowane jako różne znaki.
str_count(string = PowerRangers, pattern = "ninja")
## [1] 0 0 0 0 0 0 0
str_extract()
Funkcja str_extract() z pakietu
{stringr} w R służy do wyodrębniania
pierwszego pełnego dopasowania wzorca z każdego ciągu znaków.
# zwraca wektor tej samej długości, ale tylko elementy, które pasują do wzoru
str_extract(string = PowerRangers, pattern = "Super")
## [1] NA NA "Super" NA NA "Super" NA
Wyodrębniamy pierwszą cyfrę z każdego elementu wektora
str_extract(PowerRangers, "\\d")
## [1] NA "1" NA NA NA NA NA
Wyodrębniamy pierwsze słowo z każdego elementu wektora
str_extract(PowerRangers, "[a-z]+")
## [1] "ower" "ighty" "ighty" "power" "ower" "ower" "power"
Lepiej…
str_extract(PowerRangers, "[a-zA-Z]+")
## [1] "Power" "Mighty" "Mighty" "power" "Power" "Power" "power"
Chociaż te zakres też obejmie duże i małe litery.
str_extract(PowerRangers, "[A-z]+")
## [1] "Power" "Mighty" "Mighty" "power" "Power" "Power" "power"
str_split()
Funkcja str_split() z pakietu
{stringr} w R służy do dzielenia ciągu znaków
na części według określonego wzorca.
# rozbija element
str_split(string = c("Matematyka i Kognitywistyka"),
pattern = " i ") # czy rozpoznają Państwo jaki typ obiektu został zwrócony?
## [[1]]
## [1] "Matematyka" "Kognitywistyka"
# może tak będzie łatwiej?
str_split(string = c("Matematyka i Kognitywistyka", "Kognistywistyka i Informatyka"),
pattern = " i ")
## [[1]]
## [1] "Matematyka" "Kognitywistyka"
##
## [[2]]
## [1] "Kognistywistyka" "Informatyka"
# chcąc mieć obiekt "prostszy" w obsłudze, możemy użyć dodatkowego argumentu funkcji
str_split(string = c("Matematyka i Kognitywistyka"),
pattern = " i ", simplify = TRUE) # wówczas mamy do czynienia nie z listą ale z matrycą
## [,1] [,2]
## [1,] "Matematyka" "Kognitywistyka"
str_split(string = c("Matematyka i Kognitywistyka", "Kognistywistyka i Informatyka"),
pattern = " i ", simplify = TRUE)
## [,1] [,2]
## [1,] "Matematyka" "Kognitywistyka"
## [2,] "Kognistywistyka" "Informatyka"
str_split(string = c("Matematyka i Kognitywistyka i Informatyka"),
pattern = " i ")
## [[1]]
## [1] "Matematyka" "Kognitywistyka" "Informatyka"
str_split(string = c("Matematyka i Kognitywistyka i Informatyka"),
pattern = " i ", n = 2) # na dwie części, kolejne wystąpienie wzoru zostanie zignorowane
## [[1]]
## [1] "Matematyka" "Kognitywistyka i Informatyka"
str_replace()
Funkcjastr_replace() z pakietu
{stringr} w R służy do zastępowania
pierwszego dopasowania wzorca w ciągu znaków.
# Zamienia wzór na inny wzór
str_replace(string = "i czasopisma",
pattern = "i ", # https://pl.wikipedia.org/wiki/Ustawa_o_radiofonii_i_telewizji
replacement = " lub ")# https://pl.wikipedia.org/wiki/(%E2%80%A6)_lub_czasopisma
## [1] " lub czasopisma"
str_replace(string = "Matematyka i Kognitywistyka i Informatyka",
pattern = " i ",
replacement = " lub ")
## [1] "Matematyka lub Kognitywistyka i Informatyka"
A jeśli wszystko wówczas użyjemy funkcji
str_replace_all()
str_replace_all(string = "Matematyka i Kognitywistyka i Informatyka", # wektor jednoelementowy
pattern = " i ",
replacement = " lub ")
## [1] "Matematyka lub Kognitywistyka lub Informatyka"
str_replace_all(string = c("Matematyka i Kognitywistyka",
"Kognistywistyka i Informatyka"),
pattern = " i ",
replacement = " lub ") # działa na wektorze wieloelementowym
## [1] "Matematyka lub Kognitywistyka" "Kognistywistyka lub Informatyka"
{base}grepl()
grepl(pattern = <regex>, x = <string>)
# ?regex
animals <- c("cat","moose","impala","ant","kiwi")# ćwiczeniowy wektor
grepl(pattern = "a", x = animals) # pierwsze wystąpienie "a"
## [1] TRUE FALSE TRUE TRUE FALSE
grepl(pattern = "^a", x = animals) # "a" na początku wyrazu
## [1] FALSE FALSE FALSE TRUE FALSE
grepl(pattern = "a$", x = animals) # "a" na końcu wyrazu
## [1] FALSE FALSE TRUE FALSE FALSE
##grep()
grep(pattern = <regex>, x = <string>)
(animals <- c("cat","moose","impala","ant","kiwi"))# ćwiczeniowy wektor
## [1] "cat" "moose" "impala" "ant" "kiwi"
grepl(pattern = "a", x = animals)
## [1] TRUE FALSE TRUE TRUE FALSE
# vs
grep(pattern = "a", x = animals)
## [1] 1 3 4
which(
grepl(pattern = "a",
x = animals))
## [1] 1 3 4
grep(pattern = "^a", # ^ na początku
x = animals)
## [1] 4
Czyli..
grepl() w R jest funkcją służącą do wyszukiwania wzorców
w ciągach znaków. Zwraca wektor wartości logicznych, gdzie
TRUE oznacza, że wzorzec został znaleziony, a
FALSE oznacza, że nie został znaleziony.
grep() służy również do wyszukiwania wzorców w ciągach
znaków, ale zwraca indeksy, na których wzorzec został znaleziony.
sub(), gsub()
sub(pattern = <regex>, replacement = <str>, x = <str>)
animals <- c("cat","moose","impala","ant","kiwi")# ćwiczeniowy wektor
sub(pattern = "a",
replacement = "o",
x = animals) # pierwsze wystąpienie
## [1] "cot" "moose" "impola" "ont" "kiwi"
# vs
gsub(pattern = "a",
replacement = "o",
x = animals) # wszystkie wystąpienia
## [1] "cot" "moose" "impolo" "ont" "kiwi"
Czyli…
sub() (od “substitute”) służy do zastępowania
pierwszego wystąpienia danego wzorca w każdym elemencie wektora tekstu.
gsub() (od “global substitute”) zastępuje
wszystkie wystąpienia danego wzorca w każdym elemencie wektora
tekstu.
gsub(pattern = "a|i", # a lub i
replacement = "_",
x = animals)
## [1] "c_t" "moose" "_mp_l_" "_nt" "k_w_"
gsub(pattern = "a|i|o", # a lub i lub o
replacement = "_",
x = animals)
## [1] "c_t" "m__se" "_mp_l_" "_nt" "k_w_"
Mamy przygotowany wektor z adresami e-mail. Wydrukujmy wynik.
(emails <- c("john.doe@ivyleague.edu",
"education@world.gov",
"dalai.lama@peace.org",
"invalid.edu",
"quant@bigdatacollege.edu",
"cookie.monster@sesame.tv"))
## [1] "john.doe@ivyleague.edu" "education@world.gov"
## [3] "dalai.lama@peace.org" "invalid.edu"
## [5] "quant@bigdatacollege.edu" "cookie.monster@sesame.tv"
Chcielibyśmy wyciągnąć tylko e-mail’e, które są prawidłowe i dotyczą kont akademickich
# czy adres zawiera frazę .edu ?
czyZawiera <- grepl(".edu", emails)
emails[czyZawiera]
## [1] "john.doe@ivyleague.edu" "invalid.edu"
## [3] "quant@bigdatacollege.edu"
# ale widzimy, że jest jakiś problem z drugi adresem (to nie adres), zatem...
# czy adres zawiera 'małpę", czyli jest adresem e-mail, między małpą a domeną jest jeden lub wiele znaków, oraz jest to zdres akademicki, zapisana do wektora hits
(hits <- grep("@.*\\.edu$", emails))
## [1] 1 5
# Podzbiór szukanych mejli z użycie wektora hits (daje nam indeksy, gdzie szukana fraza została znaleziona)
emails[hits]
## [1] "john.doe@ivyleague.edu" "quant@bigdatacollege.edu"
@, ponieważ prawidłowy adres e-mail musi zawierać
znak at-sign.
.*,który dopasowuje dowolny znak (.) zero lub więcej
razy (*). Zarówno kropka, jak i gwiazdka są metaznakami. Możesz ich użyć
do dopasowania dowolnego znaku znajdującego się pomiędzy znakiem at a
częścią „.edu” adresu e-mail.
\\.edu$, aby dopasować część „.edu” adresu e-mail na
końcu ciągu. Część \\ zamienia znaczenie kropce: mówi R, że
chcesz użyć . jako jej rzeczywistą postać.
Podczas gdy grep() i grepl()
służyły po prostu do sprawdzania, czy wyrażenie regularne może być
dopasowane do wektora znaków, sub()i gsub()
idą o krok dalej: możesz podać argument zamiany. Jeśli wewnątrz wektora
znaków x, zostanie znaleziony wzór wyrażenia regularnego,
to pasujący element (elementy) zostanie zastąpiony zamiennikiem.
sub() zastępuje tylko pierwsze dopasowanie, podczas gdy
gsub() zastępuje wszystkie dopasowania.
Załóżmy, że wektor e-maili, z którym pracujesz, jest fragmentem bazy e-maili DataCamp. Dlaczego by nie zaoferować właścicielom adresów e-mail z .edu nowego adresu e-mail w domenie datacamp.edu? Edukacja online przejmuje tradycyjne instytucje edukacyjne i może pobudzać wrażenie, że się należy do takiej międzynarodowej grupy subskrybentów/ studentów.
Używając zaawansowanego wyrażenia regularnego “@.edu$”, użyjmy
sub(), aby zastąpić dopasowanie przez
"@datacamp.edu". Ponieważ będzie tylko jedno dopasowanie na
łańcuch znaków, gsub() nie jest tutaj konieczne. Sprawdź
wyniki.
# sub() do zamiany domen na datacamp.edu
sub("@.*\\.edu$", "@datacamp.edu", emails)
## [1] "john.doe@datacamp.edu" "education@world.gov"
## [3] "dalai.lama@peace.org" "invalid.edu"
## [5] "quant@datacamp.edu" "cookie.monster@sesame.tv"
Wyrażenia regularne to typowy obszar programowania, którego się nie nauczysz na pamięć, a bardziej wykonując, popełniając błędy, sprawdzając i oglądając przykłady. Przyjrzyjmy się kilku nowym rzeczom, które zostaną za moment użyte:
.*: Można go odczytać jako „dowolny znak, który jest
dopasowany zero lub więcej razy”.
\\s: spacja, niezbędny “znak ucieczki”
(\\)
[0-9]+: Dopasuj cyfry od 0 do 9 przynajmniej raz
(+).
([0-9]+): Nawiasy służą do udostępniania części
pasującego ciągu w celu zdefiniowania zamiany. \\1 w
argumencie replacement funkcji sub() zostaje
ustawione na ciąg znaków przechwycony przez wyrażenie regularne
[0-9]+.
Powiedzmy, że chcemy w ciągu znaków w poniższym wektorze wyciągnąć informacje o liczbie nominacji. Niedoskonale, ale zawsze lepiej niż ręcznie (pomyślmy o takim wektorze o tysiącu elementach).
awards <- c("Won 1 Oscar.",
"Won 1 Oscar. Another 9 wins & 24 nominations.",
"1 win and 2 nominations.",
"2 wins & 3 nominations.",
"Nominated for 2 Golden Globes. 1 more win & 2 nominations.",
"4 wins & 1 nomination.")
# sub(".*\\s[0-9]+\\snomination.*$", "\\1", awards)
sub(".*\\s([0-9]+)\\snomination.*$", "\\1", awards)
## [1] "Won 1 Oscar." "24" "2" "3" "2"
## [6] "1"
Napisaliśmy w skrócie “dopasuj dowolny ciąg znaków, następnie pojedynczy znak odstępu, jedną lub więcej cyfr, kolejny znak odstępu, słowo ‘nomination’ i dowolny ciąg znaków aż do końca linii”. Czy to naprawdę musi być takie skomplikowane i wymagać znajomości meta znaków RegEx? Otóż, nie!
{rebus}
Motto pakietu: ‘Build Regular Expressions in a Human Readable
Way’
https://cran.r-project.org/web/packages/rebus/index.html
str_view() Przydatna funkcja do testowania własnego
wyrażenia regularnego (https://www.rdocumentation.org/packages/stringr/versions/1.5.0/topics/str_view)
# shortcuts to specify regular expressions that match the start and end of the string
START # <- {rebus} regex -> ^
## <regex> ^
END # <- {rebus} regex -> $
## <regex> $
# a wildcard to match a single character
ANY_CHAR # <- {rebus} regex -> .
## <regex> .
Puśćmy w konsoli poniższe linijki..
str_view(string = PowerRangers,
pattern = START %R%
"Pow")
## [1] │ <Pow>er Rangers Time Force
## [5] │ <Pow>er Rangerrs Ninja Storm
## [6] │ <Pow>er Rrangerrs Super Ninja Steel Ninja
# When you are reading rebus code, think of %R% as "then"
# "the start of string then a Pow"
# or in other words: strings that start with Pow.
# A zatem koniec stringa jak napiszemy?
str_view(string = PowerRangers, pattern = "Rangers" %R% END)
## [3] │ Mighty Morphin Super Alien <Rangers>
pattern’u zawierającego wyrażenie regularne możemy
używać do wszystkich funkcji z rodziny
str_...().
A co w sytuacji, gdy mamy taki wektor, gdzie znak specjalny jest
istotnym elementem naszego wektora?
hajsy <- c("500$", "haj$", "hajs")
# no no no
(pattern_hajsy = "$" %R% END)
## <regex> $$
str_view(string = hajsy, pattern = pattern_hajsy)
## [1] │ 500$<>
## [2] │ haj$<>
## [3] │ hajs<>
# yee yes yes # https://www.youtube.com/watch?v=R5wTQPlHHL4
(pattern_hajsy = DOLLAR %R% END)
## <regex> \$$
str_view(string = hajsy,
pattern = pattern_hajsy)
## [1] │ 500<$>
## [2] │ haj<$>
A jeśli nie wiemy czego szukamy (samo życie), albo szukamy kilku
rzeczy w tym samym czasie? Zobaczmy…
(pattern_fn = or("Force","Ninja"))
## <regex> (?:Force|Ninja)
str_view(string = PowerRangers, pattern = pattern_fn)
## [1] │ Power Rangers Time <Force>
## [5] │ Power Rangerrs <Ninja> Storm
## [6] │ Power Rrangerrs Super <Ninja> Steel <Ninja>
## [7] │ power Rangers Wild <Force>
A co w sytuacji, gdy szukamy wystąpienia pojedynczego znaku i
dodatkowo jesteśmy wrażliwi na wielkość liter (R też w
końcu jest case sensitive)
# char_class -> a way of specifying "match one (and only one) of the following characters"
(pattern_Pp = char_class("Pp"))
## <regex> [Pp]
str_view(string = PowerRangers, pattern = pattern_Pp)
## [1] │ <P>ower Rangers Time Force
## [2] │ Mighty Mor<p>hin <P>ower Rangers 1
## [3] │ Mighty Mor<p>hin Su<p>er Alien Rangers
## [4] │ <p>ower Rangers Turbo
## [5] │ <P>ower Rangerrs Ninja Storm
## [6] │ <P>ower Rrangerrs Su<p>er Ninja Steel Ninja
## [7] │ <p>ower Rangers Wild Force
(pattern_p = char_class("P"))
## <regex> [P]
str_view(string = PowerRangers, pattern = pattern_p)
## [1] │ <P>ower Rangers Time Force
## [2] │ Mighty Morphin <P>ower Rangers 1
## [5] │ <P>ower Rangerrs Ninja Storm
## [6] │ <P>ower Rrangerrs Super Ninja Steel Ninja
# nie zastanawia nas czemu tylko pierwsze wystąpienie pokazuje?
# żeby zobaczyć wszystkie musimy użyć funkcji
(pattern_M = char_class("M"))
## <regex> [M]
str_view(string = PowerRangers, pattern = pattern_M)
## [2] │ <M>ighty <M>orphin Power Rangers 1
## [3] │ <M>ighty <M>orphin Super Alien Rangers
str_view_all(string = PowerRangers, pattern = pattern_M)
## Warning: `str_view_all()` was deprecated in stringr 1.5.0.
## ℹ Please use `str_view()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## [1] │ Power Rangers Time Force
## [2] │ <M>ighty <M>orphin Power Rangers 1
## [3] │ <M>ighty <M>orphin Super Alien Rangers
## [4] │ power Rangers Turbo
## [5] │ Power Rangerrs Ninja Storm
## [6] │ Power Rrangerrs Super Ninja Steel Ninja
## [7] │ power Rangers Wild Force
# Tylko niezaczynające się na literę "p" lub "P" na początku string'a? Wówczas
(pattern_Pp = negated_char_class("Pp"))
## <regex> [^Pp]
str_view(string = PowerRangers, pattern = pattern_M)
## [2] │ <M>ighty <M>orphin Power Rangers 1
## [3] │ <M>ighty <M>orphin Super Alien Rangers
Powtarzalność wzoru:
optional() (regex -> ?)
zero_or_more() (regex -> * )
one_or_more() (regex ->
+)
repeated() (regex -> {m, n}) #
pomiędzy m i n
kolory <- c("niebieski", "różowy", "magenta", "zielony")
str_view(kolory, pattern = or("różowy", "magenta"))
## [2] │ <różowy>
## [3] │ <magenta>
imiona <- c("Angelika", "Patrycja", "Andżelika", "Piotr")
str_view(imiona, pattern = or("Angelika", "Andżelika"))
## [1] │ <Angelika>
## [3] │ <Andżelika>
#alternatywnie, skoro kwestia jednej, dwóch liter
str_view(imiona, pattern ="An" %R% or("g", "dż") %R% "elika", match = TRUE) # ostatni argument spowoduje, że wyświetlą się nam jedynie szukane frazy, przy dużych zbiorach oszczędza przestrzeń
## [1] │ <Angelika>
## [3] │ <Andżelika>
imiona2 <- c("Jeffrey","Jeffery","Geoffrey","Jeffry","Jefferey", "Piotr")
po_elementach <- or("Je", "Geo") %R% "ff" %R% or("ry", "ery", "rey", "erey")
str_view(imiona2, pattern = po_elementach)
## [1] │ <Jeffrey>
## [2] │ <Jeffery>
## [3] │ <Geoffrey>
## [4] │ <Jeffry>
## [5] │ <Jefferey>
vowels <- char_class("aeiouAEIOU")
x <- c("ow", "ooh", "yeeeah!", "shh")
str_view(x, pattern = one_or_more(vowels)) # jedna, dwie, albo wszystkie samogłoski
## [1] │ <o>w
## [2] │ <oo>h
## [3] │ y<eeea>h!
str_view(x, pattern = zero_or_more(vowels)) # zero, albo pierwsze, patrząc od poczatku string'a wystąpienie
## [1] │ <o><>w<>
## [2] │ <oo><>h<>
## [3] │ <>y<eeea><>h<>!<>
## [4] │ <>s<>h<>h<>
Skróty
# używany przed momentem DOLLAR
DOLLAR %R% char_class("0123456789")
## <regex> \$[0123456789]
char_class("0-9") # zakres, zamiast c("0123456789")
## <regex> [0-9]
# jeszcze szybciej, a to samo co powyżej
DGT # == char_class("0-9")
## <regex> \d
# albo chcemy wszystkie małe litery alfabetu łacińskiego
char_class("a-z") # zakres, zamiast c("a", "b", "c", ....) albo letters
## <regex> [a-z]
char_class("A-Z") # zakres, zamiast c("A", "B", "C", ....) albo LETTERS
## <regex> [A-Z]
# jeszcze szybciej, a to samo co powyżej zusammen
WRD # == char_class(a-zA-Z0-9_)
## <regex> \w
SPC # spacja, whitespace character
## <regex> \s
Dygresja, użyta została funkcja charClass().
(liczbyC <- c("0123456789"))
## [1] "0123456789"
# versus
(liczbyCharClass <- char_class("0123456789"))
## <regex> [0123456789]
class(liczbyC)
## [1] "character"
class(liczbyCharClass)
## [1] "regex" "character"
Jest przydatna, gdy potrzebujemy np. szybko i efektywnie sprawdzić, do jakiej klasy należy dany znak lub ciąg znaków. Może to być szczególnie użyteczne w analizie tekstu, walidacji danych, czy przetwarzaniu wejściowych danych użytkownika.
x <- "12345"
if (all(charClass(x, "digit"))) {
print("Wszystkie znaki są cyframi")
} else {
print("Nie wszystkie znaki są cyframi")
}
## [1] "Wszystkie znaki są cyframi"
Więcej w dokumentacji.
Wracając…
Jak to “ugryźć” w praktyce powyższe? Załóżmy, że chcemy
rozpracować taki wektor
(contact <- c("Call me at 555-555-0191",
"123 Main St",
"(555) 555 0191",
"Phone: 555.555.0191 Mobile: 555.555.0192"))
## [1] "Call me at 555-555-0191"
## [2] "123 Main St"
## [3] "(555) 555 0191"
## [4] "Phone: 555.555.0191 Mobile: 555.555.0192"
Numery mają długość 10 znaków i składają się z cyfr, rzut oka na
wektor sugeruje wyciąganie elementów po trzy cyfry
str_view_all(contact, pattern = DGT %R% DGT %R% DGT)
## [1] │ Call me at <555>-<555>-<019>1
## [2] │ <123> Main St
## [3] │ (<555>) <555> <019>1
## [4] │ Phone: <555>.<555>.<019>1 Mobile: <555>.<555>.<019>2
Przeszkadzają nam różne separatory w numerach
str_view_all(contact, pattern = char_class("-.() "))
## [1] │ Call< >me< >at< >555<->555<->0191
## [2] │ 123< >Main< >St
## [3] │ <(>555<)>< >555< >0191
## [4] │ Phone:< >555<.>555<.>0191< >Mobile:< >555<.>555<.>0192
Razem:
# elementy wyrażenia
trzy_cyfry <- DGT %R% DGT %R% DGT
cztery_cyfry <- trzy_cyfry %R% DGT
separator <- char_class("-.() ")
# Wyrażenie
tel_pattern <- optional(OPEN_PAREN) %R%
trzy_cyfry %R%
one_or_more(separator) %R%
trzy_cyfry %R%
zero_or_more(separator) %R%
cztery_cyfry
# testowańsko
str_view_all(contact, pattern = tel_pattern)
## [1] │ Call me at <555-555-0191>
## [2] │ 123 Main St
## [3] │ <(555) 555 0191>
## [4] │ Phone: <555.555.0191> Mobile: <555.555.0192>
str_extract(contact, tel_pattern) # wektor
## [1] "555-555-0191" NA "(555) 555 0191" "555.555.0191"
str_extract_all(contact, tel_pattern) # lista
## [[1]]
## [1] "555-555-0191"
##
## [[2]]
## character(0)
##
## [[3]]
## [1] "(555) 555 0191"
##
## [[4]]
## [1] "555.555.0191" "555.555.0192"
Fragment bazy zawierający opisy przypadków incydentów nagłych
zaraportowany np. przez zespół ZRM. Co możemy wyciągnąć z tych
“narracyjnych” zmiennych, które mają pewną logikę i standard, ale
jeszcze nie są poczyszczone
# # dla starszych wersji R (można instalować wsteczne wersje pakietów i samego R)
# install.packages("neiss")
# library(neiss)
# data(accidents)
# ?accidents
narratives <- c("19YOM-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS",
"31 YOF FELL FROM TOILET HITITNG HEAD SUSTAINING A CHI",
"ANKLE STR. 82 YOM STRAINED ANKLE GETTING OUT OF BED",
"TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*",
"10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB",
"53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION",
"13 MOF TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION",
"14YR M PLAYING FOOTBALL; DX KNEE SPRAIN",
"55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE",
"5 YOM ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN")
(age <- DGT %R% optional(DGT)) # dgt(1, 2)
## <regex> \d[\d]?
(unit <- optional(SPC) %R% or("YO", "YR", "MO"))
## <regex> [\s]?(?:YO|YR|MO)
str_view(narratives,
pattern = age %R% unit) # mamy wiek pacjenta
## [1] │ <19YO>M-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS
## [2] │ <31 YO>F FELL FROM TOILET HITITNG HEAD SUSTAINING A CHI
## [3] │ ANKLE STR. <82 YO>M STRAINED ANKLE GETTING OUT OF BED
## [4] │ TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. <33 YO>F*
## [5] │ <10YO>M 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 MO>F TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION
## [8] │ <14YR> M PLAYING FOOTBALL; DX KNEE SPRAIN
## [9] │ <55YO>M RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE
## [10] │ <5 YO>M ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN
(gender <- optional(SPC) %R% or("M", "F"))
## <regex> [\s]?(?:M|F)
str_view(narratives,
pattern = age %R% unit %R% gender) # uzupełniliśmy płcią
## [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
# wciąż brzydki wektor
(age_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"
# zatem
(ages_numeric <- as.numeric(
str_extract(age_gender, age)
)) # wyciągnięcie wieku i nadanie mu właściwego typu
## [1] 19 31 82 33 10 53 13 14 55 5
(genders <- str_remove(age_gender,
pattern = age %R% unit)) # wyciągnięcie płci
## [1] "M" "F" "M" "F" "M" " F" "F" " M" "M" "M"
(genders <- str_remove_all(genders,
pattern = one_or_more(SPC))) # czyszczenie ze zbędnych złogów białych znaków
## [1] "M" "F" "M" "F" "M" "F" "F" "M" "M" "M"
(time_units <- str_extract(age_gender,
pattern = unit)) # jednostki wieku jeszcze "brudne"
## [1] "YO" " YO" " YO" " YO" "YO" " YO" " MO" "YR" "YO" " YO"
(time_units_clean <- str_extract(time_units,
pattern = WRD))# jednostki wieku "czyste", pierwsza litera jednostki (rok, miesiąc)
## [1] "Y" "Y" "Y" "Y" "Y" "Y" "M" "Y" "Y" "Y"
ifelse(time_units_clean == "Y", ages_numeric, ages_numeric / 12) # zamiana na miesiące, gdybyśmy chcieli ujednolicić jednostkę wieku
## [1] 19.000000 31.000000 82.000000 33.000000 10.000000 53.000000 1.083333
## [8] 14.000000 55.000000 5.000000
data.frame(Wiek = ages_numeric,
Jednostka.wieku = time_units_clean,
Plec = genders)
## Wiek Jednostka.wieku Plec
## 1 19 Y M
## 2 31 Y F
## 3 82 Y M
## 4 33 Y F
## 5 10 Y M
## 6 53 Y F
## 7 13 M F
## 8 14 Y M
## 9 55 Y M
## 10 5 Y M
capture() & str_match()
“A
powerful way for extracting pieces of text”
Zobaczmy jak działa
funkcja na przykładzie…
hero_contacts <- c("wolverine@xmen.com",
"wonderwoman@justiceleague.org",
"thor@avengers.com")
# można ją wrzucić w wyrażenie regularne...
email <- capture(one_or_more(WRD)) %R%
"@" %R%
capture(one_or_more(WRD)) %R%
DOT %R%
capture(one_or_more(WRD))
# ...które nie psuje wyrażenia,
str_view(hero_contacts, email) # podejrzenie poprawności wyrażenia regularnego
## [1] │ <wolverine@xmen.com>
## [2] │ <wonderwoman@justiceleague.org>
## [3] │ <thor@avengers.com>
# ale daje możliwość wyciągania elementów, które chcemy "przechwycić"
(hero_data <- str_match(hero_contacts, email)) # wyciągnięcie matrycy z poszczególnymi elementami
## [,1] [,2] [,3] [,4]
## [1,] "wolverine@xmen.com" "wolverine" "xmen" "com"
## [2,] "wonderwoman@justiceleague.org" "wonderwoman" "justiceleague" "org"
## [3,] "thor@avengers.com" "thor" "avengers" "com"
# bohater
hero_data[,2]
## [1] "wolverine" "wonderwoman" "thor"
# domena
hero_data[,4]
## [1] "com" "org" "com"
# host
hero_data[,3]
## [1] "xmen" "justiceleague" "avengers"
Różnica między one_or_more(WRD) a
capture(one_or_more(WRD)) polega na tym, że:
one_or_more(WRD): To wyrażenie regularne, które
dopasowuje jeden lub więcej wystąpień słowa (WRD). Używane jest do
wyszukiwania ciągów znaków, które zawierają co najmniej jedno
słowo.
capture(one_or_more(WRD)): To wyrażenie regularne,
które nie tylko dopasowuje jeden lub więcej wystąpień słowa (WRD), ale
także przechwytuje (zapamiętuje) te dopasowania. Dzięki temu można
później odwoływać się do tych przechwyconych wartości.
Wróćmy do przykładu z wyłuskiwaniem numerów telefonów
# przypomnienie jak wyglądał wektor
(contact <- c("Call me at 555-555-0191",
"123 Main St",
"(555) 555 0191",
"Phone: 555.555.0191 Mobile: 555.555.0192"))
## [1] "Call me at 555-555-0191"
## [2] "123 Main St"
## [3] "(555) 555 0191"
## [4] "Phone: 555.555.0191 Mobile: 555.555.0192"
# Wyrażenie
tel_pattern <- optional(OPEN_PAREN) %R%
capture(trzy_cyfry) %R%
one_or_more(separator) %R%
capture(trzy_cyfry) %R%
zero_or_more(separator) %R%
capture(cztery_cyfry)
# Pull out the parts with str_match()
(phone_numbers <- str_match(contact, tel_pattern))
## [,1] [,2] [,3] [,4]
## [1,] "555-555-0191" "555" "555" "0191"
## [2,] NA NA NA NA
## [3,] "(555) 555 0191" "555" "555" "0191"
## [4,] "555.555.0191" "555" "555" "0191"
# wyciąganie (sklejenie) numerów w formacie (XXX) XXX-XXXX
str_c("(", phone_numbers[,2], ")", phone_numbers[,3], "-", phone_numbers[,4])
## [1] "(555)555-0191" NA "(555)555-0191" "(555)555-0191"
# sięgając po wszystkie numery musielibyśmy użyć str_match_all(), otrzymując listę
str_match_all(contact, tel_pattern)
## [[1]]
## [,1] [,2] [,3] [,4]
## [1,] "555-555-0191" "555" "555" "0191"
##
## [[2]]
## [,1] [,2] [,3] [,4]
##
## [[3]]
## [,1] [,2] [,3] [,4]
## [1,] "(555) 555 0191" "555" "555" "0191"
##
## [[4]]
## [,1] [,2] [,3] [,4]
## [1,] "555.555.0191" "555" "555" "0191"
## [2,] "555.555.0192" "555" "555" "0192"
# ?Unicode
# ?unicode_property
# ?unicode_general_category
"\1F600" # -> :)
## [1] "\001F600"
"\u03BC" # -> μ
## [1] "μ"
as.hexmode(utf8ToInt("μ"))
## [1] "3bc"
(formula <- "Normal(\u03BC = 0, \u03C3 = 1)")
## [1] "Normal(μ = 0, σ = 1)"
Przypomnienie:
str_detect() -> zwraca prawdę lub fałsz gdy napotka
na wzór odpowiadający wyrażeniu
str_match() -> jeśli wzór wyrażenia pasuje, zwróci
nam je
Sprawdźmy działanie specjalnych symboli.
str_match("Michał", "^.")
## [,1]
## [1,] "M"
str_match("Michał", ".$")
## [,1]
## [1,] "ł"
str_match("Michał", "\\.")
## [,1]
## [1,] NA
str_match("Michał.", "\\.") # dodana kropka w
## [,1]
## [1,] "."
Pakiety: {lubridate}, {zoo},
{xts}
W R, daty są reprezentowane przez
obiekty Date, podczas gdy godziny są
reprezentowane przez obiekty POSIXct. Jednak pod maską, te
daty i godziny są prostymi wartościami numerycznymi. Obiekty
Date przechowują liczbę dni od 1 stycznia 1970 roku. Z
drugiej strony, obiekty POSIXct przechowują liczbę sekund
od 1 stycznia 1970 roku.
1 stycznia 1970 roku jest powszechnym źródłem reprezentacji czasu i daty w wielu językach programowania. Nie ma ku temu szczególnego powodu; jest to prosta konwencja. Oczywiście możliwe jest również tworzenie dat i godzin przed 1970 rokiem; odpowiednie wartości liczbowe są w tym przypadku po prostu ujemne.
(today <- Sys.Date())
## [1] "2025-12-01"
class(today) # under the hood
## [1] "Date"
(now <- Sys.time())
## [1] "2025-12-01 15:27:36 CET"
class(now) # under the hood
## [1] "POSIXct" "POSIXt"
(my_birth <- as.Date("1985-14-02", format = "%Y-%d-%m"))
## [1] "1985-02-14"
today - my_birth
## Time difference of 14900 days
Aby utworzyć obiekt Date z prostego ciągu znaków w R, można użyć funkcji as.Date(). Ciąg znaków musi być zgodny z formatem, który można zdefiniować za pomocą zestawu symboli (przykłady odpowiadają 13 stycznia 1982 r.):
%Y: 4-digit year (1982)%y: 2-digit year (82)%m: 2-digit month (01)%d: 2-digit day of the month (13)%A: weekday (Wednesday)%a: abbreviated weekday (Wed)%B: month (January)%b: abbreviated month (Jan)
Poniższe polecenia R utworzą ten sam obiekt Date dla 13 dnia
stycznia 1982 roku:
as.Date("1982-01-13")
as.Date("Jan-13-82", format = "%b-%d-%y")
as.Date("13 January, 1982", format = "%d %B, %Y")
Zauważmy, że pierwsza linia tutaj nie wymagała argumentu formatu,
ponieważ domyślnie R dopasowuje ciąg znaków do formatów
"%Y-%m-%d" lub "%Y/%m/%d".
Oprócz tworzenia dat można je także konwertować na ciągi znaków
korzystające z innego zapisu daty. W tym celu użyj funkcji
format(). Wypróbuj następujące linie kodu:
today <- Sys.Date()
format(Sys.Date(), format = "%d %B, %Y")
format(Sys.Date(), format = "Today is a %A!")
# Stringi reprezentujące daty
str1 <- "May 23, '96"
str2 <- "2012-03-15"
str3 <- "30/January/2006"
# Konwersja na daty sensu stricte
date1 <- as.Date(str1, format = "%b %d, '%y")
date2 <- as.Date(str2)
date3 <- as.Date(str3, format = "%d/%B/%Y")
# Wyciąganie z dat interesujących informacji
format(date1, "%A")
## [1] "Thursday"
format(date2, "%d")
## [1] "15"
format(date3, "%b %Y")
## [1] "Jan 2006"
Podobnie jak w przypadku dat, można użyć metody as.POSIXct() do
konwersji ciągu znaków na obiekt POSIXct oraz metody format() do
konwersji obiektu POSIXct na ciąg znaków. Ponownie mamy szeroką gamę
symboli:
%H: hours as a decimal number (00-23)%I: hours as a decimal number (01-12)%M: minutes as a decimal number%S: seconds as a decimal number%T: shorthand notation for the typical format
%H:%M:%S%p: AM/PM indicator
Pełną listę symboli konwersji znajdziesz w dokumentacji strptime
w konsoli:
?strptime
as.POSIXct() używa domyślnego formatu do dopasowywania
ciągów znaków. W tym przypadku jest to
%Y-%m-%d %H:%M:%S.
# Definition of character strings representing times
str1 <- "May 23, '96 hours:23 minutes:01 seconds:45"
str2 <- "2012-3-12 14:23:08"
# Convert the strings to POSIXct objects: time1, time2
time1 <- as.POSIXct(str1, format = "%B %d, '%y hours:%H minutes:%M seconds:%S")
time2 <- as.POSIXct(str2)
# Convert times to formatted strings
format(time1, "%M")
## [1] "01"
format(time2, "%I:%M %p")
## [1] "02:23 PM"
(today <- Sys.Date())
## [1] "2025-12-01"
today + 1
## [1] "2025-12-02"
today - 1
## [1] "2025-11-30"
as.Date("2015-03-12") - as.Date("2015-02-27")
## Time difference of 13 days
Źródła i inspiracje pomocne w przygotowaniu powyższej prezentacji:
atacamp.com String Manipulation with stringr in R
datacamp.com Intermediate Regular Expressions in R
datacamp.com Data Manipulation with data.table in R
https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html
datacamp.com Introduction to the Tidyverse
datacamp.com Data Manipulation with dplyr
Materiały z warsztatów Data Science w zastosowaniach biznesowych - Warsztaty z wykorzystaniem programu R Uniwersytet Warszawski, Wydział Nauk Ekonomicznych
DataCamp.com Introduction to R
DataCamp.com Writing Efficient R Code
Eugene O’Loughlin tutorial
Materiały dr Bartosza Maćkiewicza:
Biecek, Przemysław, “Przewodnik po pakiecie R” http://www.biecek.pl/R/ https://cran.r-project.org/doc/contrib/Biecek-R-basics.pdf
Gągolewski, Marek, “Programowanie w języku R”, “Deep R Programming”, etc. https://deepr.gagolewski.com/index.html
Crawley, Michael J. The R book. John Wiley & Sons, 2012 https://onlinelibrary.wiley.com/doi/book/10.1002/9781118448908
Kabacoff, Robert. R in action: data analysis and graphics with R. Manning Publications Co., 2015 https://www.cs.uni.edu/~jacobson/4772/week11/R_in_Action.pdf
Fox, John, Michael Friendly, and Sanford Weisberg. “Hypothesis tests for multivariate linear models using the car package.” R J 5.1 (2013) https://www.researchgate.net/publication/285736465_Hypothesis_Tests_for_Multivariate_Linear_Models_Using_the_car_Package
Hothorn, Torsten, et al. “Package ‘multcomp’.” Simultaneous inference in general parametric models. Project for Statistical Computing, Vienna, Austria (2016) https://cran.r-project.org/web/packages/multcomp/vignettes/generalsiminf.pdf
claude.ai
chatgpt.com
Wektor potrzebny w trakcie ćwiczeń…
movie_titles <- c("Karate Kid","The Twilight Saga: Eclispe","Knight & Day",
"Shrek Forever After 3D", "Marmaduke.","Street Dance",
"Predators","StreetDance 3D", "Robin Hood",
"Micmacs A Tire-Larigot", "50 Shades of Grey",
"Sex And the City 2", "Inception","The Dark Knight",
"300","Toy Story 3 In Disney Digital 3D",
"50 Shades of Gray","Italien, Le",
"Tournee","The A-Team", "El Secreto De Sus Ojos","Kiss & Kill",
"The Road","Cosa Voglio Di Piu",
"Nur für dich","Prince Of Persia: The Sands Of Time",
"Saw 4","Saw 5", "Saw 6","21 Grams" )