Adattudomány

Ez az oldal fejlesztés alatt áll.

TODO:

  • felbontani részekre
  • az R-t kiemelni (mindent), és hivatkozni rá
  • képekkel ill. online demóval illusztrálni
  • saját példák
  • GitHub linkek
  • saját feladatok megoldásai
Table of Contents

Az adattudomány (data science) jelentősége az adatmennyiség exponenciális növekedésével egyre jelentősebb. Ezen az oldalon összefoglalom a legfontosabb tudnivalókat. Az anyag az ingyenesen elvégezhető 10 részes https://www.coursera.org/specializations/jhu-data-science kurzus sorozatra épül; a fejezetcímek egyben az ottani kurzuscímek is. A tananyag szabadon elérhető itt: https://github.com/bcaffo/courses/. Az egyes kurzusok az alábbi linkeken érhetőek el:

Fontos megjegyzés: nem tartom maga adattudósnak; szoftverfejlesztő vagyok, és az adattudománnyal a PhD-m során kerültem kapcsolatba. Ez az oldal leginkább emlékeztető kivonat magamnak, de lehet, hogy másnak is hasznos, és esetleg kedvet kap a fenti kurzusok elvégzéséhez. A leírás bevezető jellegű, így azok a részeket kimaradnak belőle, amelyeket nem tartok fontosnak.

Az adattudós eszköztára

Kurzus: https://www.coursera.org/learn/data-scientists-tools/

Legfontosabb eszközök (mind ingyenes); ezeket töltsük le ill. regisztráljunk:

  • R statisztikai rendszer: R projekt (ez maga az R rendszer), R Studio (fejlesztői környezet), R tools (eszközök R csomagok készítéséhez). Az R mellett a Markdown jelölőnyelvek (markup language) is érdemes megismerni, melynek egy jó összefoglalója található itt: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet.
  • Git verziókövető rendszer: Git (ez maga a verziókövető szoftver), GitHub (repó, ahova a Git projekteket menthetjük). A git parancsok és a GitHub használata mellett szükség van alapvető parancssori ismeretekre is.

Az adattudomány kérdéseinek típusai:

  • Leíró (descriptive): egy adathalmaz leírása.
  • Felderítő (exploratory): korábban nem ismert kapcsolat felderítése.
  • Következtető (inferential): állítás megfogalmazása nagyobb adatmennyiségről viszonylag kis minta felhasználásával.
  • Előrejelző (predictive): valamilyen dologgal kapcsolatos adatok felhasználása más dologgal kapcsolatos értékek előrejelzésére.
  • Alkalmi (?) (casual): annak a kiderítése, hogy mi történik egy változóval ha egy másikat megváltoztatunk.
  • Mechanisztikus (?) (mechanistic): egy változó megváltozásának megértése, amely más dolgok változóinak a megváltozásához vezetnek.

Az adattudományban a legfontosabb a kérdés, a második legfontosabb az adat.

R programozás

Kurzus: https://www.coursera.org/learn/r-programming

Az R egy ingyenes, statisztikai célú programozási nyelv. A (fizetős) S rendszerre épül. Erejét az ingyenessége mellett a több ezer, ugyancsak ingyen elérhető csomag adja. Megjegyzés: ebbe a részbe nemcsak az itt elhangzottakat veszem fel, hanem olyan részeket is, melyek a sorozat más kurzusán hangzottak el, de lényegében az R szintaxis témához tartozik.

Bevezető

Néhány alapvető információ az R-ről:

  • Van neki parancssoros interfésze, melynek segítségével közvetlenül adhatunk ki parancsokat, amelyeket azonnal végrehajt. Ha elindítjuk az R keretrendszert vagy az R studio-t, akkor parancssori utasításokat adhatunk ki.
  • Valamint a programokat szövegfájlokba is írhatjuk és futtathatjuk. A szokásos kiterjesztés: .R. Futtatás: R —slave —vanilla < filename.R
  • Csomagok telepítése: install.packages, pl. install.packages("devtools"). Csomag beolvasás: library, pl. package(devtools).
  • Segítség: ? (pl. ?max), vignette() (pl. vignette("intro")), library(help = "datasets").
  • Az R-ben nem kell megadni a változó típusát.
  • A szokásos értékadás jele <- (pl. x <- 5), de az = jel is működik.
  • Az utasításokat ;-vel választhatjuk el, de nem kötelező a használata.
  • Megjegyzés: #
  • Kiírás: lehet implicit, amikor megadjuk a kiírandó változó nevét (pl. x), vagy explicit a print utasítással (pl. print(x)).

Adattípusok

Az R-ben az adattípusok filozófiai megközelítésben és megvalósulásában is eltérnek a más programozási nyelvekben megszokottaktól. Mivel ebben a rendszerben adatokkal dolgozunk, különösen fontos az adattípusok megértése.

  • Atomi adattípusok:
    • szöveges (character)
    • valós szám (numeric)
    • egész szám (integer)
    • komplex (complex)
    • logikai (logical)
  • Összetett adattípusok:
    • vektor (vector)
    • mátrix (matrix)
    • lista (list)
    • faktor (factor)
    • adat keret (data frame)

Általános tudnivalók:

  • Egy szám típusa alapból valós; az L posztfixet kell hozzáírni, ha egészként szeretnénk tárolni (pl. x <- 5L).
  • A számoknál létezik végtelen (pl. 1/0=Inf), nem kiszámolható (pl. 0/0=NaN).
  • A hiányzó adat a NA.
  • A szöveges típust aposztrof jelek és idézőjelek közé is tehetjük (pl. 'hello', "hello").
  • A logikai értékek: TRUE és FALSE.
  • Az adat típusát a class() függvény segítségével tudhatjuk meg (pl. class(x)).
  • Az összetett adatoknak lehetnek attribútumaik, melyet az attributes() függvény segítségével tudunk lekérdezni (pl. ha m egy mátrix, akkor attributes(m)).
  • Az adatok egymásba konvertálhatóak az as.*() függvények segítségével (pl. as.numeric(x), as.logical(x), as.character(x)).

Vektor:

  • A legelemibb adattípus.
  • A vektor ugyanolyan osztályú elemek listája.
  • Létrehozási lehetőségek:
    • Alapból mindegyik elem egy egyelemű vektor, pl. az x <- 5 hatására az x egy egyelemű vektor lesz.
    • A : operátorral, pl. a 1:5 eredménye a következő 5 elemű vektor: 1 2 3 4 5.
    • A c (concatenate, összefűzés) függvénnyel, pl. a v <- c(1, 5, 3:9) az 1 5 3 4 5 6 7 8 9 vektort hozza létre.
    • Értékadás nélküli vektort a vector() függvénnyel hozhatunk létre, pl. a vector(length = 5, mode = "numeric") eredménye ez: [1] 0 0 0 0 0.
  • Az R-ben a legtöbb művelet vektorizált, ami azt jelenti, hogy mindegyik elemre végrehajtja. Pl. az 1:4 + 2*(5:8) eredménye ez lesz: [1] 11 14 17 20.

Mátrix:

  • A mátrix valójában vektor dimenzió (dim) attribútummal. A dimenzió attribútum egy 2 elemű vektor.
  • Mivel a mátrix is vektor, csak egyfajta elemet tartalmazhat.
  • Mátrixot a matrix() függvénnyel tudunk létrehozni, ahol megadhatjuk az adatokat oszloponként, majd a nrow és ncol paraméterekkel a mátrix dimenzióit adhatjuk meg. Példa: m <- matrix(1:12, nrow = 4, ncol = 3).
  • A utólag is mátrixot átméretezhetjük azzal, hogy a dim attribútumot átállítjuk.
  • Oszlopot a cbind(), míg sor az rbind() függvénnyel adhatunk hozzá.
  • Az egyes soroknak ill. oszlopoknak nevet is adhatunk a dimnames() függvény segítségével.

Lista:

  • Olyan vektor, amely különböző típusú adatokat tartalmazhat, pl l <- list(5, "alma", TRUE).
  • Elem címzése: [[index]] (ha úgy címezzük mint a vektort ([index]), akkor egy egyelemű listát kapunk).
  • Nevet is adhatunk az elemeknek, és akkor $ jellel hivatkozhatunk rá, pl. l <- list(szam=5, gyumolcs="alma", finom=TRUE), majd l$gyumolcs.

Faktor:

  • Adatkategóriákat lehet vele létrehozni.
  • Létrehozó függvény: factor(), pl. f <- factor(c("igen", "igen", "nem", "igen", "nem")).
  • Sorrend átrendezése: relevel(), pl. f2 <- relevel(f, "nem").

Adatkeret (data frame):

  • Adatok táblázatos elrendezésére szolgál (kb. mint egy adattábla az adatbázisban).
  • A mátrixszal ellentétben különböző típusokat tartalmazhat.
  • Reprezentáció: ezek valójában speciális listák, mégpedig a lista mindegyik eleme egy ugyanolyan hosszú vektor.
  • A lista egy eleme felfogható oszlopként.
  • A listák hossza a sorok száma.
  • Oszlopok nevei: row.names attribútumok.
  • Létrehozása: data.frame() függvénnyel, pl. df <- data.frame(szam=c(2, 3, 2, 3, 5), gyumolcs=c("alma", "banan", "alma", "barack", "banan")), valamint tipikusan beolvasásokkal (read.table()).

Műveletek

  • Matematikai műveletek
    • abs(x) - abszolút érték
    • sqrt(x) - négyzetgyök
    • sin(x), cos(x), tan(x), asin(x), … - trigonometrikus függvények
    • log(x), log2(x), log10(x) - logaritmus
    • exp(x) - exponenciális
  • Kerekítések
    • ceiling(x) - felfelé kerekítés
    • floor(x) - lefelé kerekítés
    • round(x, digits=n) - adott tizedespontig kerekít, a kerekítési szabályok szerint
    • signif(x, digits=n) - adott értékes jegyig kerekít
  • Halmaz műveletek
    • intersect(x, y) - metszet
  • Logikai műveletek
    • %in% - azt vizsgálja, hogy benne van-e egy elem a halmazban, pl. table(df$gyumolcs %in% c("alma", "barack"))
  • String műveletek
    • tolower(), toupper() - kisbetűsítés, nagybetűsítés
    • strsplit() - szöveg felosztása
    • sub(), gsub() - csere (első ill. az összes)
    • grep(), grepl() - mintaillesztés (szöveget ill. logikai értéket ad vissza)
    • nchar() - karakterek száma
    • substr() - rész szöveg
    • paste, paste0 - összefűzés (szóközzel, szóköz nélkül)
    • library(stringr): str_trim() - szóköz karakterek eltávolítása a végekről

Példa adatok

A datasets csomagban találhatóak előre csomagolt adatok:

  • airquality - légminőségi adatok 1973-ból (154 megfigyelés, 6 változó)
  • mtcars - autó adatok 1974-ből (32 megfigyelés, 11 változó)
  • swiss - szociológiai adatok Svájc francia részéről, 1888-ból (47 megfigyelés, 6 változó)

Adatok generálása

  • seq() - szekvenciák létrehozása, pl. seq(1, 9, by=2) (kettesével), seq(1, 9, length=3) (az eredmény hossza van megadva).
  • ifelse() - bináris változó létrehozása, pl. ifelse(1:9 < 5, TRUE, FALSE).
  • cut() - kategorikus változók létrehozása; az egyes elemeket átalakítja a tartalmazó intervallumokká, pl. cut(1:10, breaks=c(0, 3, 7, 10)). Másik lehetőség (egyszerűbb paraméterezéssel): a Hmisc könyvtárban található cut2() függvény.

Adatok összefoglalása

Az R-ben tipikusan rengeteg adattal dolgozunk, melynek teljes áttekintése nem könnyű feladat. Az alábbi függvények gyors, de nem részletes áttekintést tesznek lehetővé:

  • head(), tail() - az első ill. utolsó pár adatot tudjuk megjeleníteni (pl. egy hosszú szöveg vagy nagy adatkeret első vagy utolsó néhány sorát).
  • summary() - adattípustól függő összegzést készít, pl. summary(df).
  • str() - az adatok struktúráját adja vissza.
  • names() - az elnevezéseket adja vissza, pl. ad adatkeretek oszlopneveit.
  • quantile() - a paraméterül átadott értékek legfontosabb kvantilisait (legkisebb elem, 25%, medián, 75%, legnagyobb elem) adja vissza (beállítható).
  • table() - táblázatot készít. Ha pl. egy nagy adatkeret két oszlopát adjuk paraméterül, akkor a táblázat egy cellája azt mutatja meg, hogy adott kombinációval hány elem fordul elő. Ha egy vektort adunk át, akkor megmutatja, hogy melyik elem hányszor fordul elő.
  • xtabs() - tartalmazás táblát készít, de komplexebb eseteket is képes kezelni. Pl. (xtabs(~szam+gyumolcs, df))
  • rowSums(), colSums(): soronkénti ill. oszloponkénti összegzés, pl. rowSums(m)
  • object.size() - az objektum bájtban kifejezett méretét adja vissza.

Adatok átalakítása

  • Részhalmazok képzése
    • A [] eredményének típusa mindig megegyezik az eredetiével. Ezzel több elemet is kiválaszthatunk. Pl. v[5], v[c(2, 4:7)]
    • A [[]] segítségével listák és adatkeretek elemeit lehet megcímezni. Az eredményének a típusa nem feltétlenül egyezik meg az eredetiével, és ezzel csak egy elemet választhatunk ki. Példa: l[[2]]
    • A $ név szerint választ elemet listából és adatkeretből; működése hasonló a [[]]-hez. Különbség: a [[]] esetében használhatunk kiszámolt változót (tehát ha pl. az oszlopnevet változóból vesszük, pl. elemnev <- "gyumolcs"; l[[elemnev]]), míg $ esetén csak literál nevet használhatunk. Példa: l$gyumolcs
    • A [[]] és $ esetén a mintaillesztés megengedett, pl. l$gy.
    • A részhalmazt képezhetjük indexekkel (pl. v[1:4] a v vektor első 4 elemét választja ki) vagy logikai műveletekkel (pl. az v[v>5] az 5-nél nagyobb elemeket választja ki).
    • Mátrixok esetében a címzésnél kiválaszthatunk egy elemet (pl. m[1,2]), képezhetünk részmátrixot (pl. m[c(2:4), c(1, 3)]), elhagyhatjuk a sor vagy oszlop kiválasztást (pl. m[1,], m[,2]). Ha az eredmény lehet vektor, akkor az R vektorrá konvertálja (ki lehet kapcsolni a drop=FALSE kapcsolóval).
    • Adatkeretekből a mátrixhoz hasonlóan választhatunk ki elemeket, sorokat, oszlopokat, rész adatkereteket. Az oszlopokra hivatkozhatunk névvel vagy sorszámmal. A listáknál bemutatott [], [[]] és $név kiválasztás is működik.
    • A kiválasztásnál logikai műveletek is megadhatóak, pl. df[df$szam < 3 | df$gyumolcs=="banan",]. Hiányzó adat lekérdezése: is.na(). Ha a feltételt a which() függvénybe tesszük, akkor a kiértékelésnél kiszűri az NA adatokat.
  • Sorba rendezés
    • sort() - rendezés, pl. sort(v).
    • order() - a rendezésutáni sorszámát adja vissza. Pl. a fenti adatkeret szám szerinti sorba rendezése: df[order(df$szam),]. Több oszlop szerint is rendezhetünk.
  • Kiegészítés
    • Oszlop hozzáadása adatkerethez: pl. df$veletlen <- rnorm(5).
    • cbind() - oszlop hozzáadása mátrixhoz vagy adatkerethez, pl. cbind(m, 1:4).
    • rbind() - sor hozzáadása, pl. rbind(m, 1:3) (szintén nem marad része).
  • Átalakítás
    • ftable() - "lapos" tartalmazási táblázatot készít. Ha az adatkeret két oszlopot tartalmaz, akkor az eredmény megegyezik a fentiekkel, ha viszont többet, akkor legenerálja az összes kombinációt. (Így sok oszlop és nagy táblázat esetén az eredmény áttekinthetetlen lesz.)
    • library(reshape2), melt() - adatok "feloldása" oly módon, hogy kettő vagy több oszlop megszűnik, létrejön egy új variable és value oszlop, a variable az eredeti oszlop nevét tartalmazza, a value pedig az értékét. Így az oszlopok száma csökkenhet, a sorok száma nőni fog. Vonatkozó függvény: cast().
    • library(plyr), arrange() - sorba rendezés, pl. arrange(df, szam), arrange(df, desc(szam)).
    • library(plyr), mutate() - adatkeret átalakítása, pl. oszlop hozzáadása.
    • library(plyr), ddply() - adatkeret felosztása, művelet végrehajtása, az eredmény visszaadása adatkeret formában.
  • Összefűzés
    • merge() - kettő adatkeret összefűzése közös oszlopnév alapján.
    • library(plyr), join() - ez is ugyanezt a műveletet hajtja végre.
    • library(plyr), join_all() - több adatkeret összefűzése

Adatok írása-olvasása

  • Fájl műveletek:
    • getwd(), setwd("könyvtárnév") - a munkakönyvtár lekérdezése, beállítása
    • file.exists("könyvtárnév") - annak ellenőrzése, hogy létezik-e a fájl vagy könyvtár
    • dir.create("könyvtárnév") - könyvtár létrehozása
    • list.files("könyvtárnév") - adott könyvtárban található fájlok kilistázása
    • download.file("fájlnév") - fájl letöltése az internetről
  • Szerializálás:
    • Egy objektum szerializálása: dput(), melynek a párja dget(). Pl. dput(fruits, file="fruits.R"), majd fruits2 <- dget("fruits.R").
    • Több objektum szerializálása: dump(), melynek a párja source(). A szintaxis hasonló mint a fenti.
  • Fájl műveletek:
    • readLines() - sorok beolvasása szövegből
    • Lehetőségek: file(), gzfle(), bzfile().
    • Példa: con <- file("filename.txt", "r"); readLines(5); close(con)
    • Fájl letöltése internetről: url("http://…")
  • Táblázatos adatok beolvasása: read.table(), pl. fruits <- read.table("table.txt"). Alapértelmezésben fejléc nélküli, vesszővel elválasztott fájlként olvassa be . Az eredmény adatkeret lesz, a típusokat megpróbálja kitalálni. Paraméterekkel sok minden beállítható. Hasonló függvény a read.csv(), ami a read.table() függvényt hívja meg olyan paraméterekkel, hogy az megfeleljen a csv fájltípusnak (pl. fejléccel).
  • Excel: library(xlsx), read.xlsx("fájlnév.xlsx"), read.xlsx2("fájlnév.xlsx"), write.xlsx("fájlnév.xls")
  • XML:
    • library(XML)
    • doc <- xmlTreeParse("fájlnév.xml") - XML beolvasása
    • node <- xmlRoot(doc) - a gyökér node lekérése
    • names(node) - egy node alatti node-ok nevei
    • node[[1]] - egy node alatti első elem lekérdezése
    • xmlSApply(node, xmlValue) - a fájl részeinek lekérdezése
    • xpathSApply(node, "xPathKifejezés", xmlValue) - XPath lekérdezés
  • HTML:
    • doc <- htmlTreeParse("fájlnév.html")
    • A többi függvény az XML-hez hasonló.
  • JSON
    • library(jsonlite)
    • jsonData <- fromJSON("uri") - beolvasás
    • toJson(jsonData) - kiírás
    • names(jsonData) - mező nevek
    • jsonData$name - egy adott mező elérése
  • Adattáblák (data tables): hasonló mint az adat keret (data frame), viszont sokkal több lehetőséget nyújt.
    • library(data.table)
    • dt <- data.table(…)
  • MySQL
    • library(RMySQL)
    • dbConn <- dbConnect(MySQL(), user="username", password="password" host="hostname", port="port", dbname="dbname")
    • dbListTables(dbConn)
    • dbListFields(dbConn, "tablename")
    • dbReadTable(dbConn, "tablename")
    • fetch(dbSendQuery(dbConn, "SELECT …"))
    • dbDisconnect(dbConn)
  • HDF5 (nagy adathalmazok tárolására)
    • library(rhdf5)
    • h5read(…)
    • h5write(…)
  • API
    • library(httr)
    • GET("URL")
  • Más statisztikai csomag: library(foreign), majd pl. read.spss().
  • Más adatbázisok: library(RPostgreSQL), library(RODBC) (PostgreQL, MySQL, Microsoft Access, SQLite), RMongo
  • Képek: library(jpeg), library(readbitmap), library(png)
  • GIS (térképek): library(rgdal), library(rgeos), library(raster)
  • Zene: library(tuneR), library(seewave)

Vezérlés

Az R-ben rendelkezésre áll az összes gyakori, más programozási nyelvben megszokott vezérlő szerkezet, így igazi programozási nyelvként is használhatjuk, nemcsak statisztikai céleszközként:

  • Feltételkezelés: if (…) {…} else if (…) {…} else {…}. Annyiba eltér a legtöbb programozási nyelvben megszokottól, hogy hogy lehet neki visszatérési értéke, mégpedig az utolsó kiértékelés eredménye. Így pl. használhatjuk ezt: x <- if (y > 3) {5} else {6}.
  • Véges ciklus: for(…) {…}, pl. for (i in 1:10) {print(i)}.
  • Elöltesztelős végtelen ciklus: while (…) {}, pont úgy működik, ahogy azt elképzeljük.
  • Végtelen ciklus: repeat {…} (ki tudja, miért gondolták ezt hasznosnak a nyelv megalkotói…).
  • Megszakítás: break.
  • Következő elem (megfelel a máshol megszokott continue-nak): next.

Függvények

A függvény a másik olyan lehetőség, ami szerethetővé teszi az R-t a programozók körében is. Mindenféle magyarázat előtt álljon itt egy példa, ami a legtöbb kérdést megválaszolja:

> add <- function(a, b) {a + b}
> add(2, 3)
[1] 5

Néhány fontos információ:

  • A függvény eredménye az utoljára meghatározott érték. Ha szeretnénk, kitehetjük a return(result) utasítást (mondjuk javítja az olvashatóságot), de el is hagyhatjuk.
  • A paraméterek típusait nem kell megadni, csak a neveit (sajnos…)
  • A paramétereknek lehet alapértelmezett értékük, melyet egyenlőség jellel adhatunk meg.
  • Híváskor paramétereket nevesítve is megadhatjuk egyenlőségjellel, pl. add(b = 3, a = 2). A látszat ellenére ennek komoly jelentősége van, és szerintem érdemes lenne máshol is bevezetni. Számos függvénynek tucatnyi paramétere van, többségük alapértelmezett értékkel, és így kiválogathatjuk azt a párat, melyet mi szeretnénk megadni. Ezen kívül az olvashatóságot is javítja, és megoldás lehet a magic number problémára.
  • Létezik a var-arg mechanizmus; a paramétert így kell megadni: . Ezt tipikusan továbbítjuk egy másik függvénynek, de listává is tudjuk alakítani (list(…)). Hasznos lehet pl. ha saját diagram készítőt implementálunk, ami a legvégén meghívja a plot() függvényt; annak tudjuk továbbítani az ő paramétereit.
  • Scoping: a tanfolyam egész hosszasan foglalkozik ezzel, és viszont szándékosan rövidre zárom a témát. Kb. úgy működik, ahogy elvárjuk, úgy éri el a globális változókat, függvényeket. Viszont egyrészt szerintem nem célszerű egy függvénynek belepiszkálni a globális adatokba, másrészt ha nem nyilvánvaló a scoping a megvalósításkor, akkor még kevésbé lesz nyilvánvaló a karbantartáskor, így egyszerűen kerüljük a témát.

Ciklusfüggvények

A ciklusfüggvények nevében szerepel az, hogy apply. Arról van szó, hogy egy adathalmaz elemire végrehajt egy műveletet, paraméterül átadva neki egyesével az adathalmaz elemeit. Valójában tehát kb. ekvivalens azzal, mintha végig iterálnánk az input elemein, és egyeséve végrehajtanánk a műveletet, viszont sokkal tömörebb.

Jellegzetes eleme az R programozási nyelvnek, így szinte elkerülhetetlen, hogy belefussunk, emiatt itt is megtalálható. Alkalmazni viszont csak óvatosan érdemes, mert drasztikusa rontja a kód olvashatóságát, különösen akkor, ha a kód olvasója R-ben kevésbé járatos.

  • lapply(): a függvény első paramétere egy lista, melynek elemeit egyesével átadja a második paraméternek első elemként, ami egy függvény. Utána tetszőleges számú paramétert megadhatunk még, ami a második paraméterként megadott függvény további paraméterei lesznek. A bemenet és a kimenet is mindig lista (tehát pl. ha a bemenet 1:5, akkor abból olyan 5 elemű lista lesz, melynek eleme egyelemű vektorok). Példa: lapply(list(1:5, 3:9), mean). Itt először meghívja a mean(1:5), majd a mean(3:9) átlagot számoló függvényt, az eredmény pedig olyan kételemű lista lesz, melynek első eleme a 3-at tartalmazó egyelemű vektor, míg a második eleme a 6-ot tartalmazó egyelemű vektor.
  • sapply(): hasonló az előzőhöz, viszont az eredményt próbálja egyszerűsíteni, pl. sokkal könnyebben kezelhető vektorrá alakítani. Pl. az sapply(list(1:5, 3:9), mean) eredménye a 3 és 6 elemekből álló kételemű vektor.
  • apply(): a nevéből levonható következtetéssel ellentétben ez egy egészen speciális eset: egy mátrix sorain vagy oszlopain hajtja végre a műveletet. Az első paramétere tehát egy mátrix, a második azt jelzi, hogy soronként (1) vagy oszloponként (2) hajtsa-e végre a műveletet (és igazán létrehozhattak volna neki valami konstanst, pl. byRows és byColumns), majd jön a függvény és a további paraméterek. Pl. az apply(matrix(1:6, 3, 2), 2, mean) hívásban a mátrix 3 sort és 2 oszlopot tartalmaz, az átlag (mean) számítást oszloponként hajtja végre (2), és mivel az első sozlop elemei az 1, 2 és 3, a másodiké pedig a 4, 5 és 6, az eredmény a 2 és 5 számokból álló kételemű vektor lesz.
  • mapply(): egy bizonyos függvényt (első paraméter) hajt végre a második és harmadik paraméter kombinációiból. Pl. a mapply(rep, 1:4, 4:1) függvény ekvivalens ezzel: list(rep(1, 4), rep(2, 3), rep(3, 2), rep(4, 1)). (A rep() függvény ismétlést jelent, pl. a rep(1, 4) eredménye 1, 1, 1, 1.)
  • tapply(): első paraméterként egy vektort vár, másodikként pedig faktort (vagy faktorként értelmezhető vektort), és a harmadik (és további) paraméterként megadott függvényt olyan bontásban hajtja végre, ahogyan azt a faktor kijelöli. Pl. a tapply(1:20, c(rep("a", 5), rep("b", 10), rep("c", 5)), mean) először három csoportra bontja az 1..20 közötti számokat, az első 5 lesz az 'a', a következő 10 a 'b', végül az utolsó 5 a 'c' kategória, és ezeken hajtja végre az átlagszámítást. Az eredmény ennek megfelelően a következő 3 elemű vektor lesz: 3.0, 10.5, 18.0, az elemek megfelelően elnevezve rendre a-bal, b-vel és c-vel. Ehhez kapcsolódó függvény a split(), amely a az első paraméterként átadott vektort bontja részekre a második paraméterként átadott faktor alapján, pl. split(1:20, c(rep("a", 5), rep("b", 10), rep("c", 5))).

Dátumkezelés

Rendes időkezelést még nem láttam. Mindenhol van valami, ami kellően komplikált és kellően ritkán használt ahhoz, hogy ne lehessen fejben tartani a szintaxist. Majd rendre megjelenik az "egyszerűsítés", ami a felülről kompatibilitás kényszere miatt gyakorlatilag még jobban összekuszálja a szálakat. Nincs ez másképp az R-ben sem. Lássuk a részleteket:

  • A rendszerórát így tudjuk lekérdezni: Sys.time()
  • A POSIXlt típus tartalmazza a szokásos felbontást. Pl. azt, hogy az év hányadik napja a mai, így tudjuk lekérdezni: as.POSIXlt(Sys.time())$yday
  • A POSIXct a háttrében gyakorlatilag a UNIX rendszeróra másodperce, melynek lekérdezése - miért is lenne egyszerű az élet - így nét ki: unclass(as.POSIXct(Sys.time()))
  • Ha pedig egy saját formátumot szeretnénk beolvasni, azt a következő, nehezen megjegyezhető nevű és logikátlan paraméterlistájú függvénnyel tudjuk megtenni: strptime("2019-03-29 07:25:12", "%Y-%m-%d %H:%M:%S")
  • Alap műveleteket végre tudunk hajtani a dátumokon, pl. egymásból ki tudjuk őket vonni.

Szimuláció

Sokféle eloszlású számokkal lehet 4 féle műveletet végrehajtani. A függvények mindegyike a műveletnek megfelelő betűvel kezdődik:

  • d - sűrűség (density), pl. normális eloszlás esetén ez a haranggörbe
  • p - kumulatív eloszlás (cumulative distribution); ez a sűrűség kumulálásából adódik, 0-ból indul, 1-be ér
  • q - qvantilis (quantile); ez a p inverze
  • r - véletlen szám generálás (random number generation)

Néhány fontosabb eloszlás:

  • norm - normális
  • pois - Poisson
  • exp - exponenciális
  • unif - egyenletes

Pl. a rnorm(10) 10 darab standard normális eloszlású véletlen számot generál.

Diagramok készítése

Az alap ábrázoló rendszer

A Lattice rendszer

A ggplot2 rendszer

Hibakeresés

Ha egy hiba okát szeretnénk megtalálni, olyan szintű hibakeresési lehetőségekre ne számítsunk, mint mondjuk a Java esetén rendelkezésünkre áll, de azért az R-ben is van pár lehetőség.

Stack trace

Egy hiba lokalizációjában sokat segít az ún. stack trace, azaz a hívási lánc. Parancssoros végrehajtás után adjuk ki a traceback() függvényt, és akkor megjelenít egy hevenyészett hívási láncot, nagy vonalakban megjelölve azt, hogy hol jött elő a hiba.

Debug

A program sorokat egyesével végrehajtani az egyik leghasznosabb hibakeresési módszer. Az R-ben kissé nehézkes, de működik. Ha a függvény neve, amit debuggolni szeretnénk, func, akkor adjuk ki ezt a parancsot: debug(func) (így, paraméterek nélkül), majd hívjuk meg a func() függvényt, megfelelően felparaméterezve. Ekkor nem fut le, hanem egyesével tudunk lépkedni a n beírásával. Ha már nem szeretnénk debug módban indítani, akkor hívjuk meg az undebug(func) függvényt.

Profiling

Nehezebb a dolgunk, ha a programunk funkcionálisan jól működik ugyan, viszont lassú. Ebben segítséget nyújthat az Rprof() függvény, ami mintavételezéssel készít statisztikákat. Alapértelmezésben nem csinál semmi hasznosít; ha pl. memória problémáink vannak, akkor érdemes így kiadni: Rprof(memory.profiling = TRUE). Az alapértelmezett mintavételezési gyakoriság 2 századmásodperc, így a fenti parancs kiadása után egy legalább ennyi ideig futó programot indítsunk. Ha befejeződött, akkor adjuk ki a summaryRprof() parancsot.

Az adatok beszerzése és tisztítása

Kurzus: https://www.coursera.org/learn/data-cleaning/

Nyers adat -> feldolgozás -> tiszta adat. A következő 4 dologra van szükség:

  • nyers adat (annak a forrása, a beszerzés időpontja),
  • tiszta adat (minden mért változó egy oszlop legyen; mindegyik mérés egy sor legyen; egy táblázat egyfajta adatot tartalmazzon; több tábla esetén legyen kapcsoló oszlop),
  • a tiszta adat leírása (mértékegységekkel),
  • annak leírása, hogy hogyan kaptuk meg a nyers adatból a tiszta adatot (ha lehet, érdemes scriptet írni, és azt mellékelni).

(A fent ismertetett adatok beolvasása és átalakítása rész döntőrészt ebből a tanfolyamból származik.)

Felderítő adatelemzés

Kurzus: https://www.coursera.org/learn/exploratory-data-analysis/

Elvek:

  1. Összehasonlítások bemutatása
  2. Következtetés, mechanizmus, magyarázat bemutatása
  3. Többváltozós adatok használata
  4. A bizonyítékok integrálása
  5. A bizonyítékok leírása
  6. A tartalom a király

TODO: folytatni

Megismételhető kutatás

Kurzus: https://www.coursera.org/learn/reproducible-research/

Statisztikai következtetés

Kurzus: https://www.coursera.org/learn/statistical-inference/

Regressziós modellek

Kurzus: https://www.coursera.org/learn/regression-models/

Alkalmazott gépi tanulás

Kurzus: https://www.coursera.org/learn/practical-machine-learning/

Adattermékek fejlesztése

Adatok:

Áttekintés

Ez a kurzus arról szól, hogy hogyan tudunk statisztikai alkalmazásokat, elsősorban webalkalmazásokat készíteni. Alapértelmezésben az R "belső" diagramokat készít, amiből természetesen lehet képeket gyártani, és azokat feltölteni a weboldalra, itt viszont dinamikusabb lehetőségekről van szó, tehát az eredmény általában nem egy kép, hanem egy weboldal.

Shiny

Áttekintés

A kezdéshez az alábbiakra van szükség

Ez utóbbihoz még kell egy beállítás: létre kell hozni a c:\Users\[user]\Documents\.Renviron fájlt az alábbi tartalommal:

PATH="${RTOOLS40_HOME}\usr\bin;${PATH}"

Majd az RStudio-ban telepítsük a shiny library-t, és töltsük be:

install.packages("shiny")
library("shiny")

Shiny alkalmazások készítése: File → New File → Shiny Web App…

Pár szó a Shiny-ról:

  • Egy tipikus alkalmazás két részből áll: az ui.R tartalmazza a megjelenítést, a server.R pedig az üzleti logikát. (Ehhez a Multiple Files opciót válasszuk ki.)
  • Az eredmény HTML + CSS + JavaScript lesz, a Bootstrap keretrendszer használatával.
  • A rendszer reaktív, ami azt jelenti, hogy egy adat megváltozása automatikusan módosítja a másik adatot. Ez a programozásban is megjelenik; ld. később.
  • Mindkét fájlnak tartalmaznia kell a shiny betöltését: library(shiny).
  • Az ui.R fájlban építjük fel a HTML oldalt, R függvényhívások segítségével. A leggyakoribb HTML elemeket használhatjuk. Ehhez segítség: ?builder.
  • Ha létrehozunk egy új Shiny projektet, akkor alapból kapunk egy alkalmazást, amelyben ha módosítjuk a csúszka értékét, akkor automatikusan megváltozik egy hisztogram.
  • Induláskor a konzolra kiírja kb. ezt: Listening on http://127.0.0.1:4587. Ha a böngészővel megnyitjuk a megadott oldalt, akkor ugyanazt látjuk, mint az RStudio-ban.
  • Indítása: felül a Run App gombra kattintva, vagy konzolon a runApp() paranccsal.
  • Eredmény: egy háttérben futó webszerver; az oldalt böngészővel is meg tudjuk nyitni.
  • Az eredményt megoszthatjuk a weboldalunkon, vagy az RStudio oldalán is; ez utóbbit korlátozottan ingyenesen is lehetséges. (TODO: kifejteni)

Hello, Shiny world!

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Hello, Shiny world!"),
    sidebarLayout(
        sidebarPanel(
            h3("Sidebar Text")
        ),
        mainPanel(
            h3("Main Panel Text")
        )
    )
))

Magyarázat:

  • A shinyUI() függvény az egész grafikus felület.
  • A függvények egymásba ágyazva vannak benne, vesszővel elválasztva.
  • A függvények HTML tag nevekre hasonlítanak.
  • A fenti példa egy gyakori elrendezés.

server.R

library(shiny)

shinyServer(function(input, output) {
})

Magyarázat:

  • A shinyServer() tartalmazza a teljes üzleti logikát.
  • Paraméterként egy olyan függvényt vár, aminek egy input és egy output paramétere van.
  • Ez a példa abszolút minimalista.

Csúszka

Az alábbi példa bal oldalon egy csúszkát tartalmaz, míg jobb oldalon kiírja a csúszka aktuális értéke +10-et:

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Slider App"),
    sidebarLayout(
        sidebarPanel(
            h1("Move the Slider!"),
            sliderInput("slider1", "Slide me", 0, 100, 50)
        ),
        mainPanel(
            h3("Slider Value + 10:"),
            textOutput("text1")
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    output$text1 <- renderText(input$slider1 + 10)
})

Magyarázat:

  • Az ui.R forrásban definiáljuk a slider1-et inputként és text1-et outputként.
  • A server.R-ben ezeket felhasználjuk. A renderText() szöveget hoz létre.

Egy összetettebb példa

Az alábbi példa az alábbiakat tartalmazza:

  • szám beviteli mező,
  • két csúszka,
  • három jelölőnégyzet (checkbox),
  • outputként pedig egy 2 dimenziós diagram

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Plot Random Numbers"),
    sidebarLayout(
        sidebarPanel(
            numericInput("numberOfPoints", "How many random points should be plotted?", value = 500, min = 1, max = 1000, step = 1),
            sliderInput("sliderX", "Minimum and maximum X:", -100, 100, value = c(-50, 50)),
            sliderInput("sliderY", "Minimum and maximum Y:", -100, 100, value = c(-50, 50)),
            checkboxInput("showXlab", "Show/Hide X axis label", value = TRUE),
            checkboxInput("showYlab", "Show/Hide Y axis label", value = TRUE),
            checkboxInput("showTitle", "Show/Hide Title", value = FALSE),
        ),
        mainPanel(
            h3("Graph of random points"),
            plotOutput("plotPoints")
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    output$plotPoints <- renderPlot({
        set.seed(1)
        numberOfPoints <- input$numberOfPoints
        minX <- input$sliderX[1]
        maxX <- input$sliderX[2]
        minY <- input$sliderY[1]
        maxY <- input$sliderY[2]
        dataX <- runif(numberOfPoints, minX, maxX)
        dataY <- runif(numberOfPoints, minY, maxY)
        xlab = ifelse(input$showXlab, "X axis", "")
        ylab = ifelse(input$showYlab, "Y axis", "")
        main = ifelse(input$showTitle, "Title", "")
        plot(dataX, dataY, xlab = xlab, ylab = ylab, main = main, xlim = c(-100, 100), ylim = c(-100, 100))
    })
})

Érdemes megfigyelni a renderPlot() függvény paraméterét: kapcsos zárójelek között van. Valójában ez egy kód blokk, amit lefuttat.

Reaktív függvény

Az alábbi példa azt mutatja be, hogy hogyan tudunk létrehozni reaktív változót. Amit az output használ, annak reaktívnak kell lennie. Az input változók alapból reaktívak. Ha az output kiszámolásakor csak input változót használunk, akkor erre nincs szükség.

A példában két csúszkát tudunk beállítani, az eredmény pedig a kettő összege. A példa kedvéért az eredményt először kiszámoljuk egy reaktív változóba, majd megjelenítjük.

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Slider App"),
    sidebarLayout(
        sidebarPanel(
            h1("Move the Slider!"),
            sliderInput("sliderA", "A", 0, 100, 50),
            sliderInput("sliderB", "B", 0, 100, 50)
        ),
        mainPanel(
            h3("Sum:"),
            textOutput("text")
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    sumAB <- reactive({
        input$sliderA + input$sliderB
    })
    output$text <- renderText(sumAB())
})

Egy igazi webalkalmazás: lóerő jóslás

Az alábbi példa már egy "igazi" adattudományos jellegű: azt vizsgáljuk, hogy az autó teljesítménye (a lóereje) hogyan függ a fogyasztástól (az adat mérföld per gallon formában van megadva). Két modellt használunk:

  • az egyikben a lóerőt közvetlenül számoljuk a fogyasztásból, lineáris modell segítségével,
  • a másikban egy mesterséges törést teszünk a 20 mérföldenkénti gallon fogyasztáshoz oly módon, hogy az az alatti esetben ugyanúgy vesszük figyelembe, mint az első modellben, a másodikban viszont a fogyasztás mellett a fogyasztásból kivonunk húszat, és e kettő adat kombinációjaként készítjük a modellt.

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Predict Horsepower from MPG"),
    sidebarLayout(
        sidebarPanel(
            sliderInput("sliderMPG", "What is the MPG of the car?", 10, 35, value = 20),
            checkboxInput("showModel1", "Show/Hide Model 1", value = TRUE),
            checkboxInput("showModel2", "Show/Hide Model 2", value = TRUE)
        ),
        mainPanel(
            plotOutput("plot1"),
            h3("Predicted Horsepower from Model 1:"),
            textOutput("pred1"),
            h3("Predicted Horsepower from Model 2:"),
            textOutput("pred2")
        )
    )
))

server.R

library(shiny)
shinyServer(function(input, output) {
    mtcars$mpgsp <- ifelse(mtcars$mpg - 20 > 0, mtcars$mpg - 20, 0)
    model1 <- lm(hp ~ mpg, data = mtcars)
    model2 <- lm(hp ~ mpgsp + mpg, data = mtcars)

    model1pred <- reactive({
        mpgInput <- input$sliderMPG
        predict(model1, newdata = data.frame(mpg = mpgInput))
    })

    model2pred <- reactive({
        mpgInput <- input$sliderMPG
        predict(model2, newdata = data.frame(mpg = mpgInput, mpgsp = ifelse(mpgInput - 20 > 0, mpgInput - 20, 0)))
    })

    output$plot1 <- renderPlot({
        mpgInput <- input$sliderMPG
        plot(mtcars$mpg, mtcars$hp, xlab = "Miles Per Gallon", ylab = "Horsepower", bty = "n", pch = 16, xlim = c(10, 35), ylim = c(50, 350))
        if (input$showModel1) {
            abline(model1, col = "red", lwd = 2)
        }
        if (input$showModel2) {
            model2lines <- predict(model2, newdata = data.frame(mpg = 10:35, mpgsp = ifelse(10:35 - 20 > 0, 10:35 - 20, 0)))
            lines(10:35, model2lines, col = "blue", lwd = 2)
        }
        legend(25, 250, c("Model 1 Prediction", "Model 2 Prediction"), pch = 16, col = c("red", "blue"), bty = "n", cex = 1.2)
        points(mpgInput, model1pred(), col = "red", pch = 16, cex = 2)
        points(mpgInput, model2pred(), col = "blue", pch = 16, cex = 2)
    })

    output$pred1 <- renderText({
        model1pred()
    })

    output$pred2 <- renderText({
        model2pred()
    })
})

Itt a szerver kód egész bonyolult lett, tartalmaz reaktív függvényeket is.

Fejlett GUI: fülek

Az alábbi példa azt illusztrálja, hogy hogyan tudunk füleket létrehozni az eredmény oldalon. Ezt használva egész összetett oldalakat tudunk készíteni a szokásos két R porgram segítségével.

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Tabs!"),
    sidebarLayout(
        sidebarPanel(
            textInput("box1", "Enter Tab 1 Text:", value = "Tab 1!"),
            textInput("box2", "Enter Tab 2 Text:", value = "Tab 2!"),
            textInput("box3", "Enter Tab 3 Text:", value = "Tab 3!")
        ),
        mainPanel(
            tabsetPanel(type = "tabs",
                        tabPanel("Tab 1", br(), textOutput("out1")),
                        tabPanel("Tab 2", br(), textOutput("out2")),
                        tabPanel("Tab 2", br(), textOutput("out3"))
            )
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    output$out1 <- renderText(input$box1)
    output$out2 <- renderText(input$box2)
    output$out3 <- renderText(input$box3)
})

Interaktív alkalmazás

Az alábbi alkalmazás egy interaktív: a felhasználó kijelölhet tetszőleges számú (legalább kettő) pontot a diagramon, amelyre a program lineáris modellt illeszt. Itt tehát a diagram egyszerre input és output is.

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Visualize Many Models"),
    sidebarLayout(
        sidebarPanel(
            h3("Slope"),
            textOutput("slopeOut"),
            h3("Intercept"),
            textOutput("intOut")
        ),
        mainPanel(
            plotOutput("plot1", brush = brushOpts(
                id = "brush1"
            ))
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    model <- reactive({
        brushed_data <- brushedPoints(trees, input$brush1, xvar = "Girth", yvar = "Volume")
        if (nrow(brushed_data) < 2) {
            return(NULL)
        }
        lm(Volume ~ Girth, data = brushed_data)
    })

    output$slopeOut <- renderText({
        if (is.null(model())) {
            "No Model Found"
        } else {
            model()[[1]][2]
        }
    })

    output$intOut <- renderText({
        if (is.null(model())) {
            "No Model Found"
        } else {
            model()[[1]][1]
        }
    })

    output$plot1 <- renderPlot({
        plot(trees$Girth, trees$Volume, xlab = "Girth",
             ylab = "Volume", main = "Tree Measurements",
             cex = 1.5, pch = 16, bty = "n")
        if (!is.null(model())) {
            abline(model(), col = "blue", lwd = 2)
        }
    })
})

index.html ui.R helyett

Az ui.R helyett létrehozhatunk index.html fájlt is. Hozzunk létre egy könyvtárat az ui.R mellett www néven, nyissuk meg az alkalmazást egy böngészőben, mentsük le az eredményt a www könyvtárba index.html néven, majd töröljük le az ui.R-t. Az eredmény:

~/myproject/server.R
~/myproject/www/index.html

Ez ekvivalens azzal, ha van ui.R és nincs index.html.

Az alkalmazás közzététele

Az alkalmazást nem egyszerű közzétenni. Két módszert ismerek.

shinyapps.io

install.packages('rsconnect')
library(rsconnect)
  • A fenti oldalon középen kattintsunk a Show Secret nyomógombra. Ez megmutatja a titkos kulcsunkat. Másoljuk a parancsot a vágólapra, és adjuk ki RStudio-ban (a példában a titkos kulcsomat elrejtettem):
rsconnect::setAccountInfo(name='faragocsaba',
              token='321EF51587CF3BB5EE7136F9B6BC7902',
              secret='<SECRET>')
  • Az rsconnect::deployApp() segítségével publikáljuk:
rsconnect::deployApp('~/R/shiny/interactiveexample')

gist.github.com

  • Nyissuk meg a https://gist.github.com/ oldalt.
  • Lépjünk be a GitHub azonosítónkkal (hozzunk létre egyet, ha még nincs).
  • A jobb felső sarokban található + jelre kattintva hozzunk létre egy új Gist projektet.
  • Adjunk egy leírást a Gist description… mezőben.
  • A Filename including extension… legyen ui.R, és töltsük fel az UI kódot.
  • Kattintsunk az Add file-ra alul, és ugyanígy töltsük fel a server.R-t.
  • Alul kattintsunk a Create secret gist nyomógombra, és utólag publikáljuk, vagy a legördülő menüből a Create public gist-re.
  • Miután létrehoztuk, az URL-ben a nevünk ellett megjelenik egy hosszú kód, pl. c81c8f345915913022c15e656d74cab3 (az URL-em: https://gist.github.com/faragocsaba/c81c8f345915913022c15e656d74cab3). Ezt másoljuk ki.
  • Adjuk ki RStudio-ból az alábbi parancsot:
runGist('c81c8f345915913022c15e656d74cab3')

Ez lokálisan indít egy webszervert, ahol meg tudjuk nézni az eredményt.

Hello, Shiny Gadgets world!

A Shiny-nak van egy egyszerűsített változata, melynek neve Shiny Gadgets. Itt egy függvényen belül található az ui és a server. Ez csak RStudio-ban fut, böngészőben nem, így leginkább arra alkalmas, hogy elősegítse az adatelemzést.

A használatához szükség van a shiny és a miniUI csomagokra. Az alábbi program lényegáben semmit sem csinál; a Done nypmógombra kattintva be lehet fejezni:

library(shiny)
library(miniUI)

myFirstGadget <- function() {
    ui <- miniPage(
        gadgetTitleBar("My First Gadget")
    )
    server <- function(input, output, session) {
        observeEvent(input$done, {
            stopApp()
        })
    }
    runGadget(ui, server)
}

myFirstGadget()

Shiny Gadgets szorzás

Lássunk egy olyan példát is, ami műveletet hajt végre! Az alábbi program két számot szoroz össze. A Done-ra kattintva kiírja az eredményt:

library(shiny)
library(miniUI)

multiplyNumbers <- function(numbers1, numbers2) {
    ui <- miniPage(
        gadgetTitleBar("Multiply Two Numbers"),
        miniContentPanel(
            selectInput("num1", "First Number", choices=numbers1),
            selectInput("num2", "Second Number", choices=numbers2)
        )
    )
    server <- function(input, output, session) {
        observeEvent(input$done, {
            num1 <- as.numeric(input$num1)
            num2 <- as.numeric(input$num2)
            stopApp(num1 * num2)
        })
    }
    runGadget(ui, server)
}

multiplyNumbers(3, 2)

Interaktív Shiny Gadgets

Az alábbi program interaktív: ki tudunk választani pontokat, amit a végén kiír:

library(shiny)
library(miniUI)

pickTrees <- function() {
    ui <- miniPage(
        gadgetTitleBar("Select Points by Dragging your Mouse"),
        miniContentPanel(
            plotOutput("plot", height = "100%", brush = "brush")
        )
    )
    server <- function(input, output, session) {
        output$plot <- renderPlot({
            plot(trees$Girth, trees$Volume, main = "Trees!", xlab = "Girth", ylab = "Volume")
        })
        observeEvent(input$done, {
            stopApp(brushedPoints(trees, input$brush, xvar = "Girth", yvar = "Volume"))
        })
    }

    runGadget(ui, server)
}

pickTrees()

googleVis

Áttekintés

A Google-nek van egy saját diagram készítő JavaScript keretrendszere, melynek segítségével diagramokat tudunk helyezni a weboldalakra. Ízelítőt láthatunk a lehetőségekből az alábbi oldalon: https://developers.google.com/chart/interactive/docs/gallery.

A googleVis egy olyan R csomag, amely R-ből generál Google Chart-okat, azaz olyan HTML oldalt, ami a Google Chartot használja. Telepítése a szokásos:

install.packages("googleVis")

A betöltésekor hosszú jogi szöveget ír ki, de mivel nem szeretünk jogászkodni, ezt célszerű elnémítani:

suppressPackageStartupMessages(library(googleVis))

A Google Chart diagram googleVis megfelelője: gvis előtag, majd a diagram típus.

A tanfolyamban van egy olyan diagram típus, amit 2021 eleje óta nem használhatunk: ez a gvisMotionChart, mozgó diagram. Az oka: megjelenítéséhez Flash kell, de annak támogatását megszüntették, a böngészők többé nem tartalmazzák. Sajnos a tananyag pont ezzel indít.

Az alábbi paranccsal megnézhetjük a lehetőségek széles tárházát:

demo(googleVis)

Oszlopdiagram

Ez ugyan nincs a tananyagban, de indítsunk egy egyszerűbbel! A googleVis csomag tartalmaz egy Exports táblát, ami 3 oszlopot tartalmaz: az országot, a profitot és egy online flag-et:

         Country Profit Online
1        Germany      3   TRUE
2         Brazil      4  FALSE
3  United States      5   TRUE
4         France      4   TRUE
5        Hungary      3  FALSE
6          India      2   TRUE
7        Iceland      1  FALSE
8         Norway      4   TRUE
9          Spain      5   TRUE
10        Turkey      1  FALSE

Érdekessége ennek az, hogy Magyarország is rajta van. Készítsünk ebből egy oszlopdiagramot:

myBarChart <- gvisBarChart(Exports, options=list(width=500, height=400))

Ez automatikusan az országonkénti profit alapján készíti a diagramot.

A következő parancs megjeleníti a böngészőben:

plot(myBarChart)

Onnan is le tudjuk menteni a forrást, de közvetlenül is:

print(myBarChart)

Térkép

Igen látványos diagram a térkép:

myGeoChart1 <- gvisGeoChart(Exports, locationvar="Country", colorvar="Profit", options=list(width=600, height=400))

Megjelenítése hasonló a fentihez. Régióként megadhatjuk Európát:

myGeoChart2 <- gvisGeoChart(Exports, locationvar="Country", colorvar="Profit", options=list(width=600, height=400, region="150"))

Opciók

Az opciók megadásával egész jól testre szabható a diagram. Pl. a vonal daigamok lehetőségeiről a https://developers.google.com/chart/interactive/docs/gallery/linechart oldalon olvashatunk. Egy példa:

df <- data.frame(label=c("US", "GB", "BR"), val1=c(1,3,4), val2=c(23,12,32))
myLineChart <- gvisLineChart(df, xvar="label", yvar=c("val1","val2"),
    options=list(
        title="Hello World", legend="bottom",
        titleTextStyle="{color:'red', fontSize:18}",
        vAxis="{gridlines:{color:'red', count:3}}",
        hAxis="{title:'My Label', titleTextStyle:{color:'blue'}}",
        series="[{color:'green', targetAxisIndex: 0},{color: 'blue',targetAxisIndex:1}]",
        vAxes="[{title:'Value 1 (%)', format:'##,######%'}, {title:'Value 2 (\U00A3)'}]",
        curveType="function", width=500, height=300
    )
)

Több diagram egy oldalon

Egy oldalon több diagramot is meg lehet jeleníteni:

myBarLineChart <- gvisMerge(myBarChart, myLineChart, horizontal=FALSE) 
myMultipleCharts <- gvisMerge(myBarLineChart, myGeoChart2, horizontal=TRUE)

Plotly

Áttekintés

JavaScript alapú webes alkalmazásokat lehet ennek segítségével létrehozni. Ehhez tehát nem kell szerver, minden a böngészőben fut. Számos rendszerrel működik, pl. R, Python, Excel. Ebben a fejezetben a Plotly R csomaggal foglalkozunk.

Telepítése a szokásos módon történik.

install.packages("plotly")
library(plotly)

A plot_ly() függvényt kell használnunk. Az eredmény többnyire interaktív. Ha megnyitunk egy diagramot (ld. lejjebb), R Studio-ban az Export → Save as Web Page… menüpont kiválasztásával tudjuk lementeni. Az eredmény egy több megabájtos HTML oldal, ami döntőrészt bináris JavaScriptet tartalmaz.

A példák valamelyest eltérnek attól, ahogy a videóban bemutatják, az GitHub-os Rmd fájlokban viszont jól szerepel.

Pontdiagram

Angolul scatterplot. Az alábbi kód a fogyasztást (mérföld gallononként; itt tehát más a logika, mint a nálunk megszokott, ahol gallon mérföldenként lenne) ábrázolja a fontban mért tömeg függvényében (az mtcars az R belső adata):

plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter")

Ha az egyes pontok fölé megyünk, kiírja az x és y értékeket.

Kategorikus változó

Az alábbi paraméterrel a cilinderek száma szerint színezi a pontokat, így 3 adatot is meg tudunk jeleníteni:

plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter", color = ~factor(cyl))

Folytonos változó

A következő példában folytonos változóval jelenítjük meg a - ha jól értem - a motor hengerűrtartalmát:

plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter", color = ~disp)

Méret

A színen kívül a mérettel is "játszhatunk", ezáltal újabb, immáron negyedik dimenziót adhatunk az ábrához. A példában a következőket használjunk:

  • x-koordináta: tömeg (font)
  • y-koordináta: fogyasztás (mérföld gallononként, a mi logikánkkal: fogyasztás inverze)
  • szín: cilinderek száma
  • méret: teljesítmény (lóerő)
plot_ly(mtcars, x = ~wt, y = ~mpg, type = "scatter", color = ~factor(cyl), size = ~hp)

3 dimenziós pontdiagram

Az alábbi parancs 3 dimenzióban ábrázolja az adatokat:

plot_ly(mtcars, x = ~wt, y = ~mpg, z = ~hp, color = ~factor(cyl), type="scatter3d")

Egész látványos, jól forgatható diagramot eredményez!

Vonaldiagram

Az egyik leggyakoribb diagram típus a vonaldiagram. Példa (az airmiles egy olyan R adat, ami egy évi 24 megfigyelést tartalmazó idősorozat 1937-1960 között; az összes repült mérföldet tartalmazza):

data("airmiles")
plot_ly(x = ~time(airmiles), y = ~airmiles, type = "scatter", mode = "lines")

A diagram interaktív: nagyítható; ha a vonal fölé visszük az egeret, akkor kiírja a koordinátákat.

Több vonal egy diagramon

A tananyag példája kissé nyakatekert, így itt egy általam létrehozott adatkeretet jelenítünk meg:

column_type <- c("A", "A", "A", "A", "B", "B", "B", "B", "C", "C", "C", "C")
column_x <- c(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4)
column_y = c(30, 44, 42, 58, 24, 29, 32, 28, 57, 48, 68, 49)
my_df <- data.frame(type = column_type, x = column_x, y = column_y)
plot_ly(my_df, x = ~x, y = ~y, color = ~type, type = "scatter", mode = "lines")

Hisztogram

A hisztogram példában az USA 70 nagyvárosában mért csapadék mennyiséget jelenítjük meg:

plot_ly(x = ~precip, type = "histogram")

Dobozdiagram

Dobozdiagramként 3 írisz faj szirom hosszát ábrázoljuk:

plot_ly(iris, y = ~Petal.Length, color = ~Species, type = "box")

Hőtérkép

A hőtérkép illusztrálására ismét saját példát kerestem:

terrain = matrix(c(1,2,1,3,2, 2,3,4,5,4, 2,3,5,6,4, 3,4,6,7,6, 2,4,5,4,3), nrow = 5, ncol = 5)
plot_ly(z = ~terrain, type = "heatmap")

3D felület

Igen látványos, 3 dimenziós, minden irányba forgatható hőtérképet kapunk akkor, ha a típust "surface"-re állítjuk (itt is eltértem a tanfolyam példájától):

plot_ly(z = ~terrain, type = "surface")

Térkép

A Plotly képes tárképeket is megjeleníteni. Itt egy az egyben átvettem a tananyag példáját, ami az USA államainak lakosságát mutatja be, 1975-ben:

# Create data frame
state_pop <- data.frame(State = state.abb, Pop = as.vector(state.x77[,1]))
# Create hover text
state_pop$hover <- with(state_pop, paste(State, '<br>', "Population:", Pop))
# Make state borders white
borders <- list(color = toRGB("red"))
# Set up some mapping options
map_options <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  showlakes = TRUE,
  lakecolor = toRGB('white')
)

plot_ly(z = ~state_pop$Pop, text = ~state_pop$hover, locations = ~state_pop$State, 
        type = 'choropleth', locationmode = 'USA-states', 
        color = state_pop$Pop, colors = 'Blues', marker = list(line = borders)) %>%
        layout(title = 'US Population in 1975', geo = map_options)

R Markdown

Áttekintés

A Markdown egy olyan jelölő nyelv, melyben egyszerű módon tudunk szöveget formázni. Tipikus kiterjesztése .md. A formázással kapcsolatos részleteket olvashatunk a Szövegszerkesztés oldalon. Emlékeztetőül egy egyszerű példa:

Szabad szöveg.

# Első fejezet

## Első alfejezet

Paragrafus szöveg.
Új sor. **Vastag**, *dőlt betűs*, <ins>aláhúzott</ins>, ~~áthúzott~~, `fix karakterszélességű` szöveg.

## Második alfejezet

Felsorolás:
* Első elem
* Második elem
* Harmadik elem

# Második fejezet

Táblázat:

| Név     | Darab |
|---------|-------|
| alma    | 5     |
| banán   | 3     |
| narancs | 6     |

A Markdown önmagában független az R-től. Az R Markdown ennek a jelölő nyelvnek a kiterjesztése oly módon, hogy tartalmazhat R programkódot, amit a rendszer kiértékel.

R Markdown készítése RStudio-ban

A következőképpen készíthetünk R Markdown fájlt RStudio-ban:

File → New File → R Markdown…

Itt adjunk neki valamilyen címet.

Megadhatjuk a dokumentum típusát. Ez lehet dokumentum (HTML, PDF (ehhez kell Latex), Word), prezentáció (kétféle HTML, PDF, PowerPoint), Shiny (dokumentum vagy prezentáció) vagy valamilyen sablon alapú. Itt válasszuk ki ezt:

Presentation → HTML (ioslides)

Itt két lehetőségünk van:

  • Ha az OK-ra kattintunk, akkor létrejön egy egész pofás dokumentum, amely tartalmaz 5 oldalt mindenféle példával.
  • Ha a Create Empty Document-re kattintunk, akkor értelemszerűen egy üres dokumentumot kapunk.

A kódot hamarosan megnézzük részletesebben; most csak próbáljuk ki. Ehhez kattintsunk fent középen erre:

Knit

Ha mindent jól csináltunk, akkor megjelenik egy ablak, melyben a nyilakkal tudunk navigálni az egyes diák között.

Ha más típusú eredményt szeretnénk akkor kattintsunk a Knit melletti lefele mutató nyílra, és válasszuk ki a megfelelő eredményt.

Dia beszúrása

A legtöbb Markdown formázást használhatjuk, ám mivel ez egy prezentáció, egy-két helyen máshogy kell írnunk.

  • Dia beszúrása a sor elején található két kettős kereszt jellel történik (##), amit a cím követ. Pl.
## Új dia

Tartalom.
  • Ebből az is következik, hogy a fejléc méretek "elcsúsznak": dián belül pl. a legnagyobb cím a három kettős kereszt (###) jelenti.
  • Ha cím nélkül szeretnénk beszúrni egy diát, akkor egy sorba három csillagot kell írnunk (***), pl.
***

Tartalom.

R parancsok kezelése

A generált példában a dokumentum elején van egy ilyen rész:

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```

Ezt egyelőre töröljük ki (ill. ha új dokumentumot hozunk létre, akkor ne írjuk bele); később visszatérünk ennek a jelentőségére.

R parancsokat a következő módon szúrhatunk be az R markdown dokumentumba.

```{r myplot}
plot(1:10, 1:10)
```

(A myplot) a diagram neve, melynek egyedinek kell lennie a dokumentumban.)

Ez esetben megjelenik a forrás és az eredmény is. Ha nem szeretnénk azt, hogy megjelenjen a parancs, akkor azt az echo=FALSE megadásával tudjuk megtenni:

```{r myplot, echo = FALSE}
plot(1:10, 1:10)
```

A fentihez hasonlóan, ha nem szeretnénk, hogy kiértékelődjön, akkor az eval=FALSE beállítást kell használnunk:

```{r myplot, eval = FALSE}
plot(1:10, 1:10)
```

És most térjünk vissza az elején kitörölt sorokra. Ha visszaírjuk, akkor azzal azt értjük el, hogy alapértelmezésben a parancs nem jelenik meg, az eredmény viszont igen. Ez esetben ha mégis csak a parancsot szeretnénk megjeleníteni, akkor azt a következőképpen tudjuk megtenni:

```{r myplot, eval = FALSE, echo=TRUE}
plot(1:10, 1:10)
```

Közzététel

Közzétenni az eredményt többféleképpen tudjuk.

Az egyik lehetőség a következő: a Knit parancs hatására keletkezik egy HTML oldal az Rmd fájl mellett; azt tetszőlegesen felhasználhatjuk. Ez egyébként egy binárisan kódolt JavaScript, szóval nem olvasható és nem is szép, de 100%-ban a böngészőben fut, és elvileg akármilyen webszerverre felt tudjuk tölteni.

A másik lehetőség az RPubs oldal, ahova ingyen feltölthetjük. Lépések:

  • Regisztráljunk az RPubs oldalon és lépjünk be.
  • A prezentációban kattintsunk a jobb felső sarokban a Publish gombra.
  • Válasszuk ki az RPub-ot.
  • Kattintsunk a Publish-ra.
  • Adjunk neki címet (Title, pl. My first R markdown project), egy elérést (Slug, pl. mytest) és opcionálisan egy leírást (Description), majd kattintunk a Continue gombra.
  • Megjelenik az eredményt; a böngészőnek az URL sávjában ki tudjuk másolni az URL-t.
  • Ugyanott törölni is tudjuk.

Leaflet

Áttekintés

A Leaflet egy JavaScript könyvtár, melynek segítségével térképeket tudunk létrehozni. Az R leaflet csomag segítségével ezeket az oldalakat JavaScript tudás nélkül is létre tudjuk hozni. A telepítése a szokásos

install.packages("leaflet")
library(leaflet)

Egy abszolút minimalista forrás, ami kirajzol egy üres OpenStreetMaps világtérképet:

addTiles(leaflet())

Ennek van egy, a dplyr csomagban definiált, ún. pipe operátoros megoldása is:

my_map <- leaflet() %>% addTiles()
my_map

Itt a balra eső rész (jelen esetben a leaflet(), ill. annak az eredménye) a tőle jobbra eső függvény (addTiles()) első paramétere lesz. Látszólag bonyolítja a kódot, viszont ez korlátlanul egymás után írható. Egy tipikus leaflet program tipikusan sok ilyen egymás után írt függvényhívásól áll; emiatt érdemes ezzel a módszerrel megbarátkozni és ezt alkalmazni.

Fontosabb oldalak:

Egyszerű jelölő

Egy térképre tipikusan jelölőket szoktunk helyezni. Lássunk egy példát!

my_map <- leaflet() %>% 
    addTiles() %>%
    addMarkers(lng=19.045669, lat=47.507121, label="Parlament")
my_map

Ha lefuttatjuk, akkor Budapest belvárosának egy részét látjuk csak, megjelölve a Parlamentet. Ha a jelölő fölé megyünk, akkor látjuk a Parlament feliratot.

TODO: kép

Érdemes megjegyezni, hogy a fenti szintaxissal megegyezik az alábbi:

my_map <- leaflet() %>% addTiles()
my_map <- my_map %>% addMarkers(lng=19.045669, lat=47.507121, label="Parlament")
my_map

Tehát így is felépíthetünk egy térképet, lépésről lépésre. Itt láthatjuk igazán a %>% operátor hasznát. Anélkül ugyanis még ezt az egyszerű példát is így kellene használni, ami kevésbé olvasható (és egy tipikus térkép ennél sokkal összetettebb):

addMarkers(addTiles(leaflet()), lng=19.045669, lat=47.507121, label="Parlament")

Ha az információt nem rögtön szeretnénk megjeleníteni, amikor a felhasználó az okon fölé megy, hanem csak akkor, amikor rákattint, a popup paramétert használhatjuk:

my_map <- leaflet() %>% 
    addTiles() %>%
    addMarkers(lng=19.045669, lat=47.507121, popup="Parlament")
my_map

További jelölő beállítások

Lássuk, milyen egyéb jelölő beállítások lehetnek! További részleteket a https://rstudio.github.io/leaflet/markers.html oldalon olvashatunk.

Több jelölő

Lássunk egy példát a több jelölőre! Az alábbi példa a budapesti főpályaudvarokat mutatja meg:

railway_stations_map <- leaflet() %>%
    addTiles() %>%
    addMarkers(
        lng=c(19.0560793, 19.0839882, 19.0247399),
        lat=c(47.5103368, 47.5004423, 47.5002831),
        label=c("Nyugati", "Keleti", "Déli")
    )
railway_stations_map

TODO: ábra

Adatkeret használata

A fenti kód kicsit strukturáltabb formában, adatkeret használatával:

railway_stations <- data.frame(
    lng = c(19.0560793, 19.0839882, 19.0247399),
    lat = c(47.5103368, 47.5004423, 47.5002831),
    label = c("Nyugati", "Keleti", "Déli")
)
railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addMarkers(lng=~lng, lat=~lat, label=~label)
railway_stations_map

Mivel egy térképen tipikusan egy-egy elemnek kevés tulajdonsága van (jelen példában: hosszúság, szélesség, címke), viszont sok adatot szeretnénk megjeleníteni (pl. csak Budapesten 41 darab vasútállomás vagy vasúti megálló található), érdemes az oszlopos megadás helyett soronkénti megadást választani. Eht viszont közvetlenül nem tudjuk megadni; vagy mátrixból konvertálunk több lépésben, van a tibble csomagot használjuk:

library(leaflet)
library(tibble)

railway_stations <- tribble(
    ~lng, ~lat, ~label,
    19.0560793, 47.5103368, "Nyugati",
    19.0839882, 47.5004423, "Keleti",
    19.0247399, 47.5002831, "Déli"
)
railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addMarkers(lng=~lng, lat=~lat, label=~label)
railway_stations_map

Különböző színek

Adódik a következő lépés: jó lenne különböző típusú dolgokat különböző színekkel jelölni. Sajnos alapból ezt nem támogatja a leaflet addMarkers() függvénye; ehhez az addAwesomeMarkers() függvényt kell használni, saját ikonkészlettel.

Ikonokat többféleképpen létrehozhatunk, pl. az ikonok URL-ének megadásával; ezekről a https://rstudio.github.io/leaflet/markers.html oldalon olvashatunk bővebben. Ugyancsak ezen az oldalon találtam egy olyan megoldást, amelyhez nem kell saját ikonkészletet feltölteni, hanem az ion könyvtárban definiáltakat tudjuk használni. A példát kibővítjük egyéb vasútállomás típusokkal:

  • piros: főpályaudvar
  • kék: pályaudvar
  • zöld: vasútállomás
  • kék: vasúti megálló

Mindegyikből létrehozunk kettőt-hármat:

library(leaflet)
library(tibble)

railway_stations <- tribble(
    ~lng, ~lat, ~label,
    19.0560793, 47.5103368, "Nyugati",
    19.0839882, 47.5004423, "Keleti",
    19.0247399, 47.5002831, "Déli",
    19.0884682, 47.4687654, "Ferencváros",
    19.0893862, 47.5253531, "Rákosrendező",
    19.1492484, 47.4634574, "Kőbánya-Kispest",
    19.0216216, 47.4651711, "Kelenföld",
    19.1008009, 47.5117573, "Zugló",
    19.2238918, 47.4371316, "Ferihegy"
)
icons <- awesomeIcons(
    icon = 'ios-close',
    iconColor = 'black',
    library = 'ion',
    markerColor = c("red", "red", "red", "blue", "blue", "green", "green", "orange", "orange")
)
railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addAwesomeMarkers(lng=~lng, lat=~lat, label=~label, icon=icons)
railway_stations_map

A fenti program az adatok további növekedésével nehezen karbantarthatóvá válik. Pl. tegyük fel, hogy felvesszük az összes budapesti vasúti megállót, majd módosítani szeretnénk rajta; a színt elég nehézkes lenne megtalálni. Ehelyett vegyük fel a típust az adatkeretbe, és egy függvény segítségével számoljuk ki a színt!

library(leaflet)
library(tibble)

railway_stations <- tribble(
    ~type, ~lng, ~lat, ~label,
    1, 19.0560793, 47.5103368, "Nyugati",
    1, 19.0839882, 47.5004423, "Keleti",
    1, 19.0247399, 47.5002831, "Déli",
    2, 19.0884682, 47.4687654, "Ferencváros",
    2, 19.0893862, 47.5253531, "Rákosrendező",
    3, 19.1492484, 47.4634574, "Kőbánya-Kispest",
    3, 19.0216216, 47.4651711, "Kelenföld",
    4, 19.1008009, 47.5117573, "Zugló",
    4, 19.2238918, 47.4371316, "Ferihegy"
)
getColor <- function(railway_stations) {
    sapply(railway_stations$type, function(type) {
        switch(
            type,
            "red",
            "blue",
            "green",
            "orange"
        )
    })
}
icons <- awesomeIcons(
    icon = 'ios-close',
    iconColor = 'black',
    library = 'ion',
    markerColor = getColor(railway_stations)
)
railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addAwesomeMarkers(lng=~lng, lat=~lat, label=~label, icon=icons)
railway_stations_map

Sajnos a példa igen összetetté vált, de egy bizonyos méret felett szükség van erre a karbantarthatóság miatt.

TODO: ábra

Klaszterezés

Tegyük fel, hogy sok pontunk van, pl. Budapesten ezer. Ez esetben a sok ikon teljesen eltakarja a térképet, és használhatatlanná teszi azt, pl.:

nonclustered_map <- leaflet() %>%
    addTiles() %>%
    addMarkers(
        lng=rnorm(1000, 19.07, 0.08),
        lat=rnorm(1000, 47.5, 0.05)
    )
nonclustered_map

TODO: ábra

Ilyen esetben az addMarkers() függvényben megadhatjuk a clusterOptions = markerClusterOptions() opciót, és megfelelően összevonja a pontokat:

clustered_map <- leaflet() %>%
    addTiles() %>%
    addMarkers(
        lng=rnorm(1000, 19.07, 0.08),
        lat=rnorm(1000, 47.5, 0.05),
        clusterOptions = markerClusterOptions()
    )
clustered_map

TODO: ábra

A klaszterezés az előzővel összekapcsolható, pl.:

railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addAwesomeMarkers(lng=~lng, lat=~lat, label=~label, icon=icons, clusterOptions = markerClusterOptions())
railway_stations_map

Jelmagyarázat

Jelmagyarázatot az addLegend() függvény segítségével tudunk hozzáfűzni. A fenti példát folytatva:

railway_stations_map <- railway_stations %>%
    leaflet() %>%
    addTiles() %>%
    addAwesomeMarkers(lng=~lng, lat=~lat, label=~label, icon=icons, clusterOptions = markerClusterOptions()) %>%
    addLegend(
        title="Jelmagyarázat", 
        position="topright", 
        color=c("red", "blue", "green", "orange"), 
        label=c("főpályaudvar", "pályaudvar", "vasútállomás", "vasúti megálló")
    )
railway_stations_map

TODO: ábra

Az egészet egybe rakva, az összes vasúti megállóval kiegészítve és némiképp felokosítva az alábbi eredményt kapjuk. A kódot helytakarékosság miatt összecsukva láthatjuk; ki kell nyitni a megtekintéséhez.

A végeredmény itt található: https://rpubs.com/faragocsaba/bpvasut.

TODO: ábra

Kör

Jelölők helyett köröket is rajzolhatunk. Ez különösen akkor lehet fontos, ha a kör mérete információt hordoz. Az alábbi példában Magyarország megyéinek a lakosságszámát illusztráljuk, ahol a kör mérete arányos az ott élő lakosok számával.

library(leaflet)
library(tibble)

hungary_county <- tribble(
    ~lng, ~lat, ~population, ~label,
    19.3466886, 46.6058070,  503825, "Bács-Kiskun",
    18.2452050, 46.0639136,  360704, "Baranya",
    21.0917859, 46.6734946,  334264, "Békés",
    20.8316648, 48.2221667,  642447, "Borsod-Abaúj-Zemplén",
    20.3035484, 46.4682023,  399012, "Csongrád-Csanád",
    18.5711316, 47.1688464,  417712, "Fejér",
    17.3104085, 47.7039240,  467144, "Győr-Moson-Sopron",
    21.5630863, 47.5029094,  527989, "Hajdú-Bihar",
    20.1130195, 47.8025447,  294609, "Heves",
    20.4778127, 47.2443900,  370007, "Jász-Nagykun-Szolnok",
    18.3234255, 47.6074306,  299207, "Komárom-Esztergom",
    19.5512564, 47.9928807,  189304, "Nógrád",
    19.4719105, 47.3991153, 1278874, "Pest",
    17.5926396, 46.4571473,  301429, "Somogy",
    22.0552721, 48.0267304,  552964, "Szabolcs-Szatmár-Bereg",
    18.5574856, 46.5192212,  217463, "Tolna",
    16.7706108, 47.1877150,  253551, "Vas",
    17.6978305, 47.1034677,  341317, "Veszprém",
    16.8632767, 46.7118773,  268648, "Zala",
    19.0543713, 47.4826327, 1752286, "Budapest (főváros)",
)
hungary_county_map <- hungary_county %>%
    leaflet() %>%
    addTiles() %>%
    addCircleMarkers(
        ~lng,
        ~lat,
        label=~label,
        radius=sqrt(hungary_county$population) / 50
    )
hungary_county_map

TODO: ábra

Téglalap

Téglalapot is rajzolhatunk a térképre az addRectangles() függvény segítségével. Itt két átlós csúcs koordinátáit kell megadni. Az alábbi példa azt illusztrálja, hogy a vidékünkön mekkora egy szélességi és hosszúsági kör mérete:

leaflet() %>%
  addTiles() %>%
  addRectangles(lng1=19, lng2=20, lat1=46, lat2=47)

TODO: ábra

Publikálás

Publikálni ezt is többféleképpen tudjuk:

  • Export → Save as Web Page… → adjunk neki egy nevet, és az így létrejövő HTML fájlt meg tudjuk osztani tetszőleges weboldalon.
  • Publish → Publish HTML… → RPubs → az rpubs.com oldalon tudjuk megosztani (előtte regisztrálnunk kell).

R csomagok létrehozása

Áttekintés

A leírások tele vannak azzal, hogy "easy", ami azt jelenti, hogy nem olyan könnyű (ha valóban könnyű lenne, nem kellene leírást készíteni; ennek a szakasznak az elkészítése is beletartott pár hosszú órába). Valójában elég sok buktatója van a dolognak. Az alábbiakban elkészítünk egy R csomagot RSudio-ban, ami két függvényt tartalmaz: egy kutya közönséges összeadást és a kutya közönséges szorzást. A leírást a https://ourcodingclub.github.io/tutorials/writing-r-package/ oldal alapján készítettem.

A lépések végrehajtásához szükség lesz két könyvtárra; ezeket előre telepítsük fel (remélhetőleg csak erre a kettőre; időközben többet is feltelepítettem, pl. desc, remotes):

install.packages("devtools")
install.packages("roxygen2")

Csomag generálása

Első lépésben hozzunk létre egy megfelelő projektet:

  • jobb felső sarokban kattintsunk a lefele mutató nyílra → New Project… → New Directory → R Package
  • Package name: tetszőleges, pl. mymath
  • Lent kiválaszthatunk egy könyvtárat, pl. ~/R/rpackage (az eredmény tehát a ~/R/rpackage/mymath/) könyvtárban lesz.
  • Create Project

Létrejött két könyvtár és pár fájl. Lássuk a lényegeseket!

A csomag részei

DESCRIPTION

Ahogy nevéből kitalálható, ez adja a csomag leírását. Valójában adja magát, hogy hogyan kell kitölteni. Az egyetlen dolog, ami nincs benne a sablonban, de hozzáadtam, az a licensz; úgy tűnik, licensz-fétisük van az R-eseknél.

Package: mymath
Type: Package
Title: Mathematical Calculations
Version: 1.0
Author: Csaba Farago
Maintainer: Csaba Farago <farago.csaba.phd@gmail.com>
Description: This package performs math operations.
    Addition and multiplication is implemented.
License: GPL-3
Encoding: UTF-8

NAMESPACE

Ide kerülnek az exportok, azaz pl. a csomag publikus függvényei. Egyelőre töröljük ki a fájlt; majd egy későbbi lépésben legeneráljuk.

R/ könyvtár

Ez a könyvtárszerkezet lényege, ugyanis ide kerülnek az R fájlok. Automatikusan létrejön egy hello.R, ami példát mutat. Töröljük ki. Hozzunk létre két fájlt az alábbi tartalommal:

R/myadd.R

#' Adding two numbers
#'
#' This function just adds two numbers. It returns the result.
#' Next line illustrating that this can be multiline.
#'
#' @param a the first number to be added
#' @param b the second number to be added
#' @return the sum of the two input values
#' @author Csaba Farago
#' @details
#' This function adds two numbers.
#' @export
myadd <- function(a, b) {
  result = a + b
  return(result)
}

R/mymultiply.R

#' Multiplying two numbers
#'
#' This function just multiplies two numbers. It returns the result.
#'
#' @param a the first number to be multiplied
#' @param b the second number to be multiplied
#' @return the prod of the two input values
#' @author Csaba Farago
#' @details
#' This function multiplies two numbers.
#' @export
mymultiply <- function(a, b) {
  result = a * b
  return(result)
}

Itt valósítjuk meg a függvényeket. Felette meghatározott formában kommentekbe írva adjuk meg azt a dokumentációt, ami szintén része lesz az eredménynek. Ez a Roxygen2 formátum. A példa mutat pár dolgot:

  • Az első sor a cím. Ennek rövidnek kell lennie.
  • Egy üres sor következik, majd egy hosszabb leírás.
  • A @param a paramétereket sorolja fel. Szóközzel elválasztva kell megadni a paraméter nevét, majd újabb szóköz után a leírását.
  • A @return a visszatérési értékről ad információt.
  • A @author ennek a függvénynek a szerzője (ami eltérhet a teljes csomag szerzőjétől).
  • A @details további technikai részleteket tartalmaz.
  • A @export azt jelzi, hogy ez része a csomag publikus interfészének, azt exportáljuk. A későbbiekben belekerül a NAMESPACE fájlba.

man/ könyvtár

Oda kerül majd a generált dokumentáció sajátos formátumban. Az ott található hello.Rd fájlt töröljük ki.

Generálás

Töltsük be a szükséges könyvtárakat:

library("devtools")
library("roxygen2")

Keressük meg a Build fület. Kicsit nehézkes megtalálni; előtte olyanok vannak, hogy Environment, History, Connections. Ott:

  • More → Configure Build Tools…
  • Build Tools → Generate documentation with Roxygen
  • Configure… → legyen minden bepipálva
  • Végül: Build → Install and Restart

Parancssorból a generálást az alábbi paranccsal hajthatjuk végre:

roxygenise()

Vizsgáljuk meg az eredményt!

  • DESCRIPTION: bekerült a Roxygen verziószáma RoxygenNote kulccsal.
  • NAMESPACE: belekerültek az exportok: export(myadd) és külön sorban export(mymultiply), valamint felül egy figyelmeztetés, hogy ne szerkesszük kézzel. Valóban ne tegyük, mert akkor a Roxygen nem tudja generálni.
  • man/ könyvtár: bekerült két .Rd kiterjesztésű fájl: myadd.Rd és mymutiply.Rd.

Használata

Most már ugyanúgy használhatjuk a csomagunkat, mint bármely másik csomagot. Betöltés:

library(mymath)

Parancsok használata:

> myadd(3, 2)
[1] 5
> mymultiply(3, 2)
[1] 6

Dokumentáció:

?myadd

Publikálás

Számos helyre publikálhatunk, most a GitHub-ot nézzük meg. Ez kellően általános, de mégse "piszkítjuk" vele össze a hivatalos R csomag tárolót, a CRAN-t. A lépés végrehajtásához git és GitHub ismeretekre van szükség, melynek részletes ismertetése túlmutat a dokumentum keretein.

  • Mindenekelőtt telepítsük fel a Gitet, regisztráljunk a GitHubon és hajtsuk végre a szükséges beállításokat, ahogyan az a Verziókövető rendszerek oldalon a megfelelő alfejezetekben le van írva.
  • Hozzunk létre egy publikus repót a csomag nevével, pl. mymath.
  • Abban a könyvtárban, ahol létrehoztuk a csomagot (az én esetemben ez C:/Users/Csaba/Documents/R/rpackage/mymath/) hozzunk létre egy git repository-t:
git init
  • Hozzunk létre tetszőleges szövegszerkesztővel egy .gitignore fájlt a fenti könyvtár gyökerében, az alábbi tartalommal (szükség esetén mással is kibővítve):
.Rproj.user/
.Rhistory
.RData
  • Adjuk hozzá a git repóhoz a fájlokat a szokásos módon:
git status
git add .
git commit -m "Initial commit."
  • Töltsük fel a GoitHubra a kódot (a saját azonosítónkkal):
git branch -M main
git remote add origin https://github.com/faragocsaba/mymath.git
git push -u origin main

Kipróbálás

  • Először töröljük a mymath csomagot:
remove.packages("mymath")
  • Indítsuk újra az RStudio-t.
  • Töltsük be a devtools csomagot, majd adjuk ki az install_github() parancsot a megfelelő paraméterrel:
library(devtools)
install_github("faragocsaba/mymath")
  • Töltsük be a csomagot, és már használhatjuk is:
library(mymath)
?myadd
myadd(3, 2)

Generikus függvények

Objektumorientáltság az R-ben

Az R-ben olyan értelembe vett klasszikus objektumorientáltság nincs, mint mondjuk a Java-ban, ám itt is bizonyos alapok meg vannak.

Az R-ben mindegyik objektumnak van osztálya, amelyet a class() függvénnyel tudunk megjeleníteni:

> class(5)
[1] "numeric"
> class("abc")
[1] "character"
> class(TRUE)
[1] "logical"

Az osztály persze nemcsak ilyen "primitív" lehet, pl.:

> x <- rnorm(100)
> y <- x + rnorm(100)
> fit <- lm(y ~ x)
> class(fit) 
[1] "lm"

Az R-ben kétféle osztály létezik, melynek típusa S3 és S4. Ezek tehát az S nyelvből öröklődnek. Ezek párhuzamosan léteznek. Mi most az S4-et nézzük meg részletesebben, de egy ponton rövid kitérőt teszünk az S3-ra is.

Az R-ben sajátosan értelmezett az objektumorientáltság: létre tudunk hozni osztályokat a setClass() függvény segítségével, példányosíthatjuk is a new() függvény segítségével, ám az osztályokra jellemző függvényeket nem tudunk benne létrehozni. Helyette viszont létre tudunk hozni ún. generikus függvényeket, amelyek különböző osztályú inputra különböző osztályú outputot adnak eredményül.

Generikus metódusok

Példák a generikus metódusokra:

  • mean(): ez S3 típusú
  • print(): ez is S3 típusú
  • plot(): ez S3 és S4 típusú is
  • show(): ez S4 típusú

Az S3 típusú generikus függvény megvalósításait a methods() függvénnyel tudjuk lekérdezni, pl.:

methods("mean")

Ez tehát azt írja ki, hogy mely S3 osztályok valósítják meg a mean() metódust.

Az S4 típusú osztályok metódusait a showMethods() függvénnyel tudjuk lekérdezni, pl.:

showMethods("mean")

Ha megpróbáljuk egy S3 generikus függvény metódusait lekérdezni showMethods() hívással, vagy S4 generikus függvény metódusait methods() hívással, akkor hibát kapunk.

Hogy mit jelent ez a gyakorlatban, lássunk egy plot() példát! Az alapértelmezett plot() a szóródiagram (angolul scatterplot):

set.seed(10)
x <- rnorm(100)
plot(x)

TODO: ábra

Viszont ha idősorozattá alakítjuk az inputot, akkor máris más lesz az eredmény:

set.seed(10)
x <- rnorm(100)
x <- as.ts(x)
plot(x)

TODO: ábra

Ha lekérdezzük a plot() generikus függvény S3 metódusait, akkor ott látjuk a plot.ts-t:

> methods("plot")
...
[45] plot.ts
...

Az előző diagram tehát az alábbival megegyezik:

set.seed(10)
x <- rnorm(100)
plot.ts(x)

De ez nem javasolt; a specifikus függvény helyett hívjuk meg megfelelő típussal a generikust.

Egy saját példa

Készítsünk el egy saját példát:

  • Hozzunk létre egy polygon osztályt (setClass()).
  • Valósítsuk meg rajta a plot metódust, ami kirajzol egy poligont (setMethod()).
  • Példányosítsuk a polygon osztályt (new()).
  • Hívjuk meg a plot() függvényt az imént létrehozott példányon.
library(methods)
setClass("polygon", representation(x = "numeric", y = "numeric")) 
setMethod("plot", "polygon",
    function(x, y, ...) {
        plot(x@x, x@y, type = "n", ...)
        xp <- c(x@x, x@x[1])
        yp <- c(x@y, x@y[1])
        lines(xp, yp)
    }
) 
p <- new("polygon", x = c(1, 3, 4), y = c(1, 3, 1))
plot(p)

TODO: eredmény

Az objektumorientált programozáshoz szokott szemnek kissé szokatlan ez a kódrészlet. Ráadásul eléggé nehézkes is, ugyanis noha a plot() függvény két paramétert vár, x-et és y-t, itt csak x-et kap, ami viszont tartalmaz x-et és y-t is. A x@x jelenti az x-et, az x@y az y-t, az y pedig nincs használva. A kódban az xp és az yp a kapott két lista kiegészítve a végén az első elemekkel, hogy bezáruljon a poligon. A példában egy hármoszöget rajzolunk ki.

Swirl

Áttekintés

A Swirl egy interaktív oktatási környezet. A Data Science Specialization sorozat szerzői készítették. Ez egy ugyanolyan R csomag, mint a többi, így a szokásos módon kell telepíteni:

install.packages("swirl")
library(swirl)

Majd az indítása:

swirl()

Érdemes "eljátszani" vele, hogy ráérezzünk a lehetőségekre (és korlátokra). Néhány lényegesen lehetőség:

  • Információt ír ki, és Enter lenyomásával tudunk tovább lépni.
  • Kapunk egy kérdést, és be kell írni a választ.
  • A kérdésre többféle válaszlehetőség közül választhatunk.
  • Végre tud hajtani R parancsokat, beleértve a diagram rajzolást is.
  • Kilépni az Esc nyomógombba tudunk.

Elég nehézkes egyébként, és nemcsak amiatt, mert kissé régimódinak tűnik, hanem pl. olyan egyszerű műveletet sem sikerült végrehajtani, mint pl. egy tananyag újraindítása. Elvileg a restart() paranccsal kell újraindítani (bizonyos dokumentációk szerint a reset() paranccsal), de mindkét esetben oda nem illő hibát kaptam, ám ténylegesen nem indította újra. Az RStudio újraindítása nem segített, ahogy a .Rdata fájl törlése sem, sőt, még a csomag újratelepítése sem. Mindegyiket végre kellett hajtani:

  • uninstall.packages("swirl")
  • az RStudio leállítása,
  • a .Rdata törlése,
  • az RStudio indítása,
  • install.packages("swirl")
  • library(swirl)
  • swirl()

A másik nehézkessége az az, hogy ha van a memóriában változó, akkor figyelmeztetést ír ki, de nem segít érdemben abban, hogy hogyan lehet megoldani. Íme:

rm(list=ls())

Szóval van vele számos probléma. Ettől függetlenül persze önmagában egy egész sok területre kiterjedő interaktív tananyagot készítettek, melyről mindenképpen érdemes tudnunk.

Ebben a szakaszban arról lesz szó, hogy hogyan tudunk ehhez hasonló anyagot készíteni.

Saját Swirl készítése

Saját Swirl készítéséhez fel kell telepíteni a swirlify csomagot:

install.packages("swirlify")
library(swirlify)

Célszerű készítenünk egy külön könyvtárat a Swirl számára, pl.:

dir.create("~/R/swirl")
setwd(file.path("~/R/swirl"))

Saját Swirl tananyagot többféleképpen készíthetünk, és érdemes ezekkel megismerkednünk, ugyanis sajnos nem mindig működik mindegyik.

swirlify()

Normál esetben - ha működik - ez a legegyszerűbb. Indítsuk el a következő parancsot:

swirlify("Lesson 1", "My First Course")

Egy szerkesztő ablak jelenik meg, ahol elég intuitív módon tölthetjük ki a kérdéseket.

  • A jobb felső sarokban töltsük ki értelemszerűen az adatokat. Legalább a nevünket adjuk meg.
  • Bal oldalon válasszuk ki a legördülő menüből a kérdés típusát. Pl. első körben hagyjuk az alapértelmezett Message-en.
  • Az Outputhoz írjunk be valamilyen szöveget, majd kattintsunk az Add Question-re.
  • Figyeljük meg, hogy jobb oldalon megjelenik az új bejegyzés.
  • A bal oldali lehetőségek közül válasszuk most ki a Command-ot.
  • Az Outputhoz most írjunk be egy kérdést.
  • A Correct Answer mezőbe írjuk be a helyes választ.
  • Az Answer Test az ellenőrző. Kattintsunk az alatta levő Make Answer Test from Correct Answer nyomógombra.
  • A Hint-hez írjunk be valamilyen segítséget (akár a helyes választ).
  • Kattintsunk ismét az Add Question-re.
  • Kattintsunk a Save Session-re.
  • Kattintsunk a Demo Lesson-re.
  • Ha mindent jól csináltunk, akkor az ablak bezáródik, és a konzolon ki tudjuk próbálni a leckét.

A swirlify() parancs a már korábban, akár más módon megkezdett lecke folytatására is alkalmas. Az eredmény egy lesson.yaml fájl ehhez hasonló tartalommal:

- Class: meta
  Course: My First Course
  Lesson: Lesson 1
  Author: Csaba
  Type: Standard
  Organization: private
  Version: 2.4.5

- Class: text
  Output: Addition comes.

- Class: cmd_question
  Output: 3 + 2 = 
  CorrectAnswer: 5
  AnswerTests: omnitest(correctExpr='5')
  Hint: 5

new_lesson()

A második leckét a fenti mintájára készítsük el, de most a new_lesson() parancs segítségével:

new_lesson("Lesson 2", "My First Course")

Ennek hatására fent megnyílik a lesson.yaml fájl. A fenti mintájára készítsünk egy kicsit mást, pl. szorzást:

- Class: meta
  Course: My First Course
  Lesson: Lesson 2
  Author: Csaba
  Type: Standard
  Organization: private
  Version: 2.4.5

- Class: text
  Output: Multiplication comes.

- Class: cmd_question
  Output: 3 * 2 = 
  CorrectAnswer: 6
  AnswerTests: omnitest(correctExpr='6')
  Hint: 6

Indítása:

demo_lesson()

Fájlok létrehozása

Ha egy fájlkezelőből megnézzük, akkor a következőt láthatjuk:

  • A könyvtárszerkezet a munkakönyvtárban létrehozott My_First_Course, majd az alatt a Lesson_1 és Lesson_2.
  • Benne található a lesson.yaml, ami a lényeg.
  • De van ott még 3, lényegében üres fájlt is: customTests.R, initLesson.R és dependson.txt.

Készítsük el most "gyalog" módszerrel a következő leckét!

  • Készítsünk egy másolatot a Lesson_2 könyvtár alapján Lesson_3 néven.
  • A lesson.yaml fájlt módosítsuk, a többit hagyjuk úgy, ahogy van. Pl. lehet most kivonás.
- Class: meta
  Course: My First Course
  Lesson: Lesson 3
  Author: Csaba
  Type: Standard
  Organization: private
  Version: 2.4.5

- Class: text
  Output: Subtraction comes.

- Class: cmd_question
  Output: 3 - 2 = 
  CorrectAnswer: 1
  AnswerTests: omnitest(correctExpr='1')
  Hint: 1

A set_lesson() paranccsal tudjuk beállítani az új leckét. Ezt vagy kiadjuk paraméter nélkül, és akkor ki tudjuk választani a megfelelő fájlt, vagy megadhatjuk neki paraméterként:

set_lesson("My_First_Course/Lesson_3/lesson.yaml")

Indítani a fenti alapján a demo_lesson() paranccsal tudjuk.

Egészen elképesztően nehézkes ez a rendszer, szóval tényleg csak a legelszántabbaknak ajánlom. Néhány dolog, amire jó, ha felkészülünk:

  • Ékezetes karaktereket egyáltalán nem fogad el.
  • Gyakran érthetetlen hibát ír ki.
  • Sok esetben logikátlan a felépítése.
  • A swirlify() sok esetben hiányos vagy egyenesen hibás, így a felületen kattingatással megnyert időt könnyen többszörösen elveszítjük a hibák keresésével.

Kérdés típusok

Ismerkedjünk meg néhány kérdés típussal!

Egyszerű üzenet

Ahogy fent láthattuk, a text típussal írhatunk ki olyan üzenetet, melyre nem várunk választ, csak Entert:

- Class: text
  Output: This is just a message. Press Enter to continue.

R parancs

A fenti cmd_question valójában R parancsot ad ki. Egy másik példa:

- Class: cmd_question
  Output: Let x be 3 + 2!
  CorrectAnswer: x <- 3 + 2
  AnswerTests: omnitest(correctExpr='x <- 3 + 2')
  Hint: x <- 3 + 2

Itt ha beírjuk a helyes választ (x <- 3 + 2), akkor az tényleg végrehajtódik az R-ben, tényleg létrejön egy x változó, melynek az értéke a kiszámolt 5 lesz, valamint a kiadott parancs még a history-ban is benne lesz.

Kitöltendő kérdés

Megtévesztő az előző lehetőség, ugyanis az ember azt gondolná, hogy az való a kitöltendő kérdésre. Ahhoz viszont van egy másik, a text_question:

- Class: text_question
  Output: What is the capital of Hungary? 
  CorrectAnswer: Budapest
  AnswerTests: omnitest(correctVal='Budapest')
  Hint: Type Budapest.

Itt hibás a sirlify(), erre ügyeljünk!

Többszörös választási lehetőség

A mult_question többszörös válaszadási lehetőséget biztosít. Ráadásul kellően intelligens ahhoz, hogy a lehetőségeket összekeveri. (Ahhoz már sajnos nem, hogy elég legyen megadni a helyes választ egyszer.)

- Class: mult_question
  Output: Multiple choice question. What is the capital of Germany?
  AnswerChoices: Berlin;Bonn;Munich
  CorrectAnswer: Berlin
  AnswerTests: omnitest(correctVal='Berlin')
  Hint: The capital of Germany is Berlin.

Egész hosszú időt eltöltöttem egyébként itt, mire kiderült, hogy nem tudja kezelni az ékezetes karaktereket (München). Ráadásul nem ennél a kérdésnél írt ki hibát!

Diagram

Diagramot a figure típussal tudunk létrehozni:

- Class: figure
  Output: This is a figure.
  Figure: squareplot.R
  FigureType: new

Ehhez persze szükség van ugyanabban a könyvtárban egy megfelelő squareplot.R fájlra is, pl.:

plot(1:10, (1:10)^2)

Ezt szintén létrehozhatjuk az RStudio-ban: File → New File → R Script, majd lementve a megfelelő könyvtárban. Annyira sajnos nem intelligens, hogy automatikusan létrehozza nekünk. Ezzel kapcsolatos kérdést nem tudunk feltenni közvetlenül, csak a következőben.

A FigureType a new mellett lehet additional is; ez esetben folytatja a már megkezdett ábrát.

További lehetőségeket ill. példákat találunk a http://swirlstats.com/swirlify/writing.html oldalon.

Publikálás

Az elkészített leckék publikálása könnyű nehézkes.

Első körben győződjünk meg arról, hogy a megfelelő kurzuson állunk. Így állíthatjuk be:

set_lesson("My_First_Course/Lesson_4/lesson.yaml")

Majd csomagoljuk be:

pack_course()

Ez valójában nem csak az adott leckét, hanem teljes kurzust becsomagolja, aminek az eredménye egy .swc kiterjesztésű fájl lesz.

A telepítéshez csak a swirl kell, a swirlify nem. A következőképpen tudjuk telepíteni (a fájlt a megfelelő könyvtárba másolva, pl. ~/R/swirl/):

install_course(swc_path = "My_First_Course.swc")

A swirl() parancs hatására ez indul el.

Elvileg ha a GitHub-ra töltjük fel, akkor az install_course_github("username", "coursename") paranccsal fel tudnánk telepíteni. Ezt viszont kipróbálni nem tudtam, ugyanis a saját felhasználónév gyökérbe nem tudok pusholni. Ide feltöltöttem: https://github.com/faragocsaba/swirl. Valójában néhány próbálkozás után a install_course_github("faragocsaba", "swirl/My_First_Course" elindult, le is töltötte, de utána hibával leállt. Ezt követően nem próbálkoztam.

A Swirl kurzusoknak ban egy saját központi gyűjtőhelye SCN (Swirl Course Network) néven (http://swirlstats.com/scn/). Onnan az install_course() paranccsal tudunk telepítnei, pl:

install_course("Getting and Cleaning Data")

Azt nem sikerült kiderítenem, hogy pontosan hol is van a telepíthető kurzusok listája; a következő két esélyes hely az alábbi:

R Presentations

Az R Prezentáció sokban hasonlít az R Markdown-hoz. Létrehozása RStudio-ban: File → New File → R Presentation. A fal felső sarokban megjelenik egy sablon, a jobb felsőben pedig az eredmény. Elindulni nem bonyolult; a sablon némiképp módosított változata pl. ez:

My First Presentation
========================================================
author: Csaba
date: 2021-04-05
autosize: true

First Slide
========================================================

This is a text.

- Bullet 1
- Bullet 2
- Bullet 3

Slide With Code
========================================================

```{r}
summary(cars)
```

Slide With Plot
========================================================

```{r, echo=FALSE, fig.width=20, fig.height=12}
plot(cars)
```

A jelölő nyelv szinte teljesen megegyezik az R Markdown-nal. A részletes ismertetése nem cél; konkrét problémával - bár ebben az esetben nem egyszerű - a net segíthet. Két funkciót viszont még érdemes megismerni:

  • A jobb felső sarokban a More melletti kis háromszögre kattintva View in Browser: megnyitja böngészőben. Ezt publikálás előtt mindenképpen érdemes megnézni, mert sajnos nem pont úgy néz ki böngészőben mint a gyors nézőben.
  • Publikálás: szintén a jobb felső sarokban, a More mellett van egy kék ikon, és az mellett is van egy kis háromszög; arra kattintva Publish Presentation. Az RPubs-ra tudjuk publikálni.

Kurzus végi feladat

Ez egy picit izmosabb a korábbi kettőnél.

Shiny alkalmazás

Kell készíteni egy Shiny applikációt, amely tartalmaz interakciót, reaktív diagramot, dokumentációt. Én a hasznaltauto.hu oldalon 20 véletlenszerű autó 3 adatát töltöttem le:

  • életkor (hónapban kifejezve),
  • a bevallott futott kilométerek száma (1000 km-ben),
  • a hengerűrtartalom (felfelé kerekítve, literben).

Nyilván adja magát a feltételezés, hogy az életkor korrelál a futott kilométerek számával. Volt még egy olyan feltételezés is, hogy a magasabb kategóriás autókat többet használják; ez utóbbi miatt kellett a hengerűrtartalom. (Általában a nagyobb hengerűrtartalom és az autó presztízse szintén korrelál.)

A felhasználó ki tudja választani azt, hogy mely autó kategóriákat szeretné látni. Ezen kívül magán a diagramon is ki tudj jelölni pontokat. A forrás:

ui.R

library(shiny)

shinyUI(fluidPage(
    titlePanel("Used cars - predict mileage"),
    p("The diagram below contains some used cars data: age (months), mileage (1000 kms) and cylinder capacity (see colors). The source of the data: hasznaltauto.hu."),
    p("You can select / deselect cylinder capacity ranges on the left hand side. Additionally you can select points on the right hand side."),
    p("The diagram shows lines indicating the linear model of mileage ~ age. On the left hand side you see how many thousand kms the car ran monthly."),
    sidebarLayout(
        sidebarPanel(
            checkboxInput("show_a", "0.0 < cylinder capacity <= 1.0", value = TRUE),
            checkboxInput("show_b", "1.0 < cylinder capacity <= 2.0", value = TRUE),
            checkboxInput("show_c", "2.0 < cylinder capacity <= 3.0", value = TRUE),
            checkboxInput("show_d", "3.0 < cylinder capacity", value = TRUE),
            textOutput("slopeOut"),
        ),
        mainPanel(
            plotOutput("used_cars_plot", brush = brushOpts(id = "used_cars"))
        )
    )
))

server.R

library(shiny)

shinyServer(function(input, output) {
    used_cars <- reactive({
        used_cars <- data.frame(
            age_month =    c( 46,  53, 163, 160, 144, 164, 176, 163, 189,  55,  68, 144, 122, 128,  34,  58,  87, 104, 251, 232,  41),
            mileage_tkm =  c( 64,  53, 193, 287, 199, 227, 225, 214, 136,  59, 128, 220, 183, 100,  30, 125,  98,  99, 112, 216,  29),
            cyl_capacity = c(3.5, 5.0, 1.6, 2.0, 2.0, 2.0, 1.8, 2.5, 1.4, 1.6, 2.2, 2.2, 4.4, 1.0, 3.0, 2.0, 0.6, 1.6, 1.0, 1.0, 4.6)
        )
        used_cars <- transform(
            used_cars, 
            cyl_category = 
                ifelse(cyl_capacity <= 1.0, "red", 
                       ifelse(cyl_capacity <= 2.0, "green", 
                              ifelse(cyl_capacity <= 3.0, "blue", 
                                     "yellow")))
        )
        if (!input$show_a) {
            used_cars = used_cars[used_cars["cyl_category"] != "red",]
        }
        if (!input$show_b) {
            used_cars = used_cars[used_cars["cyl_category"] != "green",]
        }
        if (!input$show_c) {
            used_cars = used_cars[used_cars["cyl_category"] != "blue",]
        }
        if (!input$show_d) {
            used_cars = used_cars[used_cars["cyl_category"] != "yellow",]
        }
        brushed_data <- brushedPoints(used_cars, input$used_cars, xvar = "age_month", yvar = "mileage_tkm")
        if (nrow(brushed_data) < 2) {
            used_cars
        } else {
            brushed_data
        }
    })

    output$slopeOut <- renderText({
        used_cars = used_cars()
        if (nrow(used_cars) >= 2) {
            model_brushed <- lm(mileage_tkm ~ age_month, data = used_cars)
            slope = model_brushed[[1]][2] * 1000
            slope_str = format(round(slope, 0), nsmall = 0)
            paste(slope_str, "kms / month")
        } else {
            "NA"
        }
    })

    output$used_cars_plot <- renderPlot({
        used_cars = used_cars()
        plot(used_cars$age_month, 
             used_cars$mileage_tkm,
             main = "Used cars for sale",
             xlab = "Age (month)",
             ylab = "Mileage (1000 km)",
             xlim = c(0, 300),
             ylim = c(0, 300),
             cex = 1.5,
             pch = 16,
             col = used_cars$cyl_category)
        legends = c()
        cols = c()
        if (nrow(used_cars) >= 2) {
            legends = c("all cars")
            cols = c("black")
            model_all <- lm(mileage_tkm ~ age_month, data = used_cars)
            abline(model_all, col = "black", lwd = 3)
        }
        if (input$show_a) {
            used_cars_a <- used_cars[used_cars["cyl_category"] == "red",    c("age_month", "mileage_tkm")]
            if (nrow(used_cars_a) >= 2) {
                legends = c(legends, "0.0 < cc <= 1.0")
                cols = c(cols, "red")
                model_a <- lm(mileage_tkm ~ age_month, data=used_cars_a)
                abline(model_a, col="red", lwd = 2)
            }
        }
        if (input$show_b) {
            used_cars_b <- used_cars[used_cars["cyl_category"] == "green",  c("age_month", "mileage_tkm")]
            if (nrow(used_cars_b) >= 2) {
                legends = c(legends, "1.0 < cc <= 2.0")
                cols = c(cols, "green")
                model_b <- lm(mileage_tkm ~ age_month, data=used_cars_b)
                abline(model_b, col="green", lwd = 2)
            }
        }
        if (input$show_c) {
            used_cars_c <- used_cars[used_cars["cyl_category"] == "blue",   c("age_month", "mileage_tkm")]
            if (nrow(used_cars_c) >= 2) {
                legends = c(legends, "2.0 < cc <= 2.0")
                cols = c(cols, "blue")
                model_c <- lm(mileage_tkm ~ age_month, data=used_cars_c)
                abline(model_c, col="blue", lwd = 2)
            }
        }
        if (input$show_d) {
            used_cars_d <- used_cars[used_cars["cyl_category"] == "yellow", c("age_month", "mileage_tkm")]
            if (nrow(used_cars_d) >= 2) {
                legends = c(legends, "3.0 < cc")
                cols = c(cols, "yellow")
                model_d <- lm(mileage_tkm ~ age_month, data=used_cars_d)
                abline(model_d, col="yellow", lwd = 2)
            }
        }
        if (length(legends) > 0) {
            legend(1,
                   300, 
                   legend = legends,
                   col = cols,
                   lty = 1,
                   lwd = 2,
                   cex = 0.8
            )
        }
    })
})

Az eredmény: https://faragocsaba.shinyapps.io/usedcars/.

R prezentáció

A feladat második része egy R prezentáció készítése volt, amely 5 oldalban részletezi a fenti feladat eredményeit. Az én megoldássom a következő:

Used cars
========================================================
author: Csaba Farago
date: 2021.04.04.
autosize: false

Overview
========================================================

This is the assignment task for the Developing Data Products course. I collected 20 car data from the Hungarian web side about selling used cars, page <https://www.hasznaltauto.hu/>. The collected data are the following:
* Age (in months)
* Mileage (in thousand kms)
* Cylinder capacity (in thousand cm3, rounded up)

In the analysis I checked how the total mileage per age changed totally and also in various categories. I found that the higher the cylinder capacity is, the mileage per month increases.

The added cars are listed on the last slide (Appendix).

Diagram
========================================================

![plot of chunk unnamed-chunk-1](assignment-figure/unnamed-chunk-1-1.png)

Results
========================================================

Here are the results of the analysis:
* **Mileage per month of all cars**: 809 km / month, meaning that an average car runs about 10.000 km in a year
* **Considering cylinder capacity**: the higher the capacity is, it is most likely that the mileage per age increases
* **Mileage on cylinder capacity breakdown**
 * *Low category* (at most 1.0): 409 km / month
 * *Lower-mid category* (between 1.0 and 2.0): 1012 km / month
 * *Upper-mid category* (between 2.0 and 3.0): 1399 km / month
 * *Upper category* (higher than 3.0): 1783 km / month

Appendix
========================================================

* *Low category*: [Suzuki Splash](https://www.hasznaltauto.hu/szemelyauto/suzuki/splash/suzuki_splash_1_0_glx_cd_ac_ujszeru_mo-i_auto_vez_szerv_konyv_teli-nyari_kerek_ulesfutes-16898109), [BMW I3](https://www.hasznaltauto.hu/szemelyauto/bmw/i3/bmw_i3_rex_automata-16894273), [Fiat Uno](https://www.hasznaltauto.hu/szemelyauto/fiat/uno/fiat_uno_1_0_i_e_2_tulajdonos_111_750km_napi_hasznalatban-16874226)
* *Lower-mid category*: [Citroen C2](https://www.hasznaltauto.hu/szemelyauto/citroen/c2/citroen_c2_1_6_by_loeb_vts_hdi_ritkasag_20-tol_hitelre_is-16526608), [Kia Ceed](https://www.hasznaltauto.hu/szemelyauto/kia/ceed/kia_ceed_sw_2_0_crdi_tx_ritkasag_10_legzsak_20-tol_hitelre_is-16549170), [Honda Accord](https://www.hasznaltauto.hu/szemelyauto/honda/accord/honda_accord_tourer_2_0_elegance_automata_automata_20-tol_hitelre_is-16644937), [Honda Accord](https://www.hasznaltauto.hu/szemelyauto/honda/accord/honda_accord_tourer_2_0_elegance_20-tol_hitelre_is-16654142), [Opel Astra](https://www.hasznaltauto.hu/szemelyauto/opel/astra_h/opel_astra_h_tt_1_8_enjoy_hitelre_is_tulajdonostol_tokeletes_tetovel_2_garnitura_kerek_felbor-16779290), [Honda Civic](https://www.hasznaltauto.hu/szemelyauto/honda/civic/honda_civic_1_4_sport_bar_honda-16832550), [Open Cascada](https://www.hasznaltauto.hu/szemelyauto/opel/cascada/opel_cascada_1_6_t_cosmo_automata_euro6-16900947), [Skoda Octavia](https://www.hasznaltauto.hu/szemelyauto/skoda/octavia/skoda_octavia_combi_2_0_tdi_cr_rs_euro_6-16755012), [Ford Focus](https://www.hasznaltauto.hu/szemelyauto/ford/focus/ford_focus_1_6_ti-vct_trend_plus_st_line_magyarorszagi_99e_km_1_tulaj_vegig-16886209)
* *Upper-mid category*: [Volkswagen Crafter](https://www.hasznaltauto.hu/kishaszonjarmu/volkswagen/crafter/volkswagen_crafter_35_2_5_tdi_l3h2_kozep_hosszu_-_kozep_magas_4eu_raklapos-16786397), [Mazda Sport](https://www.hasznaltauto.hu/szemelyauto/mazda/6/mazda_6_sport_2_2_cd_revolution_top_automata_175_le_full_extra_magyarorszagi_tulajdonostol_makulatlan_szervizmult-16890653), [Mercedes Benz](https://www.hasznaltauto.hu/szemelyauto/mercedes-benz/e_220/mercedes-benz_e_220_t_cdi_avantgarde_automata-16900827), [BMW X6](https://www.hasznaltauto.hu/szemelyauto/bmw/x6/bmw_x6_xdrive30d_aut-16840463)
* *Upper category*: [Mercedes Benz](https://www.hasznaltauto.hu/szemelyauto/mercedes-benz/s_500/mercedes-benz_s_500_coupe_4m_9g-tronic-16680172), [Ford Mustang](https://www.hasznaltauto.hu/szemelyauto/ford/mustang/ford_mustang_fastback_5_0_ti-vct_v8_gt_automata-16045215), [BMW 7](https://www.hasznaltauto.hu/szemelyauto/bmw/7-es_sorozat/bmw_7-es_sorozat_7_activehybrid_automata-16832445), [Mercedes Benz](https://www.hasznaltauto.hu/szemelyauto/mercedes-benz/e_400/mercedes-benz_e_400_4matic_9g-tronic_m_o_i_vezetett_szervizkonyv-16896026)

Az eredmény itt található: https://rpubs.com/faragocsaba/usedcars.

Adattudomány záró projekt

Kurzus: https://www.coursera.org/learn/data-science-project

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License