A mérés során R-ben fogunk dolgozni, így a mérésre való felkészülésként az alábbi R bevezetőt szükséges átnézni. Ez a bevezető teljesen megegyezik a Facebook adatok elemzése R nyelven c. mérés segédletében található R bevezetővel.
Az R egy erősen funkcionális nyelv, ill. környezet statisztikai számításokhoz és grafikus ábrázoláshoz. Az R környezet egy ingyenes, komplett rendszer, amely könnyen kiterjeszthető, bővíthető. Többek között az alábbi feladatokhoz nyújt megoldást:
Az R letölthető a http://www.r-project.org/* oldalról. Egy részletesebb leírás a nyelvről elérhető a http://cran.r-project.org/doc/manuals/r-release/R-intro.html* oldalon, ami alapján ez a bevezető is készült.
A mérés során UNIX rendszer alatt használjuk az R-t. Az R-beli számításokhoz, elemzésekhez tartozó adatok egy working directory-ba kerülnek mentésre. Célszerű egy adott problémához tartozó számításokhoz egyetlen working directory-t használni, míg egy másik, teljesen különálló feladatnak új working directory-t létrehozni.
Az R indításához indítsunk el egy terminált, lépjünk abba a könyvtárba, amit working directory-ként szeretnénk használni, majd adjuk ki az R parancsot. Ekkor elindul az R. (A working directory automatikusan az a könyvtár lesz, ahonnan az R-t indítottuk.) Az indítás után egy parancssoros felületet látunk, ahol R parancsok megadásával elkezdhetünk dolgozni. Az R-beli parancssor a UNIX-os parancssorhoz hasonlóan kezelhető. (Fel-le nyilakkal lehet navigálni a korábbi parancsok között, tab-bal ki lehet egészíteni a parancsokat stb.)
Az R bezárásához adjuk ki a q() parancsot. Ekkor a program felteszi a kérdést, hogy szeretnénk-e menteni a munkafolyamathoz tartozó adatokat. Az elmentett adatok az R következő indításánál ismét rendelkezésre fognak állni. (Ha a megfelelő könyvtárból indítjuk az R-t.)
R-ben a parancsokat pontosvesszővel vagy új sorral zárhatjuk le.
Commentet #-tel írhatunk, a # utáni rész a sor végéig comment lesz.
Az elemi parancsok lehetnek kifejezések (expressions) vagy hozzárendelések (assignments). Kifejezések esetén a kifejezés kiértékelődik, és az eredmény kiíródik (alapesetben a képernyőre). Hozzárendelés esetén egy kifejezés értékét hozzárendeljük egy változóhoz. Ekkor a kifejezés értéke nem íródik ki automatikusan a kimenetre.
A source() függvény segítségével egy szöveges fájlba írt R parancsokat hajthatunk végre:
source("r_elemzes.r")Az R rendelkezik egy beépített súgóval, amely a UNIX rendszereken használatos man parancshoz hasonlóan működik. A parancsokhoz tartozó help oldalak leírják a parancs funkcióját, paramétereit, visszatérési értékét és egyéb hasznos információkat. Használata: ha pl. az átlagszámításra alkalmas mean() függvény leírását szeretnénk megtekinteni, adjuk ki az alábbi parancsot:
?meanA help-ből a q billentyűvel léphetünk ki.
Az R-beli adatstruktúrákat objektumoknak nevezzük. A legegyszerűbb adatstruktúra a vector. Egy vector minden eleme azonos típusú, így ezeket atomi struktúráknak nevezik. Az atomi struktúrákon belül is megkülöböztethetünk különböző típusokat. A legfontosabb atomi típusok: numeric (integer vagy double), logical, character. Vectorokat a c() függvénnyel hozhatunk létre, pl. egy x nevű 3 elemű numeric vector változót az alábbi módon hozhatunk létre:
x <- c(10.5, 3.6, 23.4)A <- operátor a hozzárendelés (assignment) operátor, amely értéket rendel egy változóhoz. (Használható helyette az = jel is, ill. használható a másik irányban is: ->.)
Vectorokkal végezhetünk aritmetikai műveleteket. Ekkor a műveletek a vectorok megfelelő elemein egyesével végrehajtódnak. Ha a műveletben szereplő vectorok nem ugyanolyan hosszúak, akkor az eredmény a műveletben szereplő leghosszabb vectorral megegyező hosszúságú lesz, és a számításkor a rövidebb vectorok értékei ciklikusan újra felhasználásra kerülnek, amíg el nem érik a leghosszabb vector hosszát. Pl.:
x <- c(2,3,4)
y <- c(10,11)
x + y + 1## [1] 13 15 15
Néhány függvény vectorok tulajdonságainak meghatározására:
-min, max: legkisebb, ill. legnagyobb elem értéke,
-length: a vector hossza,
-sum: a vector elemeinek összege,
-mean: átlag,
-var: variancia,
-summary: összefoglaló statisztikákat ad a vectorról (minimum, maximum, kvartilisek, medián, átlag, hiányzó értékek száma).
A numerikus vectorokhoz hasonlóan az R-ben kezelhetünk logikai (logical) vectorokat, amelyeknek háromféle eleme lehet: TRUE, FALSE, NA, ahol az NA a hiányzó értéknek (not available) felel meg. Logikai vectorokat feltételekkel hozhatunk létre, pl.:
x <- 20 > 10
x## [1] TRUE
x <- c(20>10, 1>10)
x## [1] TRUE FALSE
Az alapvető logikai operátorok a következők: <, <=, >, >=, ==, !=. Továbbá ha c1 és c2 logikai vectorok, akkor c1 & c2 az elemenkénti és kapcsolatnak felel meg, c1 | c2 az elmenkénti vagy kapcsolatot jelenti, ill. !c1 az elemenkénti negálást jelenti.
Vectorok (ill. más objektumok) tartalmazhatnak nem ismert, hiányzó adatokat. A hiányzó adatokat jelképező elemek értéke NA (not available). Minden NA értéken végrehajtott művelet eredménye szintén NA.
Az is.na(x) függvény egy logikai vectorral tér vissza, amelynek egy eleme pontosan akkor TRUE, ha az x vector megfelelő eleme NA értékű:
z <- c(1,2,3,NA)
ind <- is.na(z)
ind## [1] FALSE FALSE FALSE TRUE
Figyelem: az x == NA kifejezés eredménye egy x-szel megegyező hosszúságú vector, ami kizárólag NA értékeket tartalmaz. (Mivel minden NA értéken végrehajtott művelet értéke szintén NA.)
Egy vector elemeinek kiválaszthatjuk egy részhalmazát egy index vector segítségével, amelyet szögletes zárójelek között kell megadni a vector után. Az index vector négyféle típusú lehet:
x <- c(1, 2, 3, 4, 5, NA)
x[c(TRUE, FALSE)]## [1] 1 3 5
x[c(1, 4, 6)]## [1] 1 4 NA
x[c(-1, -4, -6)]## [1] 2 3 5
names(x) <- c("a", "b", "c", "d", "e", "f")
x[c("a", "d", "e")]## a d e
## 1 4 5
Egy részhalmaz-kiválasztás (indexelés) szerepelhet egy értékhozzárendelés bal oldalán is. Ekkor csak a kiválasztott elemek értékeire vonatkozik az értékadás. Pl.:
x[is.na(x)] <- 0
x## a b c d e f
## 1 2 3 4 5 0
A példában az x változó összes hiányzó értékét 0-val helyettesítjük.
A vector mellett további típusokat is használhatunk R-ben. Ezek közül a fontosabbak:
Mátrixok: többdimenziós vektorok. Valójában vektorok, amelyeket 2 vagy több indexszel indexelhetünk.
Factorok: a factorok lehetőségeket biztosítanak kategorikus változók kezelésére. (Azaz olyan változók kezelésére, amelyek csak véges sok értéket vehetnek fel.)
Listák: a vectorhoz hasonló típus, azonban egy listában az egyes elemek különböző típusúak is lehetnek.
Data frame-ek: a data frame-ek mátrixszerű struktúrák, melyekben az egyes oszlopok különböző típusúak lehetnek. Sok esetben egy data frame sorai az egyes entitásoknak, elemeknek felelnek meg, míg az oszlopok az entitások egy-egy tulajdonságát, jellemzőjét írják le. A data frame-ek igen gyakran használatosak az R-beli adatelemzések során.
Függvények: az R függvényei maguk is objektumok, amelyeket a munkafolyamat adatai között tárolhatunk, így az R könnyen kiterjeszthető új funkciókkal.
Az objektumok (alap)típusát a typeof() függvénnyel kérdezhetjük le. Az objektumokat átkonvertálhatjuk más típusra is, azonban az ilyen esetekben körültekintően kell eljárni, mivel a nem megfelelő konverzió adatvesztéshez vezethet. A konverzióra az as függvények szolgálnak. Pl. az atomi típusokra való konvertálásra az as.numeric(), as.logical(), as.character() stb. függvények. (De nem csak az atomi típusokra érhetőek el a konverziós függvények!)
A class() függvény segítségével meghatározhatjuk, hogy milyen osztályba tartozik egy objektum. A class() függvény egy absztrakt típust ad vissza, amely az objektum viselkedését határozza meg. ha különböző műveleteket hajtunk velük végre. Az osztálytípusok lehetővé teszik generikus függvények alkalmazását, melyek más és más módon viselkednek attól függően, hogy milyen class-hez tartozik a függvénynek átadott paraméter. A class() függvény sok esetben megegyezik a typeof()-fal, azonban nem mindig. A typeof() azt jelzi, hogy a memóriában, milyen módon tárolódik az adott objektum, míg a class() egy absztrakt típust ad meg, amely az objektumnak egy attribútuma. Pl.: egy data frame esetében a class() függvény data.frame-mel fog visszatérni, míg a typeof() függvény list-tel, mivel a data frame-ek valójában listaként kerülnek tárolásra a memóriában.
A factorok speciális típusú vector változók, melyek egy diszkrét osztályozást, csoportosítást valósítanak meg. Azaz factorokat olyan változók leírására alkalmazunk, amelyek véges sok számú diszkrét értéket vehetnek fel. R-ben egyaránt lehetőség van rendezett (ordered) és rendezetlen (unordered) factorok használatára.
Factorokat a factor() függvénnyel hozhatunk létre. A factor különböző lehetséges értékeit a levels() függvénnyel érhetjük el.
Pl. tfh. van egy vectorunk, amely egy kísérletünk alanyainak nemét tartalmazza:
nemek <- c("fiu", "lany", "fiu", "fiu", "lany", "lany", "lany", "fiu")
nemek <- factor(nemek)
nemek## [1] fiu lany fiu fiu lany lany lany fiu
## Levels: fiu lany
levels(nemek)## [1] "fiu" "lany"
A data frame-ek mátrixszerű objektumok, melyeknek az egyes oszlopai különböző típusúak lehetnek, azonban az egy oszlopon belüli elemek mindig azonos típusúak. A data frame-ek valójában listaként kerülnek tárolásra. Sok esetben a data frame sorait entitásoknak feleltethetjük meg, míg oszlopait az entitások tulajdonságainak. Az oszlopok névvel rendelkeznek, melyeket a names() függvénnyel érhetünk el. A sorok számát az nrow() függvénnyel határozhatjuk meg.
A data frame-eket a vectorokhoz hasonlóan indexelhetjük, azonban mivel mátrixszerű struktúrájúak, ezért két indexet kell alkalmaznunk vesszővel elválasztva. Amennyiben elhagyunk egy indexvectort, akkor az indexvectornak megfelelő dimenzióban minden érték kiválasztásra kerül. Az első index a sorokat jelenti, míg a második az oszlopokat. A hagyományos szögletes zárójelek mellett a $ jelet is alkalmazhatjuk egy adott oszlop teljes kijelöléséhez. Néhány példa:
emberek <- data.frame(kor=c(45,50,34), nem=c("fiu","fiu","lany"), magassag=c(178, 184, 165))
emberek## kor nem magassag
## 1 45 fiu 178
## 2 50 fiu 184
## 3 34 lany 165
names(emberek)## [1] "kor" "nem" "magassag"
nrow(emberek)## [1] 3
emberek[,]## kor nem magassag
## 1 45 fiu 178
## 2 50 fiu 184
## 3 34 lany 165
emberek$kor## [1] 45 50 34
emberek[3,1]## [1] 34
emberek$kor[3]## [1] 34
emberek[,c("kor","nem")]## kor nem
## 1 45 fiu
## 2 50 fiu
## 3 34 lany
emberek[1,]## kor nem magassag
## 1 45 fiu 178
Új oszlopot az alábbi módon adhatunk hozzá egy data frame-hez:
emberek$tomeg <- c(78, 82, 56)
emberek## kor nem magassag tomeg
## 1 45 fiu 178 78
## 2 50 fiu 184 82
## 3 34 lany 165 56
Egy oszlopot az oszlop NULL-ra állításával törölhetünk:
emberek$tomeg <- NULL
names(emberek)## [1] "kor" "nem" "magassag"
A data frame-ek létrehozásának egyik egyszerű és gyakori módja a fájlban tárolt adatok beolvasása a megfelelő függvénnyel. Táblázatosan tárolt adatok beolvasására szolgál a read.table() függvény, ill. annak egyéb változatai. A mérésen használt adatokat egy .csv fájlból kell beolvasni, amihez célszerű a read.csv() függvényt használni. (Ez a függvény valójában a read.table() függgvényt hívja, pusztán a paraméterek alapértelmezett értékeiben különbözik a két függvény.)
Az egyik leggyakrabban használt függvény ábrák megjelenítésére a plot() függvény. Ez egy generikus függvény: a viselkedése az első paraméter típusától (class-étől) függ.
Ha x és y vectorok, akkor a plot(x, y) függvényhívás egy scatterploton fogja ábrázolni az y értékeket az x értékek függvényében.
Ha x egy numerikus vector, akkor a plot(x) függvény a vector értékeit az index függvényében ábrázolja.
Ha f egy factor típusú változó, y pedig numerikus vector, akkor a plot(f) függvény oszlopdiagramon (barplot-on) ábrázolja f-et, míg a plot(f, y) az f factor minden osztályához (level-éhez) tartozó y értékeket egy-egy boxplot-on ábrázolja.
A fenti példákon kívül a plot() függvény még számos egyéb módon alkalmazható, ill. a megfelelő paraméterbeállításokkal az ábrák egyes tulajdonságai továbbfinomíthatóak.
Az R-ben a statisztikai modellek létrehozása általában egy ún. formula alapján történik. A formula megadja a statisztikai modellben a célváltozót, ill. a célváltozó meghatározására felhasznált magyarázó változókat.
Természetesen minden típusú statisztikai modell esetén más és más módon kerül felhasználásra a megadott formula. Pl.: az lm() függvény segítségével lineáris regressziós modellt hozhatunk létre a paraméterben megadott formula alapján, a randomForest() függvénnyel (randomForest package-ből) random forest modellt hozhatunk létre a megadott formula alapján és így tovább. Vagyis a formula a modellekben felhasznált változókat határozza meg, nem a modell típusát. A különböző típusú modellek létrehozásához pedig különböző függvényeket alkalmazunk.
Az alábbiakban néhány egyszerű példán keresztül mutatjuk be a formulák felépítését:
y ~ x vagy y ~ x + 1: az y célváltozót az x változó segítségével, ill. egy konstans bias értékkel való eltolással magyarázzuk. Az első változatban a bias érték implicit módon jelenik meg, míg a másodikban explicite megadtuk a formulában. A két eset azonos.
y ~ 0 + x vagy y ~ x - 1 vagy y ~ -1 + x: az y változót kizárólag az x változóval magyarázzuk, bias nélkül.
y ~ x1 + x2: az y változót az x1 és x2 változóval (és az implicit bias értékkel) magyarázzuk.
Példa: egy lineáris regressziós modell létrehozása:
lin_model <- lm(y ~ x1 + x2, data=dataset)A fenti példában az lm() függvény segítségével egy lineáris regressziós modellt hozunk létre az y változó becslésére az x1 és x2 változók (és egy bias érték) segítségével. Az y, x1, x2 változók a dataset nevű data frame változó egy-egy oszlopának felelnek meg. Az lm() függvény visszatérési értéke egy illesztett lm típusú (class-ű) modell változó, amely leírja a létrehozott modell tulajdonságait, és lehetővé teszi a létrehozott modell alkalmazását későbbi példákon.
Az R nyelv lehetővé teszi a felhasználók számára saját függvények (függvény típusú objektumok) definiálását. A függvények lehetővé teszik az R nyelv kiegészítését új funkciókkal, így az egyik legalapvetőbb és legfontosabb komponensei az R nyelvnek. Az R rendszer legtöbb függvénye (pl.: mean(), var() stb.) szintén R nyelven van megírva, így hasonlóak a felhasználók által definiált függvényekhez. (Azonban vannak rendszerfüggvények, amelyek C-ben vagy Fortran nyelven kerültek implementálásra.)
Az alábbi példa egy derékszögű háromszög átfogójának hosszának kiszámítására alkalmas függvényt definiál. A függvénynek két paramétere van, amelyek a befogók hosszát adják meg.
atfogo <- function(a,b){
c <- sqrt(a^2 + b^2)
c
}A függvény visszatérési értéke a függvényen belül utoljára végrehajtott utasítás értéke lesz. A fenti példában így a c változó értékével fog visszatérni a függvény. A visszatérésre használható a return() függvény is.
A függvények paraméterei rendelkezhetnek alapértelmezett értékkel is, melyet a függvény definiálásakor a paraméterlistában a parameter_name=default_value módon adhatunk meg. Ha egy paraméter rendelkezik alapértelmezett értékkel, akkor a függvény hívásakor elhagyható az adott paraméter, és ekkor az alapértelmezett értékkel kerül meghívásra a függvény.
A paramétereket két alapvető módon adhatjuk meg a függvények számára: pozíció szerint vagy név szerint. Az alábbi példában először pozíció alapján történik a paraméterek megadása, majd név szerinti megadással hajtjuk végre ugyanazt a függvényhívást:
atfogo(5, 6)## [1] 7.81025
atfogo(b=6, a=5)## [1] 7.81025
Ahogy a példából is látható, név szerinti paramétermegadás esetén bármilyen sorrendben megadhatjuk a paramétereket, míg pozíció szerinti megadás esetén a függvény definiálásakor meghatározott sorrendben kell megadni a paramétereket. A két paramétermegadási módot kombinálni is lehet: a paraméterek kezdődhetnek pozíció szerinti megadással, majd utánuk következhetnek név szerint megadott paraméterek is. (Továbbá lehetőség van részleges név alapján történő paramétermegadásra. Ekkor a paraméterek teljes nevének megadása helyett pusztán az első részét adjuk meg, úgy hogy az már egyértelműen meghatározza az egyik paramétert. Azonban ez a módszer kevésbé átlátható kódot eredményez, így nem alkalmazzák gyakran.)
Az R függvényei package-ekben, csomagokban vannak tárolva. Egy package tartalma csak akkor érhető el, ha a package be van töltve. Az installált package-eket a library() függvénnyel listázhatjuk ki paraméterek nélkül. Egy package betöltéséhez szintén a library() függvényt alkalmazhatjuk paraméterként megadva a már korábban installált package nevét. Pl.:
library(randomForest)A mérés során jegyzőkönyvet kell készíteni az elvégzett feladatokról. A jegyzőkönyv tartalmazza a mérést végző hallgatók nevét, neptunkódját, a mérés helyét és idejét, ill. az elvégzett feladatok megoldását. A jegyzőkönyvben a feladatok megoldásánál szerepeljenek az adott feladat megoldásához használt R kódok, valamint a kimenetük releváns része is, ami hozzájárult a feladat megoldásához, a következtetések levonásához. (Irreleváns, ill. nagyon hosszú kimenetek ne kerüljenek a jegyzőkönyvbe.) A jegyzőkönyv mellett a feladatok megoldására írt R kódokat külön is be kell adni egyetlen megoldasok.r nevű fájlban. Fontos, hogy a megoldasok.r fájlban ne legyen hibás kód, vagyis futtatni lehessen az összes feladat megoldását a source(‘megoldasok.r’) paranccsal.
A feladatok elvégzéséhez szükséges adatfájlokat a tantárgy weboldaláról lehet letölteni. Emellett szükséges, hogy a mérési számítógépen telepítve legyen az R az alábbi package-ekkel együtt:
A mérés során szöveges, angol nyelvű filmkritikák véleményanalízisével fogunk foglalkozni. A célunk olyan algoritmusok létrehozása, amelyek minél pontosabban képesek a filmkritikákban rejlő véleményeket számszerűen jellemezni.
A mérés során felhasznált adathalmazok az IMDB-ről származó szöveges filmkritikák. Az adathalmazban szerepelnek címkézett kritikák (ahol a felhasználó véleménye számszerűen is ismert) és címkézetlen kritikák (ahol csak a filmkritika szövege ismert, a vélemény számszerű értéke nem). Az adathalmazban bináris címkék szerepelnek: a 0 negatív, míg az 1 pozitív véleményt jelöl. A semleges/átlagos értékelésű kritikák ki lettek szűrve az adathalmazokból. (Azok a kritikák kaptak 0-ás címkét, amelyeknél a felhasználó 5-nél rosszabbra értékelte a filmet 1-től 10-ig terjedő skálán, míg a 6-nál jobb értékelésű kritikák kaptak 1-es címkét.)
Az adathalmazban 25000 címkézett és 50000 címkézetlen kritika található.
Állítsuk be a véletlenszámgeneráláshoz használt seed értékét a munkánk legelején. Ezzel lehetővé tesszük, hogy megismételhessük a korábbi futtatásaink eredményét akkor is, ha véletlenszámgenerálást tartalmazó függvényeket is futtattunk.
set.seed(1)Az adatokat egy data frame típusú változóba olvassuk be. Ügyeljünk arra, hogy a data frame változói (oszlopai) megfelelő típusúak legyenek. Azaz az id és a review változók ne factorok legyenek, hanem character típusúak, ugyanakkor a sentiment változó legyen factor! Továbbá figyeljünk a fájlok struktúrájára (elválasztó karakter, idézőjelek hiánya)! (Segítség: read.csv().)
Milyen nevű változók szerepelnek a beolvasott data frame-ekben?
Ellenőrizzük, hogy a megfelelő számú sor került-e a data frame-ekbe. Hány soruk van?
Mik a változók típusai a data frame-ekben?
Mi a pozitív és negatív kritikák gyakorisága? (Melyikből hány darab van?)
Ábrázoljuk a kritikák hosszúságának eloszlását (az eloszlás becsült sűrűségfüggvényét)! (Segítség: plot(), density(), nchar().)
A nyers szöveges kritikák HTML-tageket tartalmaznak. Szűrjük ki ezeket a HTML-tageket, hogy ne okozzanak gondot a későbbi elemzések során! Továbbá szűrjük ki az írásjeleket is a szövegekből, és konvertáljuk őket kisbetűs szövegekké. (A címkézett és a címkézetlen adatokon is végezzük el a műveleteket!)
Segítség: tolower(), ill. a HTML tag-ek és az írásjelek kiszűréséhez használjuk az alábbi függvényt:
removeHTMLTags <- function(htmlStringVector) {
result <- gsub("<.*?>", " ", htmlStringVector)
result <- gsub("[[:punct:]]", "", result)
# MAGYARAZAT:
# (1): [:punct:] --> R-beli regularis kifejezesekben az irasjeleket jeloli
# (2): [abc] --> barmelyik karakter a felsoroltak kozul: a vagy b vagy c
# (1) es (2) alapjan: [[:punct:]] --> barmelyik irasjel
#
result
}(Megjegyzés: A gsub függvény leírása megtekinthető a szokásos módon, a ?gsub függvényhívással. Látható, hogy a fenti függvény a HTML-tageket nem egyszerűen eltávolítja, hanem egy szóközre cseréli. Ennek oka, hogy így, ha korábban két szót kizárólag egy HTML-tag választott el - mondjuk egy sortörés - akkor a HTML-tag eltávolításával sem fog egybecsúszni a két szó. Ha viszont a szóközzel esetleg több white-space karakter kerülne egymás mellé, akkor az nem okoz problémát, azokat később egyszerűen eltávolíthatjuk.)
Véletlenszerűen kiválasztva egy kritikát (ami eredetileg tartalmazott HTML-taget), nézzük meg, valóban eltávolításra kerültek-e a HTML-tagek és az írásjelek!
A címkézett adathalmazunkat osszuk két részre. A kritikák ~70%-a alkossa a tanuló adathalmazt, míg a maradék része a tesztadathalmazt. A tanuló- és teszthalmazt mentsük el egy-egy új változóba. A szétválasztás véletlenszerűen történjen! (Segítség: sample() és round().)
Készítsünk egy nagyon egyszerű véleményanalízis-modellt egy előre létrehozott szólista alapján, melyben a szavakhoz rendelkezésre áll egy-egy számszerűsített érzelmi érték. A mérés során az AFINN listát használjuk fel, amelyet Finn Årup Nielsen hozott létre. A lista egy -5 és 5 közötti egész értékű skálán jellemzi a szavakban rejlő véleményt, érzelmi jelentést.
3.1.1 Olvassuk be a listát (AFINN-111.txt fájl)! Hány elem található a listában?
3.1.2 Nevezzük át a lista oszlopait ,,word’‘és ,,sentiment_value’’ nevekre!
3.1.3 Ábrázoljuk hisztogrammon az egyes véleményértékek eloszlását (előfordulásaiknak számát)! Segítség: hist().)
A listát a következő módon fogjuk felhasználni: megszámoljuk, hogy a listában szereplő egyes szavak hányszor fordulnak elő az egyes kritikákban, majd egy kritikához az adott kritikában a szavak előfordulásainak számából álló vektor a szavak érzelmi jelentéseit leíró vektor skaláris szorzatát rendeljük. Ez az érték fogja jellemezni a kritikában rejlő véleményt, ez lesz az egyetlen magyarázó változó a modellünkben.
Az alábbi lépésekkel építsük fel a kezdeti modellünket:
# afinnList <- TODO, lasd: 3.1.1 es 3.1.2 feladat
library(stringr)
# Ez sokaig is eltarthat, ~10 perc. Figyeljunk, hogy ne futtassuk le tobbszor feleslegesen!
documentTermMxTrain <- apply(
as.matrix(labeledTrain$review_clean),
1,
str_count,
pattern=afinnList$word)
documentTermMxTrain <- t(documentTermMxTrain)
documentTermMxTest <- apply(
as.matrix(labeledTest$review_clean),
1,
str_count,
pattern=afinnList$word)
documentTermMxTest <- t(documentTermMxTest)
# a matrixok merete:
dim(documentTermMxTrain)
dim(documentTermMxTest)A kódban a stringr package str_count() függvényét használtuk a kritikákban az egyes szavak előfordulásainak megszámolására. Próbáljuk meg értelmezni a fenti kódot! Mit csinál az apply() függvény?
3.2.2 Hozzunk létre a teszthalmazban egy új oszlopot score néven, melynek értékei legyenek a létrehozott mátrix sorainak és a lista szavainak értékeiből álló vektornak a skaláris szorzata. (Segítség: mátrix-szorzás: A %*% b.)
3.2.3 Az alábbi kód segítségével tekintsük meg a kiszámolt értékek eloszlását a két célváltozó két osztályára nézve:
library(ggplot2)
ggplot(labeledTrain,aes(score, fill = as.factor(sentiment))) + geom_density(alpha = .2)Az ábra alapján mit mondhatunk el a létrehozott magyarázó változóról, milyen teljesítményt várhatunk majd a modellünktől?
A létrehozott modellt sokféle mutatóval lehet értékelni. A mérés során a modell jellemzésére a konfúziós mátrixot, a precision-t, a recall-t és a pontosságot fogjuk használni. Írjunk egy függvényt ezen teljesítménymutatók meghatározására! A függvénynek két paramétere legyen: a modell által predikált értékek vektora és a helyes értékek vektora (mindkét vektor csak 0-kat és 1-eseket tartalmazhat). A függvény visszatérési értéke pedig legyen egy 4-elemű lista. A lista elemei tartalmazzák az egyes teljesítménymutatókat, így a neveik legyenek: confusion_matrix, precision, recall és accuracy. A függvény váza:
function <- getPerformanceMeasures(predictions, realValues){
# konfuzios mx meghatarozasa
confusion_mx <- # TODO
# precision meghatarozasa
precision <- # TODO
# recall meghatarozasa
recall <- # TODO
# accuracy meghatarozasa
accuracy <- # TODO
list(confusion_mx=confusion_mx, precision=precision, recall=recall, accuracy=accuracy)
}Az újabb modell felépítéséhez használni fogjuk a különböző szövegbányászati eszközöket tartalmazó tm package-et. A package részletes bemutatása nem része a labornak, így ez az alfejezet a package használatához ad egy kis segítséget, amivel a labor feladatai könnyebben megoldhatóak már.
A tm package corpus-ban tárolja a dokumentumok gyűjteményét. A példakód bemutatja, hogyan tudunk a tanuló és teszthalmazunkból corpust csinálni, és azon egy-két alapműveletet elvégezni.
library(tm)
# corpusok letrehozasa
corpusTrain <- Corpus(VectorSource(labeledTrain$review_clean))
corpusTest <- Corpus(VectorSource(labeledTest$review_clean))
# stop-szavak eltuntetese a corpus-okbol:
corpusTrain <- tm_map(corpusTrain, removeWords, stopwords("eng"))
corpusTest <- tm_map(corpusTest, removeWords, stopwords("eng"))
# felesleges white-space karakterek eltuntetese a corpus-okbol:
corpusTrain <- tm_map(corpusTrain, stripWhitespace)
corpusTest <- tm_map(corpusTest, stripWhitespace)
# dokumentum-kifejezes matrix felepitese egy corpusbol:
documentTermMxTrain <- DocumentTermMatrix(corpusTrain)
documentTermMxTrain # ez a matrix altalaban nagyon nagy!
# a dokumentum-kifejezes mx csokkentese:
# csak azon szavak meghagyasa amik legalabb a dok-ok 5%-aban szerepelnek:
documentTermMxTrainReduced <- removeSparseTerms(documentTermMxTrain, .95)
# dokumentum-kifejezes matrix felepitese egy corpusbol csak meghatarozott szavakkal:
documentTermMxTrain2 <- DocumentTermMatrix(corpusTrain, list(dictionary = c("igen", "nem")))
# dokumentum-kifejezes matrix konvertalasa "sima" matrix tipussa:
# (ezutan lehetove valik a matrix szokasos modon valo indexelese R-ben)
documentTermMxTrain2 <- as.matrix(documentTermMxTrain2)Az AFINN-lista alapján létrehozott modell nem volt túl jó, ami nem is meglepő. Valószínűleg jobb eredményt kaphatunk, ha nem egy előre létrehozott szólistával próbálunk dolgozni, hanem dinamikusan, a rendelkezésre álló adathalmazok segítségével próbáljuk az aktuális problémának megfelelő szavakat összegyűjteni. Ebben a feladatban egy ilyen modellt építünk.
Szövegbányászati feladatokban a dokumentumok szavainak fontosságának jellemzésére egy gyakran használt metrika a tf-idf érték, amely egy szó gyakoriságát egy adott dokumentumban hasonlítja a szó gyakoriságához a dokumentumok gyűjteményében.
Számítása két lépésből áll:
Ezek után a tf-idf értéket a tf és idf érték szorzataként kaphatjuk meg.
Pl.: ha a ,,popcorn’‘szó egy kritikában kétszer előfordul, és 75,000 kritikából 314 tartalmazza a ,,popcorn’‘szót, akkor a ,,popcorn’’ szó tf-idf értéke az adott kritikában: 2 * log(75000/314) = 4.756.
Mielőtt nekilátnánk a tf-idf értékek kiszámításához szűkítsük le a szavaink listáját. Az összes szó túl sok lenne, ráadásul a legtöbbjük nem is hordoz releváns információt számunkra. Nekünk azok a szavak kellenek, melyek gyakran fordulnak elő és erős véleményt hordoznak.
Ahogy az előző pontban írtuk, azokat a szavakat szeretnénk megkeresni, melyek gyakoriak és erős véleményt hordoznak. A címkézett tanuló halmaz segítségével a következő lépések elvégzésével válasszuk ki a szavainkat:
Lépések:
1-1 corpus létrehozása a pozitív és a negatív kritikákból (azok szövegeiből).
Stop-szavak eltüntetése a corpusokból.
Dokumentum-kifejezés mátrix felépítése a corpusokból.
Dokumentum-kifejezés mátrixok redukálása a ritkán előforduló kifejezések törlésével.
A redukált dokumentum-kifejezés mátrixok oszlopneveiből a szólisták létrehozása.
(Segítség: a tm package bemutatott függvényei, ill. a colnames() függvény.)
Ezek után hozzuk létre a tf-idf mátrixot. A mátrix oszlopai a kiválasztott szavaink, a sorok a kritikák, és az i. sor j. oszlopa megadja az i. kritikában a j. szó tf-idf értékét.
A mátrixot együttesen hozzuk létre a tanuló és a teszthalmazra úgy, hogy a két halmazt egyesítjük a számítás előtt. A számítások végeztével pedig újra válasszuk ketté a mátrixot úgy, hogy a tanuló halmazba és a teszthalmazba az egyesítés előtti, ugyanazon sorok kerüljenek be.(Erre azért van szükség, mert a tf-idf érték számításánál relevánsabb értéket kapunk, ha az idf értéket a teljes rendelkezésre álló halmazon számoljuk.)
Lépések:
Egy új data frame létrehozása a tanuló és a teszthalmaz ,,összefűzésével’’.
Az új adathalmaz kritikáiból corpus létrehozása.
A corpusból dokumentum-kifejezés mátrix létrehozása az előző feladatban előállított szólista segítségével (a szólista elemei legyenek a mátrix kifejezései).
A dokumentum-kifejezés mátrix konvertálása R-beli mátrix objektummá (as.matrix()).
tf értékek számítása, majd idf értékek számítása.
tf-idf értékek számítása.
A létrehozott tf-idf mátrix szétválasztása két részre a tanuló és a teszthalmaznak megfelelően.
(Segítség: rbind(), a tm package bemutaott függvényei, as.matrix(), apply().)
Ismét használjunk randomForest modellt! A modellt a tf-idf mátrix tanuló részén tanítsuk. A randomForest() függvény hívásakor állítsuk az ntree paraméter értékét 50-re, hogy ne kelljen sokat várni a futásra (így nyilván feltehetőleg pontatlanabb lesz valamivel a modellünk).
A létrehozott modellel hozzunk létre predikciókat a teszthalmaz kritikáira, majd használjuk a korábbi feladatban megírt függvényünket az új modell értékelésére! Az eredmények alapján hasonlítsuk össze az új modellt a korábbival! Mit tapasztalunk? Hogyan lehetne esetleg tovább javítani a jelenlegi modellen (bármilyen ötlet)?
Az adatok egy korábbi kaggle versenyről származnak. A mérés során a verseny adatainak csak egy részhalmazát használjuk. A verseny oldaláról - regisztrációt követően és a versenyhez való csatlakozással - letölthetőek az adatok (2015.11.04.): *https://www.kaggle.com/c/word2vec-nlp-tutorial/data*.
Az AFINN szólista pedig az alábbi linkről került letöltésre (2015.11.04.): *http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010*.
A mérés anyagának fő forrása: *https://drive.google.com/file/d/0B_sqyEYBKc1wVm4xN0NvQlJlNWc/view*. A tanulmány a mérés anyagánál egy jóval részletesebb elemzésen vezet végig.