Rkový skript ke stažení: R
Co si zde představíme?
character
,character
Se stringy jsme se už setkali. Ohraničujeme je pomocí uvozovek, čímž
v Rku vzniká objekt třídy character
:
x <- "text"
class(x)
## [1] "character"
is.character(x) # ověření, zda se jedná
## [1] TRUE
y <- character(5) # vytvoření vektoru s prázdnými stringy
length(x); length(y) # délka vektoru
## [1] 1
## [1] 5
nchar(x); nchar(y) # délky jednotlivých stringů
## [1] 4
## [1] 0 0 0 0 0
Čísla lze konvertovat na text, obráceně jen tehdy, vznikne-li srozumitelné číslo:
as.character(123.456) # konverze čísla na string
## [1] "123.456"
as.numeric("123.456") # string na číslo
## [1] 123.456
as.numeric("123,456") # nebere desetinnou čárku a vrací NA!
## Warning: NAs introduced by coercion
## [1] NA
format(123.456, digits = 4) # další způsob s kontrolou počtu platných číslic (digits)
## [1] "123.5"
format(123.456, digits = 4, nsmall = 2) # nsmall = minimální počet desetinných míst
## [1] "123.46"
format(123.456, decimal.mark = ",") # desetinná čárka pro český text
## [1] "123,456"
format(123.456, scientific = TRUE) # vědecký zápis čísla x.xxx * 10^{to za e}
## [1] "1.23456e+02"
format(c(123.4, 0.0256), digits = 2) # pro srovnání na stejný počet desetinných míst zadat jako vektor
## [1] "123.400" " 0.026"
Na stringy také funguje řazení dle abecedy:
"a" < "b"
## [1] TRUE
c("A" < "b", "a" < "B") # zjevně ignoruje velikost písmen
## [1] TRUE TRUE
"Adam" < "Aaron" # samozřejmě srovnává delší slova dle abecedy
## [1] FALSE
"chleba" < "emental" # při nastavení češtiny rozumí zařazení "ch"
## [1] FALSE
c("b"<"ch", "c"<"ch", "c"<"d", "ch"<"d", "ch"<"i", "ch" < "cecilie")
## [1] TRUE TRUE TRUE FALSE TRUE FALSE
Sys.setlocale("LC_ALL", "en")
## [1] "LC_COLLATE=en;LC_CTYPE=en;LC_MONETARY=en;LC_NUMERIC=C;LC_TIME=en"
"chleba" < "emental"
## [1] TRUE
"ďábel" < "řehoř" # v angličtině neznámé symboly
## [1] NA
Sys.setlocale("LC_ALL", "czech.utf8")
## [1] "LC_COLLATE=Czech_Czechia.utf8;LC_CTYPE=Czech_Czechia.utf8;LC_MONETARY=Czech_Czechia.utf8;LC_NUMERIC=C;LC_TIME=Czech_Czechia.utf8"
"chleba" < "emental"
## [1] FALSE
Předefinované vektory velkých a malých písmen:
letters
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
LETTERS
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
Začněme s transformacemi daného stringu:
x <- "MiXeD cAsE 123"
tolower(x)
## [1] "mixed case 123"
toupper(x)
## [1] "MIXED CASE 123"
casefold(x, upper = TRUE) # funkce, která umí obojí
## [1] "MIXED CASE 123"
casefold(x, upper = FALSE) # jen je třeba upravit druhý argument
## [1] "mixed case 123"
# Character translation - výměna znaku za znak
chartr("iXs", "why", x) # kde byly symboly i, X, s, tam teď budou w, h, y
## [1] "MwheD cAyE 123"
chartr("a-cX1", "D-Fw9", x) # rozlišuje velká a malá písmena, zvládá i čísla a jiné symboly
## [1] "MiweD FAsE 923"
Spojování stringů:
# "a" + "b" # bohužel nefunguje jako v Pythonu
paste("a", "b") # automaticky přidává mezeru mezi jednotlivými stringy
## [1] "a b"
paste0("a", "b") # bez mezery
## [1] "ab"
paste(c("a", "b")) # vektorově funguje po složkách, tedy zde se nic nestane
## [1] "a" "b"
paste(letters[1:2], "c") # ke každé složce přilepí ten samý
## [1] "a c" "b c"
paste(letters[1:2], letters[3:4]) # po složkách
## [1] "a c" "b d"
paste(letters[1:2], letters[3:4], letters[5:6], sep="...") # sep udává, čím se mají slepit k sobě
## [1] "a...c...e" "b...d...f"
paste(letters[1:6], collapse=" + ") # z vektoru do jednoho jediného stringu pomocí collapse
## [1] "a + b + c + d + e + f"
Oddělování stringů:
(lslova <- strsplit("Adam šel za Boženou na oběd.", split = " ")) # oddělení mezarami na jednotlivá slova
## [[1]]
## [1] "Adam" "šel" "za" "Boženou" "na" "oběd."
(lcisla <- strsplit(c("123,456", "0,456", "1,2"), split = ",")) # oddělení čísel před a za desetinnou čárkou
## [[1]]
## [1] "123" "456"
##
## [[2]]
## [1] "0" "456"
##
## [[3]]
## [1] "1" "2"
# Vrací vždy jako list vektorů
(slova <- unlist(lslova)) # jednotlivá slova ve vektoru
## [1] "Adam" "šel" "za" "Boženou" "na" "oběd."
(cisla <- matrix(as.numeric(unlist(lcisla)), nrow = 2)) # 1.ř. jednotky, 2.ř. desetinná místa
## [,1] [,2] [,3]
## [1,] 123 0 1
## [2,] 456 456 2
Část stringu:
tel <- "+420 123 456 789"
substr(tel, start = 6, stop = nchar(tel)) # jen část tel. čísla po předvolbě
## [1] "123 456 789"
months <- c("January", "February", "March")
substr(months, start = 1, stop = 3) # jen první tři písmena měsíců
## [1] "Jan" "Feb" "Mar"
Substituce stringů:
chartr(".", ",", "123.456") # znak za znak, více ukázek výše
## [1] "123,456"
sub("@", "[at]", "vavraj@karlin.mff.cuni.cz") # změna za více znaků
## [1] "vavraj[at]karlin.mff.cuni.cz"
sub("mathrm", "mathsf", "\\mathrm{d} x \\mathrm{d} y") # sub() mění jen PRVNÍ výskyt
## [1] "\\mathsf{d} x \\mathrm{d} y"
gsub("mathrm", "mathsf", "\\mathrm{d} x \\mathrm{d} y") # gsub() mění KAŽDÝ výskyt
## [1] "\\mathsf{d} x \\mathsf{d} y"
Vyhledávání, které stringy obsahují daný pattern
:
coef_names <- c("(Intercept)", "age", "gender", "height", "age:gender", "age:height", "gender:height")
(cisla_stringu_s_age <- grep("age", coef_names)) # grep zde vrací čísla stringů obsahujících "age"
## [1] 2 5 6
(obsahuje_age <- grepl("age", coef_names)) # grepl vrací vektor logických hodnot (T/F)
## [1] FALSE TRUE FALSE FALSE TRUE TRUE FALSE
coef_names[cisla_stringu_s_age] # obojí se dá použít k výběru těch stringů
## [1] "age" "age:gender" "age:height"
coef_names[obsahuje_age] # i pomocí T/F se dá vybírat podvektor
## [1] "age" "age:gender" "age:height"
Tohle je samo o sobě poměrně velmi obsáhlá kapitola přesahující i Rko. Zde si ukážeme jen nějaké ty nejpoužívanější příkazy či na co si dát pozor. Pro podrobnější návody stačí googlit a naleznete hned několik návodů v angličtině.
Funkce v Rku, které mají argument pattern
jako například
grep
, grepl
, sub
,
gsub
, mohou přijímat konkretizovanější specifikace, přes
meta charactery:
"."
- Match any single character (except a line
break)"?"
- Match the preceding item 0 or 1 times"+"
- Match the preceding item 1 or more times"*"
- Match the preceding 0 or more times"^"
- Match the start of a string"$"
- Match the end of a string"-"
- Create a range in a character class"|"
- The “or” operator"[]"
- Create a character class"()"
- Create a group"{}"
- Create a quantifier"\"
- Escape a special characterUkážeme si to na příkladech:
slova
## [1] "Adam" "šel" "za" "Boženou" "na" "oběd."
sub(".", "", slova) # neodstraní nám tečky, jak bychom čekali, ale odstraní hned první znaky
## [1] "dam" "el" "a" "oženou" "a" "běd."
sub("\\.", "", slova) # \. slouží jako tečka, ale jelikož je "\" taky meta character, tak je třeba navíc
## [1] "Adam" "šel" "za" "Boženou" "na" "oběd"
slova[grep("a", slova)] # slova obsahující "a"
## [1] "Adam" "za" "na"
slova[grep("^A", slova)] # slova začínající na "A"
## [1] "Adam"
slova[grep("a$", slova)] # slova končící na "a"
## [1] "za" "na"
slova[grep("[0-9]", slova)] # slova obsahující číslice
## character(0)
slova[grep("^[a-o]", slova)]# slova začínající na malá písmena z první půlky abecedy
## [1] "na" "oběd."
slova[grep("^([a-o]|[A-O])", slova)] # slova začínající na písmena z první půlky abecedy (nehledě na velikost)
## [1] "Adam" "Boženou" "na" "oběd."
Chceme-li skutečně použít nějaký meta character, musíme před něj
položit "\\"
:
coef_names[grep("^\\(", coef_names)]
## [1] "(Intercept)"
ukazka <- c(paste0("a", 1:5), paste0("a[", 1:5, "]"))
ukazka[grep("[0-2]", ukazka)]
## [1] "a1" "a2" "a[1]" "a[2]"
ukazka[grep("\\[[0-2]\\]", ukazka)]
## [1] "a[1]" "a[2]"
Už víme, jak si nastavit pracovní adresář. Konkrétně právě pracujeme zde:
(WD <- getwd()) # celá cesta k adresáři
## [1] "M:/Vyuka/Rko/cviceni"
dirname(WD) # jak se k adresáři dostat
## [1] "M:/Vyuka/Rko"
basename(WD) # jméno tohoto adresáře
## [1] "cviceni"
Podívejme se, co je zde vše uložené za soubory:
dir(getwd())
list.files(getwd()) # jen soubory
list.files(getwd(), pattern="R$") # jen soubory končící na R
list.dirs(getwd()) # jen adresáře
S adresáři můžeme pracovat následovně:
TXT <- paste0(dirname(WD), "/txt") # adresář pro textové soubory
# Nepraktické - záleží na tom, co je oddělovač adresářů, zde "/"
# Praktičtěji - file.path doplní oddělovač sám mezi všechny dané stringy
TXT <- file.path(dirname(WD), "txt") # pospojuje za sebe
dir.exists(TXT) # Existuje následující adresář?
dir.create(TXT) # vytvoří adresář, pokud ještě neexistuje
dir.create(TXT) # podruhé vrátí warning, že už takový adresář existuje
V adresáři můžeme pomocí Rka pracovat se soubory následovně (více
help(file.create)
):
file.path(TXT, "test.txt") # nová cesta k souboru
file.create(file.path(TXT, "test.txt")) # vytvoření nového souboru
file.exists(file.path(TXT, "test.txt")) # ověření existence souboru
file.rename(file.path(TXT, "test.txt"), file.path(TXT, "priklad1.txt")) # přejmenování souboru
file.copy(file.path(TXT, "priklad1.txt"), file.path(TXT, "priklad2.txt")) # okopírování souboru
cat("1 + 1 = 2\n", file = file.path(TXT, "priklad1.txt")) # připsání do souboru 1
cat("2 + 2 = 4\n", file = file.path(TXT, "priklad2.txt")) # připsání do souboru 2
file.append(file.path(TXT, "priklad1.txt"), file.path(TXT, "priklad2.txt")) # připsání z 2 do 1
file.remove(file.path(TXT, "priklad2.txt")) # odstranění souboru
Pokud se nám nechce neustále opakovat, do kterého souboru to
vepisujeme, lze použít sink()
:
sink(file.path(TXT, "priklad1.txt")) # otevření souboru
{
print("1+1=2") # print nebude fungovat hezky (jako v Console)
cat("1+2=3\n") # zapisujeme ideálně pomocí cat
cat("1+3=4\n") # \n je symbol pro nový řádek
cat("1+4=5\n")
cat("1+5=6\n")
}
sink() # uzavření souboru - NEZAPOMENOUT!
K čemu to je dobré? Můžeme si třeba vyrobit vlastní LaTeXové tabulky s čísly přímo z Rka! Změnilo se ti něco na výpočtech? Nevadí! Jednoduše projdi kód na výrobu tabulky znovu jen s novými čísly.
TAB <- file.path(dirname(WD), "tab") # nový adresář pro tabulky
n <- c(100, 200, 300)
x <- rnorm(length(n)) # náhodná čísla
y <- rnorm(length(n)) # je jich tolik, kolik je prvků v n
dir.create(TAB)
sink(file.path(TAB, "jednoducha_tabulka.tex")) # otevření souboru
{
cat("\\begin{tabular}{lcc}\n") # \ slouží opět jako escape character --> třeba zdvojit
cat("\\hline\n") # horizontální linka
cat("$n$ & Metoda 1 & Metoda 2 \\\\\n") # nadpis
cat("\\hline\n") # horizontální linka
for(i in 1:length(n)){
cat(n[i], "&", round(x[i], digits = 3), "&", round(y[i], digits = 3), "\\\\\n") # jeden řádek tabulky
}
cat("\\hline\n") # horizontální linka
cat("\\end{tabular}") # konec tabulky
}
sink() # uzavření souboru
Může se stát, že hodnoty v jednom sloupci nejsou se stejným počtem
desetinných míst (což je zvykem). Proto by nejprve měly všechny hodnoty
x
a y
spolu projít funkcí format
,
která to sjednotí.
Teď už jen stačí vložit tento soubor do texového souboru pomocí
\input{tab/jednoducha_tabulka.tex}
a je to.
Vyřešte si to sami, aniž byste se dívali do řešení (úplně dole vespod skriptu).
Předpokládejte, že ve vektoru y
máte jména
vysvětlovaných proměnných (odezev). Ve vektoru x
máte
seznam vysvětlujících proměnných. Pro každou odezvu vytvořte formuli
následujícího tvaru y_1 ~ x_1 + ... + x_n
, kde
y_1
značí první název odezvy a x_j
značí
j
-tou vysvětlující proměnnou. Poraďte si bez
for
cyklu.
Uspořádejte si složku pro toto cvičení přehledně tak, aby Vám to vyhovovalo. Například si založte zvláštní adresáře pro
Nadefinujte si také proměnné, kterými se do těchto adresářů dostanete.
Máte k dispozici dva stringy, ve kterých jsou čárkami oddělené e-mailové adresy uživatelů (vytvořte si sami, třeba náhodně). Přetvořte to ve dva vektory e-mailových adres. Utvořte opět dlouhé stringy e-mailových adres oddělených čárkami, které obsahují průnik, sjednocení (unikátní) a rozdíly množin adres.
Vytvořte LaTeXový kód pro následující tabulku a uložte jej do tabulkového adresáře pod “tabulka.tex”. Začněte s hrubým nástinem a postupně přidávejte na detailech:
Pro jednoduchost předpokládejte, že čísla máte daná v náhodně
vygenerovaných maticích X
a Y
, a to pro každé
\(K\) zvlášť:
X <- Y <- list()
Ks <- c(2, 3)
scenare <- LETTERS[1:3]
ns <- c(50,100,200,500,1000)
for(k in Ks){
X[[k]] <- Y[[k]] <- matrix(nrow = length(ns)*length(scenare), ncol = 2)
i <- 0
for(scenar in scenare){
for(n in ns){
i <- i + 1
smpl <- rnorm(n, mean = k, sd = which(scenar == LETTERS))
X[[k]][i,] <- c(mean(smpl), sd(smpl))
smpl <- k + rt(n, df = 5+which(scenar == LETTERS))
Y[[k]][i,] <- c(mean(smpl), sd(smpl))
}
}
colnames(X[[k]]) <- colnames(Y[[k]]) <- c("mean", "sd")
}
X
Y