Slovník | Vyhledávání | Mapa webu
 
Základy informatiky pro biologyCvičebnice jazyka R 3. cvičení – Práce s datovými tabulkami

3. cvičení – Práce s datovými tabulkami, indexování, spojování a agregace

Práce s datovými tabulkami, indexování, spojování a agregace

Cíle 3. cvičení:

V předchozí kapitole jsme si představili datové tabulky, které jsou vlastně speciálními seznamy stejně dlouhých vektorů (potenciálně různých datových typů). Následující text uvádí užitečné příkazy pro práci s datovými rámci, které jsou nejpoužívanějším datovým typem v praktické analýze dat.


Indexování

Podobně jako v ostatních dvourozměrných strukturách (seznamy, matice), také v případě datových tabulek se lze na konkrétní prvek (řádek, sloupec) odkázat pomocí dvojice souřadnic v hranatých závorkách, přičemž souřadnice může být i prázdná.

Nejprve nadefinujme jednoduchou datovou tabulku:

halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód"),
"hmotnost"=c(19.00,35.45,79.90,126.90),
"znacka"=c("F","Cl","Br","I"))

Naše datová tabulka disponuje názvy sloupců, názvy řádků jsme ponechali prázdné. Zjistit nebo nadefinovat názvy sloupců a řádků lze jednoduše pomocí funkcí colnames() a rownames(). Názvy pak rovněž můžeme použít k indexaci buď při zadání jejich textových hodnot nebo lze vyextrahovat z datové tabulky jeden její sloupec pomocí značky dolaru $ (na české klávesnici lze zadat $ jako kombinaci Alt Gr + ů) - a to dokonce ve zjednodušené podobě bez uvozovek. Zjištění atomové hmotnosti bromu pak lze provést několika způsoby:

halogeny[3,2]
halogeny[3,"hmotnost"]
halogeny$"hmotnost"[3]
halogeny$hmotnost[3]

Indexací lze rovněž z datové tabulky vyextrahovat celý sloupec nebo se sloupcem dále pracovat. V následujícím příkladu z tabulky halogeny nejprve vybereme sloupec prvek, což lze opět provést dvěma způsoby, a použijeme jej pro nadefinování názvů řádků datové tabulky. Povšimněte si, že pokud necháme pozici souřadnice pro řádek prázdnou, získáme místo jedné buňky celý sloupec a tedy vektor:

halogeny$prvek # vybere sloupec nazvaný prvek
halogeny[,1] # provede totéž
rownames(halogeny) # názvy řádků nebyly definovány
rownames(halogeny)<-halogeny$prvek # definuje názvy řádků z prvního slouce tabulky
rownames(halogeny) # nyní už názvy řádků jsou definovány

Názvy sloupců a řádků (a obecně názvy v R) lze sice pojmenovat libovolnými textovými řetězci, nicméně při použití ve složitějších funkcích může docházet ke kolizím (násobné názvy, nepovolené znaky aj.). Proto správně nadefinované (a tedy univerzálně použitelné) názvy musí splňovat určitá jednoduchá pravidla - nesmí obsahovat mezery, čárky, středníky a speciální znaky používané jako operátory v R (např. lomítko, dvojtečka aj.) a nesmí začínat číslicí.

Pokud se pokusíme názvy definovat pomocí textových hodnot obsahujících tyto znaky, uspějeme, ale již při snaze vyextrahovat sloupec za pomocí jeho názvu a značky $ dojde k chybě a přerušení výpočtu. Proto, pokud chceme nadefinovat názvy korektně, máme k dispozici funkci make.names(), která nahradí všechny nepovolené znaky tečkou (ta je povolená). V případě, že název začíná číslicí předřadí funkce make.names() před číslici písmeno x, v případě jiné třídy než je character, provede konverzi pomocí as.character():

nazvy<-c("1. název prvku","2. atomová hmotnost","3. chemická značka") # vektor zamýšlených názvů
colnames(halogeny)<-nazvy # lze nadefinovat
colnames(halogeny)
halogeny$"3. chemická značka" # funguje správně
halogeny$3. chemická značka # vrací chybu
nazvy<-make.names(nazvy) # konverze na „správné“ názvy
colnames(halogeny)<-nazvy
colnames(halogeny)
halogeny$"X3..chemická.značka"
halogeny$X3..chemická.značka # nyní již funguje bez problémů

Podobně jako lze indexovat jednotlivými názvy či pořadovými čísly řádků a sloupců, lze (jak je v R obvyklé) indexaci provádět také celými jejich vektory. Ještě zajímavější je pak možnost „negativního indexování“, tj. vatrianta, kdy pořadové číslo konkrétníhio sloupce či řádku uvedeme se znaménkem minus. Výsledná struktura (vyextrahovaný vektor/datová tabulka) nebude dané sloupce/řádky vůbec obsahovat. Negativní indexování nefunguje v případě textových vektorů, zde bychom si museli pomoci dohledáním pořadového čísla pomocí funkce which() (k té se dostaneme později):

halogeny[,] # vrátí celou datovou tabulku
halogeny[,c(1,2)] # vrátí datovou tabulku pouze s prvními dvěma sloupci
halogeny[,-3] # totéž
halogeny[,-"hmotnost"] # vrací chybu
halogeny[,-which(colnames(halogeny)=="hmotnost")] # již funguje

Obdobně jako jednotlivé sloupce, můžeme z datové tabulky vyextrahovat také její jednotlivé řádky. Zde však nedojde ke konverzi na vektor (není totiž obecně splněna podmínka stejného datového typu).

halogeny[2,]
class(halogeny[2,])

Pokud chceme získat vektor odpovídající jednomu řádku datové tabulky, nezbývá, než provést konverzi ručně. Pokud se ovšem v datové tabulce budou vyskytovat sloupce třídy factor, obdržíme namísto textu pouze pořadové číslo vektoru. Zde je řešení ještě složitější - nejprve je nutné příslušné sloupce třídy factor kovertovat na třídu character, posléze vyextrahovat daný řádek a provést opět jeho konverzi na třídu character:

as.character(halogeny[2,])
halogeny$prvek<-as.character(halogeny$prvek)
halogeny$znacka<-as.character(halogeny$znacka)
as.character(halogeny[2,])


Spojování tabulek nad sebou a vedle sebe (rbind(), cbind())

Schopnost zmenšovat a rozdělovat datové tabulky je jen zčásti užitečná bez možnosti tabulky opět spojovat dohromady. K tomu slouží dvojice jednoduchých funkcí rbind() (rows bind) a cbind() colums bind), umožňující spojení tabulek s totožnými názvy sloupců a řádků. Důležité jsou opravdu jenom názvy, na pořadí sloupců nezáleží.

Nadefinujme si nyní znovu datovou tabulku s halogeny a přidejme obdobnou tabulku pro sousední chalkogeny, se záměrně odlišným pořadím sloupců:

halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód"),
"hmotnost"=c(19.00,35.45,79.90,126.90),
"znacka"=c("F","Cl","Br","I"))
chalkogeny<-data.frame("prvek"=c("kyslík","síra","selen","tellur"),
"znacka"=c("O","S","Se","Te"),
"hmotnost"=c(16.00,32.07,78.94,127.60))

Protože názvy sloupců jsou v obou případech totožné, můžeme jednoduše rovnou použít funkci rbind() a obě datové tabulky spojit do nové datové tabulky, nazvané prvky. Pořadí sloupců se převezme z tabulky uvedené na prvním místě v seznamu argumentů (tj. halogeny):

prvky<-rbind(halogeny,chalkogeny)

Obdobně lze použít funkci cbind() pro spojování datových tabulek vedle sebe, pokud si navzájem odpovídají jejich názvy řádků.


Spojování tabulek podle obsahu (merge())

Mimo mechanického spojování tabulek s pevným počtem řádků a sloupců lze využít také „chytřejší“ spojování tabulek podle řádků obsahujících stejné hodnoty v indexovém sloupci (sloupcích). K tomuto účelu slouží výhodně funkce merge(), jejímiž argumenty jsou obě spojované datové tabulky a názvy sloupců specifikované pomocí argumentů by (v případě shodných názvů sloupců v obou tabulkách), případně by.x a by.y pro první a druhou tabulku (pokud se názvy liší). Využít lze rovněž podobně strukturované logické argumenty all, resp. all.x a all.y.

halogeny1<-data.frame("prvek"=c("fluor","chlor","brom","jód","astat"),
"hmotnost"=c(19.00,35.45,79.90,126.90,209.99),
"znacka"=c("F","Cl","Br","I","At"))
halogeny2<-data.frame("prvek"=c("brom","chlor","fluor","jód"),
"barva"=c("červená","žlutozelená","žlutozelená","červená"))
merge(halogeny1,halogeny2,by="prvek")
merge(halogeny1,halogeny2,by.x="prvek",by.y="prvek")
merge(halogeny1,halogeny2,by="prvek",all=TRUE)
merge(halogeny1,halogeny2,by.x="prvek",by.y="prvek",all.x=TRUE)


Přeformátování tabulky na databázovou formu (reshape())

Jednoduchá užitečná funkce melt() z balíku reshape umožní přeformátovat tabulku o více sloupcích tak, že hodnoty z navzájem si odpovídajících sloupců budou opakovány v jednom sloupci pod sebou:

install.packages("reshape")
library("reshape")
halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód"),
"první.ionizační.energie"=c(1.68,1.25,1.14,10.1),
"druhá.ionizační.energie"=c(3.37,2.30,2.10,18.5),
"třetí.ionizační energie"=c(6.05,3.82,3.47,3.18))
melt(halogeny,id=c("prvek"))


Záměna názvů proměnných a sloupců (attach())

V případech, kdy pracujeme s jedinou datovou tabulkou (nebo obecně databází, tj. taky seznamem nebo zdrojovým souborem obsahujícím více proměnných, včetně balíků) může být výhodné využít funkci attach(), která připojí danou tabulku do jmenného prostoru tak, že názvy jejích sloupců jsou ztotožněny s vektory stejných názvů. To může v některých případech výrazně zkrátit délku kódu a usnadnit práci, na druhou stranu může dojít ke kolizím:

 

znacka<-c("zákaz zastavení","zákaz stání","přikázaný směr objíždění")
attach("prvky")
znacka # pracuje s původní proměnnou
hmotnost # pracuje se sloupcem připojené tabulky prvky

Prostým opakem funkce attach() je pak funkce detach(), kterou lze využít stejně dobře pro odpojení datové tabulky, jako pro odpojení připojených balíků V takovém případě je třeba před názvem balíku použít klíčové slovo package::

detach("prvky")
hmotnost
detach("package:xlsx")
read.xlsx("brno.xlsx",1)


Jednoduchá sumarizace

Jednoduché popisné statistiky (počty hodnot, průměr a kvartily u číselných proměnných) poskytuje generická funkce summary(), která pro každý sloupec zadané datové tabulky vrátí sadu popisných statistik odpovídající datové třídě konkrétního sloupce. Pro sloupce třídy factor jde o počty hodnot jednotlivých faktorových úrovní, u číslených proměnných pak o kvartily (minimum, 1. kvartil, medián, 3. kvartil, maximum) a průměr:

halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód","astat"),
"hmotnost"=c(19.00,35.45,79.90,126.90,209.99),
"znacka"=c("F","Cl","Br","I","At"),
"barva"=c("žlutozelená","žlutozelená","červená","červená",NA))
summary(halogeny)


Agregace datových tabulek

Aplikování (sumárních, agregačních, popisných) funkcí na skupiny řádků v datové tabulce lze provést jednoduše za pomoci funkce aggregate(), která na základě zadaného seznamu kritérií (tj, vyjmenovaných sloupců datové tabulky) rozdělí datovou tabulku na skupiny se stejnými hodnotami těchto kritérií a z každé skupiny vytvoří jeden řádek nové datové tabulky obsahující výslednou hodnotu agregační funkce aplikované na zadanou proměnnou (sloupec).

Například pokud bychom potřebovali (z nějakého zvláštního důvodu) zjistit průměrnou atomovou hmotnost červeně a žlutozeleně zbarvených halogenů, specifikujeme funkci aggregate(), že agregovaná proměnná je hmotnost, rozdělení se provede podle proměnné barva a agregační funkce je mean(), tedy průměr. Název agregační funkce se poněkud netradičně zadává ve formě textového řetězce jako argument FUN. Další volitelné argumenty jsou pak interpretovány jako zpřesňující argumenty použité agregační funkce:

halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód","astat"),
"hmotnost"=c(19.00,35.45,79.90,126.90,209.99),
"znacka"=c("F","Cl","Br","I","At"),
"barva"=c("žlutozelená","žlutozelená","červená","červená",NA))
halogeny_agg<-aggregate(halogeny$hmotnost,by=halogeny$barva,FUN="mean")
halogeny_agg<-aggregate(halogeny$hmotnost,by=list(halogeny$barva),FUN="mean")
halogeny_agg<-aggregate(halogeny$hmotnost,by=list(halogeny$barva),FUN="mean",na.rm=TRUE)
attach(halogeny)
halogeny_agg<-aggregate(halogeny$hmotnost,by=list(barva),FUN="mean",na.rm=TRUE)

Je vhodné dodat, že funkci aggregate() lze zadat více proměnných pro agregování opět ve formě seznamu a že pojmenování jednotlivých prvků seznamu (pokud je zadáno) určuje pojmenování sloupců ve výsledné datové tabulce:

halogeny_agg<-aggregate(list(hmotnost1=halogeny$hmotnost,hmotnost2=1000*halogeny$hmotnost),by=list(barva=halogeny$barva),FUN="mean")


Vytváření kontingenčních tabulek

Pro specifikování kontingenční tabulky uvádějící počty hodnot (tj. řádků datové tabulky) odpovídající jednotlivým úrovním zadaných faktorových proměnných slouží užitečná funkce ftable(). Argumenty row.vars a col.vars slouží pro zadání řádkových a sloupcových proměnných; v případě, že nejsou tyto argumenty zadány, konvertuje funkce všechny sloupce datové tabulky na faktorové a vytvoří kontingenční tabulku ze všech sloupců:

halogeny<-data.frame("prvek"=c("fluor","chlor","brom","jód","astat"),
"hmotnost"=c(19.00,35.45,79.90,126.90,209.99),
"znacka"=c("F","Cl","Br","I","At"),
"barva"=c("žlutozelená","žlutozelená","červená","červená",NA))
ftable(halogeny)
ftable(halogeny,row.vars="barva",col.vars="znacka")


Cvičení

  1. Využijte funkci read.xlsx() pro správné načtení šesti souborů s počtem obyvatel v českých obcích obyvatelstvo_2010.xlsx, obyvatelstvo_2011.xlsx, obyvatelstvo_2012.xlsx, obyvatelstvo_2013.xlsx, obyvatelstvo_2014.xlsx, obyvatelstvo_2015.xlsx a uložte jejich obsah do datových tabulek obyvatelstvo_2010, obyvatelstvo_2011, obyvatelstvo_2012, obyvatelstvo_2013, obyvatelstvo_2014 a obyvatelstvo_2015.
  2. Použijte funkci nrow() pro zjištění počtu řádků jednotlivých datových tabulek. Hodnoty navzájem porovnejte.
  3. Použijte funkci which() a negativní indexování pro vyjmutí řádků s neexistující číselnou hodnotou ve sloupci celkem ze všech šesti tabulek.
  4. Zajistěte, že budou ve všech šesti tabulkách sloupce celkem vektory třídy integer.
  5. Zvolte vhodnou funkci a využijte sloupce zuj, obsahující kód základních územních jednotek, ke spojení všech šesti datových tabulek do nové datové tabulky obyvatelstvo.1, která bude obsahovat sloupec zuj, sloupec obec a šest sloupců s počty obyvatel v jednotlivých letech, pojmenovaných pocet.2010, pocet.2011, pocet.2012, pocet.2013, pocet.2014 a pocet.2015.
  6. Využijte funkci reshape() a další vhodné funkce pro přeformátování datové tabulky obyvatelstvo.1 na datovou tabulku obyvatelstvo.2, která bude obsahovat 4 sloupce: zuj, obec, rok a pocet.
  7. Přidejte ke každé z datových tabulek obyvatelstvo_2010, obyvatelstvo_2011, obyvatelstvo_2012, obyvatelstvo_2013, obyvatelstvo_2014 a obyvatelstvo_2015 sloupec obsahující vždy hodnotu příslušného roku (tj. např. v tabulce obyvatelstvo_2010 se bude ve sloupci rok opakovat stále stejná hodnota 2010).
  8. Využijte funkci rbind() a spojte všech šest tabulek pod sebou do nové tabulky , která bude mít čtyři sloupce: zuj, obec, rok a pocet.
  9. Porovnejte tabulky obyvatelstvo.2 a obyvatelstvo.3.
  10. Najděte minimální a maximální počty obyvatel v obcích v letech 2010 a 2015.
 
vytvořil Institut biostatistiky a analýz Lékařské fakulty Masarykovy univerzity