Rkový skript ke stažení: R
Co si zde představíme?
factor
a jak s ní pracovat.factor
Už jsme se naučili pracovat s čísly a stringy. Ale co když máme jen
pár vybraných čísel či slov, kterých mohou naše proměnné nabývat? Jak s
nimi pracovat jako s kategoriemi? Na to je v Rku právě třída
factor
:
slova <- c("hodne", "stredne", "malo", "malo", "malo", "stredne", "hodne")
cisla <- c(2, 1, 0, 0, 0, 1, 2)
unique(slova) # unikátní stringy (srovnané dle prvního výskytu)
## [1] "hodne" "stredne" "malo"
unique(cisla) # unikátní čísla (srovnané dle prvního výskytu)
## [1] 2 1 0
(fslova <- factor(slova)) # jako factor o 3 kategoriích dle abecedy
## [1] hodne stredne malo malo malo stredne hodne
## Levels: hodne malo stredne
(fcisla <- factor(cisla)) # jako factor o 3 kategoriích dle uspořádání čísel
## [1] 2 1 0 0 0 1 2
## Levels: 0 1 2
vicedruhu <- c("a", 1, "a", 2, "c") # už tady si převede čísla na stringy (naopak nelze)
factor(vicedruhu)
## [1] a 1 a 2 c
## Levels: 1 2 a c
Čím je faktorová proměnná užitečná? Mnohé funkce v Rku reagují na
takovouto proměnnou jiným způsobem, ku příkladu funkce
summary
:
summary(slova) # vektor o délce 7 plný prvků třídy character
## Length Class Mode
## 7 character character
summary(fslova) # četnosti jednotlivých kategorií
## hodne malo stredne
## 2 3 2
summary(cisla) # základní popisné charakteristiky vektoru číselných dat
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 1.0000 0.8571 1.5000 2.0000
summary(fcisla) # četnosti jednotlivých kategorií
## 0 1 2
## 3 2 2
Ke kategoriím se lze dostat z existujícího faktoru následovně:
levels(fslova) # získání vektoru kategorií
## [1] "hodne" "malo" "stredne"
nlevels(fslova) # počet kategorií factoru, alternativně: # length(levels(factor))
## [1] 3
R-ko s faktorovou proměnnou interně pracuje jako s čísly od 1 do počtu kategorií:
as.numeric(fslova) # kategorie jako čísla
## [1] 1 3 2 2 2 3 1
as.numeric(fcisla) # POZOR i čísla si to přečísluje na 1, 2, ..., nlevels!
## [1] 3 2 1 1 1 2 3
as.numeric(as.character(fcisla)) # takhle lze získat původní čísla zpět
## [1] 2 1 0 0 0 1 2
Pořadí kategorií bývá často velmi důležité, tak si ukažme, jak ho
upravit pomocí levels
:
factor(slova) # dle abecedy
## [1] hodne stredne malo malo malo stredne hodne
## Levels: hodne malo stredne
factor(slova, levels = unique(slova)) # dle pořadí výskytu
## [1] hodne stredne malo malo malo stredne hodne
## Levels: hodne stredne malo
factor(slova, levels = c("malo", "stredne", "hodne")) # manuální řazení (je třeba napsat kategorie správně)
## [1] hodne stredne malo malo malo stredne hodne
## Levels: malo stredne hodne
factor(slova, levels = c("m", "Málo", "str", "hodne")) # hodnota nematchuje nic z levels --> NA
## [1] hodne <NA> <NA> <NA> <NA> <NA> hodne
## Levels: m Málo str hodne
Existuje ještě druhý způsob pomocí funkce relevel
, která
posune zvolenou kategorii na první místo, což může být užitečné pro
regresní modely, ale pořadí ostatních zůstane nezměněné:
relevel(fslova, "malo")
## [1] hodne stredne malo malo malo stredne hodne
## Levels: malo hodne stredne
Chcete-li ono pořadí zafixovat a dát Rku vědět, že existuje nějaké
pořadí mezi kategoriemi, tak přidejte ordered = TRUE
:
ofslova <- factor(slova, levels = c("malo", "stredne", "hodne"), ordered = TRUE)
class(fslova)
## [1] "factor"
class(ofslova)
## [1] "ordered" "factor"
ofslova
## [1] hodne stredne malo malo malo stredne hodne
## Levels: malo < stredne < hodne
min(ofslova) # min, max lze provádět s ordered ale ne s běžným factor
## [1] malo
## Levels: malo < stredne < hodne
quantile(ofslova, type = 1, probs = 0.4) # i výběrové kvantily lze provádět jen s ordered factorem
## 40%
## malo
## Levels: malo < stredne < hodne
Jak přejmenovávat kategorie pomocí argumentu labels
:
fslova_hezky <- factor(slova, ordered = TRUE,
levels = c("malo", "stredne", "hodne"), # seřazené hodnoty vyskytující se v datech
labels = c("Málo", "Středně", "Hodně")) # nové popisky kategorií
summary(fslova_hezky)
## Málo Středně Hodně
## 3 2 2
fcisla_hezky <- factor(cisla, ordered = TRUE,
levels = 0:2, # seřazené hodnoty vyskytující se v datech
labels = c("Ne", "Možná", "Ano")) # nové popisky kategorií
summary(fcisla_hezky)
## Ne Možná Ano
## 3 2 2
# Pokud už máme existující `factor`, lze to udělat také následovně:
levels(fslova) # současné kategorie factoru
## [1] "hodne" "malo" "stredne"
levels(fslova) <- c("Hodně", "Málo", "Středně") # nové kategorie TOHO SAMÉHO factoru
summary(fslova) # upravené fslova
## Hodně Málo Středně
## 2 3 2
Jak sjednocovat kategorie:
fcisla_anone <- factor(cisla, ordered = TRUE,
levels = 0:2, # seřazené hodnoty vyskytující se v datech
labels = c("Ne", "Ano", "Ano")) # stačí dát stejnou nálepku dvěma různým hodnotám
summary(fcisla_anone)
## Ne Ano
## 3 4
# Nebo opět pomocí přejmenování kategorií již existujícího factoru
levels(fcisla) <- c("Ne", "Ano", "Ano")
summary(fcisla) # upravené fcisla
## Ne Ano
## 3 4
Jak ignorovat kategorie:
fcisla_anone <- factor(cisla, ordered = TRUE,
levels = c(0,2), # jen vybrané hodnoty
labels = c("Ne", "Ano")) # nové popisky kategorií
summary(fcisla_anone) # z ignorovaných kategorií se stane NA
## Ne Ano NA's
## 3 2 2
Jak z numerické proměnné udělat kategoriální:
realne <- c(-1.2, 3.4, 2.0, -4.3, -5.0, 0.2, 1.7, 4.8, 5.0, 0.9, -0.7, 0.3, -0.6)
summary(realne)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -5.0 -0.7 0.3 0.5 2.0 5.0
cisla_jako_kategorie <- factor(realne) # každé vyskytující se číslo má svou kategorii
summary(cisla_jako_kategorie)
## -5 -4.3 -1.2 -0.7 -0.6 0.2 0.3 0.9 1.7 2 3.4 4.8 5
## 1 1 1 1 1 1 1 1 1 1 1 1 1
intervaly <- cut(realne, breaks = c(-1, 0, 1)) # funkce cut() třídí do intervalů dle breaks
summary(intervaly) # nestačí dát jen oddělovače
## (-1,0] (0,1] NA's
## 2 3 8
intervaly <- cut(realne, breaks = c(-5, -1, 0, 1, 5)) # 4 kategorie -> 4+1 hodnot v breaks
summary(intervaly) # chybí ta nejmenší hodnota
## (-5,-1] (-1,0] (0,1] (1,5] NA's
## 2 2 3 5 1
intervaly <- cut(realne, breaks = c(-5, -1, 0, 1, 5),
include.lowest = TRUE) # zahrne i nejmenší hodnotu
summary(intervaly)
## [-5,-1] (-1,0] (0,1] (1,5]
## 3 2 3 5
intervaly <- cut(c(realne, -Inf), # pro demonstraci si do hodnot přidejme i -Inf
breaks = c(-Inf, -1, 0, 1, Inf)) # takto každé číslo někam zapadne
summary(intervaly) # výjimkou je právě hodnota -Inf
## (-Inf,-1] (-1,0] (0,1] (1, Inf] NA's
## 3 2 3 5 1
intervaly <- cut(realne, breaks = c(-5, -1, 0, 1, 5),
right = FALSE, include.lowest = TRUE) # uzavřené zleva, horní také zahrnuta
summary(intervaly)
## [-5,-1) [-1,0) [0,1) [1,5]
## 3 2 3 5
intervaly <- cut(realne, breaks = c(-5, -1, 0, 1, 5), # vzniklé kategorie si lze rovnou pojmenovat
labels = c("Rozhodně ne", "Spíše ne", "Spíše ano", "Rozhodně ano"),
include.lowest = T, ordered_result = T) # ulož jako ordered factor
summary(intervaly)
## Rozhodně ne Spíše ne Spíše ano Rozhodně ano
## 3 2 3 5
Jak factor
funguje za přítomnosti NA
?
int_s_NA <- c(NA, intervaly, NA)
fint_s_NA <- factor(int_s_NA) # ignorování NA hodnot přes defaultní "exclude = NA"
summary(fint_s_NA)
## 1 2 3 4 NA's
## 3 2 3 5 2
nlevels(fint_s_NA) # NA nemají vlastní kategorii
## [1] 4
fint_s_NA <- addNA(fint_s_NA) # NA jako dodatečné kategorie (jen pokud víme, co to znamená!)
nlevels(fint_s_NA)
## [1] 5
nlevels(addNA(intervaly)) # přidává kategorii NA nehledě na to, zda tam jsou (ifany = FALSE)
## [1] 5
nlevels(addNA(intervaly, ifany = TRUE)) # přidává kategorii NA jen pokud, tam nějaké je
## [1] 4
Vyřešte si to sami, aniž byste se dívali do řešení (úplně dole vespod skriptu).
# Výroba náhodných e-mailových adres (nepovinná část, klidně okopírujte a pokuste se pochopit)
samo <- letters[c(1,5,9,15,21,25)]
souh <- setdiff(letters, samo)
ends <- c("gmail.com", "seznam.cz", "email.cz")
n <- 100 # počet různých adres
adresy <- character(n)
for(i in 1:n){
pocet_slabik <- sample(2:6, 1)
adresy[i] <- ""
for(j in 1:pocet_slabik){
adresy[i] <- paste0(adresy[i], sample(souh, 1), sample(samo, 1))
}
if(sample(0:1,1)){
adresy[i] <- paste0(adresy[i], paste0(sample(0:9, sample(1:3, 1), replace = T), collapse = ""))
}
adresy[i] <- paste0(adresy[i], "@", sample(ends, 1, prob = c(0.55, 0.4, 0.05)))
}
adresy
## [1] "nyfi@seznam.cz" "myzyboninuqu@gmail.com" "qitydecopu@seznam.cz"
## [4] "zohigeva@seznam.cz" "gujykyvideny6@seznam.cz" "pekiky2@gmail.com"
## [7] "jibyqonelo@seznam.cz" "turevu@gmail.com" "kacofe2@seznam.cz"
## [10] "viquxebyva@gmail.com" "dyca66@gmail.com" "xowywazumu@seznam.cz"
## [13] "tire8@seznam.cz" "live@gmail.com" "bybypitogage@gmail.com"
## [16] "pogonyzeweki@gmail.com" "xuly797@gmail.com" "cicici@gmail.com"
## [19] "metifadorahi@seznam.cz" "teha@gmail.com" "pyhoze68@gmail.com"
## [22] "jiny709@email.cz" "kezy87@seznam.cz" "qalumuzi@seznam.cz"
## [25] "zypuziwoku@gmail.com" "gosixico@seznam.cz" "kanigosu198@gmail.com"
## [28] "sagozenifozo8@gmail.com" "kajihirydatu20@gmail.com" "nepojujahire@gmail.com"
## [31] "zikenoge@gmail.com" "lubyzulixusi57@seznam.cz" "luco3@gmail.com"
## [34] "cojaqevuhy@seznam.cz" "qezulocy@seznam.cz" "lyrymejixaha@gmail.com"
## [37] "buryfowaty@gmail.com" "fumixoriny048@seznam.cz" "loba9@gmail.com"
## [40] "hypeqowiqo@gmail.com" "dubolo@seznam.cz" "pyhapeqa93@seznam.cz"
## [43] "cuwovuci192@seznam.cz" "wihyzivizugo@gmail.com" "veduxypi@gmail.com"
## [46] "qaqodejaxy801@gmail.com" "rigubagycaxy74@gmail.com" "nefu@seznam.cz"
## [49] "tekijorixisu@gmail.com" "vydebo95@gmail.com" "dabe@gmail.com"
## [52] "sadiqujake5@gmail.com" "solusyrewu@gmail.com" "luzejasy85@seznam.cz"
## [55] "voviqiqopesi@gmail.com" "gureresi@gmail.com" "qepogijyse@gmail.com"
## [58] "nuxi@seznam.cz" "ditonyvi6@gmail.com" "xizuxo@seznam.cz"
## [61] "hohywyceci@seznam.cz" "tipera@gmail.com" "solywukawipi1@gmail.com"
## [64] "kunyjagusygi@seznam.cz" "rovo@gmail.com" "porexy36@gmail.com"
## [67] "qamikibyjena@gmail.com" "jypokobi@gmail.com" "mixetokuti967@seznam.cz"
## [70] "nafudupe4@gmail.com" "romi4@gmail.com" "dymatuve@gmail.com"
## [73] "jedeqedytowo14@gmail.com" "fehecule2@seznam.cz" "wojocigyrine7@gmail.com"
## [76] "cahijahiwy24@gmail.com" "johowara99@seznam.cz" "vikoguco@seznam.cz"
## [79] "kojamepygucu2@seznam.cz" "xucaloli@seznam.cz" "dukulaly@seznam.cz"
## [82] "hixakavaze631@seznam.cz" "wali9@gmail.com" "dizopanohe3@gmail.com"
## [85] "rala@gmail.com" "xojejujeba69@seznam.cz" "depufityjefa@seznam.cz"
## [88] "pusy325@gmail.com" "fewudodabifo5@gmail.com" "pixabuqufozu@gmail.com"
## [91] "xobywo@gmail.com" "xybufyqeryge95@gmail.com" "jynaby156@gmail.com"
## [94] "jyze@seznam.cz" "nega@seznam.cz" "taji829@gmail.com"
## [97] "latutyro2@seznam.cz" "numymahyga@seznam.cz" "sivupe48@gmail.com"
## [100] "pefa@gmail.com"
Nagenerujte si spoustu hodnot z normálního rozdělení pomocí
rnorm()
. Definujte si faktorovou proměnnou o zhruba 15
ekvidistantních intervalech. Napočítejte relativní četnosti a zaneste do
tabulky. Gratuluji, právě jste zkonstruovali histogram, odhad hustoty.
Porovnejte se skutečnými pravděpodobnostmi (více info o
pravděpodobnostních rozděleních zde).
Uvažte příklad ze cvičení 3 a převeďte zjevně kategoriální proměnné na faktory. Nadefinujte si jak pracovní faktorovou proměnnou (krátké jednoduše srozumitelné kategorie), tak i faktorovou proměnnou s hezkými pojmenováními kategorií. Slučte kategorie nadváha a obezita do jedné. Využijte informace o obvodu pasu a definujte si (vlastním laickým náhledem) novou kategoriální proměnnou o třech (slušně) pojmenovaných kategoriích.