Ishodi učenja:

  • Provesti pretprocesiranje, tokenizaciju i izračun TF/TF-IDF.

  • Usporediti reprezentacije (bag-of-words, n-gram) i njihove posljedice.

  • Analizirati najvažnije pojmove po dokumentima/korpusima.

  • Izraditi reproducibilan pipeline analize teksta u R-u.

Tekst kao podatak

U analizi podataka tekst predstavlja poseban tip informacije jer je nestrukturiran. Za razliku od numeričkih ili kategorijskih varijabli, tekstualni zapisi (objave, komentari, dokumenti) sastoje se od prirodnog jezika, koji je potrebno pretvoriti u analitički prikladan oblik.

Proces pretvaranja teksta u podatke naziva se text mining ili text analytics. Njegov cilj je izdvojiti korisne obrasce, pojmove ili teme iz velikih korpusa teksta. U praksi se taj proces može shvatiti kao transformacija teksta iz neorganiziranog stanja u strukturirani skup značajki (features) koji se mogu analizirati statističkim metodama.

Tipični koraci u analizi teksta uključuju:

  • prikupljanje i organizaciju dokumenata u korpus
  • pretprocesiranje i čišćenje teksta
  • tokenizaciju
  • izgradnju reprezentacije teksta
  • analizu frekvencija ili težina pojmova

U ovoj lekciji fokus je na osnovnim metodama reprezentacije teksta i njihovoj analizi u R-u.


Korpus i dokumenti

Osnovna jedinica analize teksta je dokument.

Dokument može biti:

  • novinski članak
  • objava na društvenim mrežama
  • opis proizvoda
  • odgovor u anketi
  • web stranica organizacije
  • poglavlje u knjizi.

Skup dokumenata naziva se korpus.

Formalno:

\[ Corpus = \{d_1, d_2, ..., d_n\} \]

gdje je:

  • \(d_i\) – i-ti dokument
  • \(n\) – broj dokumenata u korpusu

Korpus je početna struktura nad kojom se provodi analiza teksta.


Pretprocesiranje teksta

Prije analize tekst je potrebno očistiti i standardizirati.

Ovaj proces naziva se pretprocesiranje.

Tipični koraci uključuju:

  • pretvaranje teksta u mala slova
  • uklanjanje interpunkcije
  • uklanjanje brojeva
  • uklanjanje stop riječi
  • uklanjanje URL-ova i oznaka

Cilj je ukloniti šum u podacima kako bi analiza bila fokusirana na informativne pojmove.

Primjer u R-u

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(stringr)

dat <- tibble(
  doc = c(
    "Data science is powerful.",
    "Text mining extracts insights from text!",
    "Netw*rk models are am@zing!",
    "Let's play with text mining and get powerful insights..."
  )
)

dat_clean <- dat |>
  mutate(
    doc = str_to_lower(doc),
    doc = str_remove_all(doc, "[[:punct:]]")
  )

dat_clean
## # A tibble: 4 × 1
##   doc                                                 
##   <chr>                                               
## 1 data science is powerful                            
## 2 text mining extracts insights from text             
## 3 netwrk models are amzing                            
## 4 lets play with text mining and get powerful insights

Tokenizacija

Nakon čišćenja tekst se dijeli na manje jedinice – tokene.

Token može biti:

  • riječ
  • bigram
  • znak
  • rečenica

Najčešći pristup u analizi teksta je tokenizacija na riječi.

U paketu tidytext koristi se funkcija:

unnest_tokens()

Primjer u R-u

library(tidytext)

tokens <- dat_clean |>
  unnest_tokens(word, doc)

tokens
## # A tibble: 23 × 1
##    word    
##    <chr>   
##  1 data    
##  2 science 
##  3 is      
##  4 powerful
##  5 text    
##  6 mining  
##  7 extracts
##  8 insights
##  9 from    
## 10 text    
## # ℹ 13 more rows

Rezultat je tablica u kojoj svaki red predstavlja jedan token.


Frekvencija riječi (TF)

Najjednostavnija mjera važnosti pojma je frekvencija pojavljivanja.

Matematički:

\[ TF_{t,d} = \frac{f_{t,d}}{N_d} \]

gdje je:

  • \(TF_{t,d}\) – term frequency za pojam \(t\) u dokumentu \(d\)
  • \(f_{t,d}\) – broj pojavljivanja pojma \(t\) u dokumentu
  • \(N_d\) – ukupan broj tokena u dokumentu

Interpretacija:

  • veći TF → riječ je važnija u dokumentu.

Primjer u R-u

tf <- tokens |>
  count(word, sort = TRUE)
tf
## # A tibble: 18 × 2
##    word         n
##    <chr>    <int>
##  1 text         3
##  2 insights     2
##  3 mining       2
##  4 powerful     2
##  5 amzing       1
##  6 and          1
##  7 are          1
##  8 data         1
##  9 extracts     1
## 10 from         1
## 11 get          1
## 12 is           1
## 13 lets         1
## 14 models       1
## 15 netwrk       1
## 16 play         1
## 17 science      1
## 18 with         1

TF–IDF

Term Frequency – Inverse Document Frequency

što se može prevesti kao:

  • Term Frequency (TF) – frekvencija pojma u dokumentu

  • Inverse Document Frequency (IDF) – inverzna frekvencija dokumenta.

Drugim riječima, TF-IDF je mjera koja procjenjuje koliko je neki pojam važan za određeni dokument u odnosu na cijeli korpus.

Ključna ideja je da riječ može biti česta u dokumentu, ali ne nužno informativna. TF-IDF daje veću težinu riječima koje su:

  • česte u jednom dokumentu
  • rijetke u ostalim dokumentima.

Zato se koristi TF-IDF, koji smanjuje težinu riječi koje su česte u cijelom korpusu.

Težina pojma računa se kao:

\[ TF\text{-}IDF_{t,d} = TF_{t,d} \times IDF_t \]

gdje je:

  • \(TF_{t,d}\) — frekvencija pojma \(t\) u dokumentu \(d\)
  • \(IDF_t\) — inverzna frekvencija dokumenta
  • \(t\) — pojam (riječ)
  • \(d\) — dokument.

IDF komponenta definira se kao:

\[ IDF_t = \log\left(\frac{N}{n_t}\right) \]

gdje je:

  • \(N\) — ukupan broj dokumenata u korpusu
  • \(n_t\) — broj dokumenata u kojima se pojam pojavljuje.

Riječi koje se pojavljuju u mnogim dokumentima imaju niži TF-IDF, dok riječi specifične za pojedini dokument dobivaju višu težinu.

Pritom se važnost pojma procjenjuje u odnosu na dokument u kojem se pojavljuje.

Primjer u R-u

#not run
# u ovom primjeru svaki red tretiramo kao dokument
tfidf <- tokens |>
  count(document = row_number(), word) |>
  bind_tf_idf(word, document, n)
tfidf

Lematizacija i stemming

Hrvatski je morfološki bogat jezik. Ako ne lematiziramo riječi (npr. “mreža”, “mreži”, “mrežom”), sustav će ih tretirati kao tri potpuno različita pojma, što razvodnjava rezultate. Za računalo su “mreža” i “mreži” različiti nizovi znakova.

Pogledajmo što se događa s frekvencijama ako analiziramo tekst bez prethodne obrade oblika riječi:

Bez lematizacije (sirovi podaci)

Pretpostavimo da u tekstu imamo sljedeću rečenicu:

“Analiziramo mrežu jer u toj mreži vidimo strukturu, a s mrežom lakše upravljamo.”

Računalo će generirati ovakvu tablicu:

Riječ Frekvencija
mrežu 1
mreži 1
mrežom 1

Glavni pojam (“mreža”) se ne ističe kao dominantan jer je njegova frekvencija podijeljena na tri različita oblika.

Uz lematizaciju (napredna obrada)

Lematizator prepoznaje da su svi ovi oblici zapravo ista riječ i svodi ih na osnovni oblik (kanonsku formu, nominativ jednine):

Riječ (Lema) Frekvencija
mreža 3

Lematizacija nam omogućuje da vidimo pravu “težinu” pojma u tekstu.

U R-u se za hrvatski jezik najčešće koristi lematizacija putem vanjskih alata (poput sustava Classla ili rječnika za lematizaciju).

library(dplyr)
library(tidytext)

# Simulirani tekst
tekst <- c("mreža", "mreži", "mrežom", "mreže", "mrežu")

# Prikaz bez obrade
tablica_bez <- tibble(word = tekst) %>% count(word)

# Simulirana lematizacija (ručno za potrebe demonstracije)
tablica_lema <- tibble(word = tekst) %>%
  mutate(lemma = "mreža") %>% # Zamislimo da je lematizator ovo odradio
  count(lemma)

# Rezultati
tablica_bez # Pokazuje 5 redova s frekvencijom 1
## # A tibble: 5 × 2
##   word       n
##   <chr>  <int>
## 1 mreža      1
## 2 mreže      1
## 3 mreži      1
## 4 mrežom     1
## 5 mrežu      1
tablica_lema # Pokazuje 1 red s frekvencijom 5
## # A tibble: 1 × 2
##   lemma     n
##   <chr> <int>
## 1 mreža     5

Alati za obradu hrvatskog jezika u R-u

Dok se za engleski jezik često koristi jednostavna funkcija stemDocument() iz paketa tm, ona za hrvatski jezik ne daje dobre rezultate. Umjesto toga, preporučuje se korištenje sljedećih pristupa:

  1. Paket udpipe (Svestrano rješenje)

Paket udpipe je trenutno najpopularnije rješenje za R jer omogućuje preuzimanje već treniranih modela za preko 60 jezika, uključujući i hrvatski.

  • Funkcija: udpipe_annotate() i udpipe_download_model()

  • Automatski prepoznaje padeže i vraća osnovni oblik riječi (lemu), ali i vrstu riječi (imenica, glagol, pridjev).

  1. Paket classla (Vrhunska preciznost)

Za najprecizniju obradu hrvatskog, srpskog i slovenskog jezika koristi se sustav CLASSLA (zasnovan na Stanza modelu). Iako je primarno razvijen za Python, u R-u mu se može pristupiti putem paketa reticulate ili korištenjem predtretiranih tablica.

  • Treniran je na golemim korpusima hrvatskog jezika i najbolje se snalazi s dijalektima i internetskim slengom.
  1. Rječnička lematizacija (brzo, ali ručno rješenje)

Ako ne želite koristiti složene lingvističke modele, možete koristiti rječnike za lematizaciju (npr. Hrvatski morfološki leksikon).

  • Koristi se left_join iz paketa dplyr kako bi se svaka tokenizirana riječ uparila s njenom lemom iz vanjske .csv tablice.
library(udpipe)

# 1. Preuzimanje i učitavanje modela za hrvatski
dl <- udpipe_download_model(language = "croatian")
## Downloading udpipe model from https://raw.githubusercontent.com/jwijffels/udpipe.models.ud.2.5/master/inst/udpipe-ud-2.5-191206/croatian-set-ud-2.5-191206.udpipe to C:/Users/Korisnik/Documents/SNA/croatian-set-ud-2.5-191206.udpipe
##  - This model has been trained on version 2.5 of data from https://universaldependencies.org
##  - The model is distributed under the CC-BY-SA-NC license: https://creativecommons.org/licenses/by-nc-sa/4.0
##  - Visit https://github.com/jwijffels/udpipe.models.ud.2.5 for model license details.
##  - For a list of all models and their licenses (most models you can download with this package have either a CC-BY-SA or a CC-BY-SA-NC license) read the documentation at ?udpipe_download_model. For building your own models: visit the documentation by typing vignette('udpipe-train', package = 'udpipe')
## Downloading finished, model stored at 'C:/Users/Korisnik/Documents/SNA/croatian-set-ud-2.5-191206.udpipe'
model <- udpipe_load_model(dl$file_model)

# 2. Lematizacija teksta
analiza <- udpipe_annotate(model, x = "mrežama upravljamo lakše")
rezultat <- as.data.frame(analiza)

rezultat$lemma
## [1] "mreža"      "upravljati" "lako"

Reprezentacije teksta

Tekst se može reprezentirati na različite načine.

Najčešće su:

Bag-of-Words

Tekst se promatra kao skup riječi bez reda.

Primjer:

text mining is useful

pretvara se u:

riječ frekvencija
text 1
mining 1
useful 1

Prednosti:

  • jednostavno
  • učinkovito
  • često dovoljno za analizu

Nedostatak:

  • gubi se kontekst i red riječi.

N-grami

N-grami zadržavaju lokalni kontekst riječi.

Primjeri:

bigram

text mining
mining methods

trigram

text mining methods

Primjer u R-u

bigrams <- dat_clean |>
  unnest_tokens(bigram, doc, token = "ngrams", n = 2)
bigrams
## # A tibble: 19 × 1
##    bigram           
##    <chr>            
##  1 data science     
##  2 science is       
##  3 is powerful      
##  4 text mining      
##  5 mining extracts  
##  6 extracts insights
##  7 insights from    
##  8 from text        
##  9 netwrk models    
## 10 models are       
## 11 are amzing       
## 12 lets play        
## 13 play with        
## 14 with text        
## 15 text mining      
## 16 mining and       
## 17 and get          
## 18 get powerful     
## 19 powerful insights

Analiza važnih pojmova

Jedan od ciljeva osnovne analize teksta je identificirati ključne pojmove.

To se može raditi na razini:

  • dokumenta
  • cijelog korpusa
  • usporedbe korpusa

Primjer:

top_terms <- tokens |>
  count(word, sort = TRUE) |>
  slice_head(n = 5)
top_terms
## # A tibble: 5 × 2
##   word         n
##   <chr>    <int>
## 1 text         3
## 2 insights     2
## 3 mining       2
## 4 powerful     2
## 5 amzing       1

Rezultati se često prikazuju pomoću:

  • stupčastih dijagrama
  • wordclouda
  • mreža pojmova

Reproducibilni pipeline analize

U analizi podataka važno je da postupak bude reproducibilan.

To znači da se cijela analiza može ponoviti istim kodom.

Tipičan pipeline u R-u:

#not run
library(tidyverse)
library(tidytext)

text_data |>
  mutate(text = str_to_lower(text)) |>
  unnest_tokens(word, text) |>
  anti_join(stop_words) |>
  count(word, sort = TRUE)

Prednosti pipeline pristupa:

  • transparentnost
  • ponovljivost analize
  • jednostavna modifikacija koraka

Ograničenja osnovne analize teksta

Važno je razumjeti ograničenja osnovnih metoda.

Bag-of-words pristup:

  • zanemaruje red riječi
  • ne razlikuje značenje u različitim kontekstima
  • ne prepoznaje ironiju ili sarkazam

Zbog toga se u naprednijim analizama koriste:

  • topic modeling
  • word embeddings
  • transformers modeli

No osnovne metode i dalje su vrlo korisne za eksploratornu analizu korpusa.


Paketi za analizu teksta u R-u

Analiza teksta u R-u temelji se na više specijaliziranih paketa koji omogućuju obradu prirodnog jezika, statističku analizu i primjenu modernih modela strojnog učenja. U ovoj lekciji koriste se tri paketa koji pokrivaju različite razine analize: string manipulaciju, klasični text mining i napredne NLP modele.

stringr

Paket stringr dio je tidyverse ekosustava i služi za rad s tekstualnim nizovima (strings). U analizi teksta koristi se prvenstveno u fazi pretprocesiranja, kada je potrebno očistiti i standardizirati tekst prije tokenizacije.

Tipične operacije uključuju:

  • pretvaranje teksta u mala slova
  • uklanjanje interpunkcije ili posebnih znakova
  • pretraživanje i zamjenu uzoraka
  • ekstrakciju dijelova teksta

Primjeri funkcija:

  • str_to_lower() – pretvara tekst u mala slova
  • str_remove_all() – uklanja uzorke iz teksta
  • str_detect() – provjerava pojavljuje li se uzorak
  • str_replace_all() – zamjenjuje uzorke

Primjer:

library(stringr)
text_clean <- dat$doc |>
  str_to_lower() |>
  str_remove_all("[[:punct:]]")
text_clean
## [1] "data science is powerful"                            
## [2] "text mining extracts insights from text"             
## [3] "netwrk models are amzing"                            
## [4] "lets play with text mining and get powerful insights"

Ovaj paket omogućuje jednostavne i konzistentne operacije nad tekstom, što je posebno važno u pretprocesiranju.


stopwords

Paket stopwords pruža standardizirane liste stop riječi za različite jezike. Stop riječi su česte riječi male informativnosti (npr. i, je, da, the, and) koje se često uklanjaju iz korpusa prije analize.

Primjer:

library(stopwords)

stopwords("en")
##   [1] "i"          "me"         "my"         "myself"     "we"        
##   [6] "our"        "ours"       "ourselves"  "you"        "your"      
##  [11] "yours"      "yourself"   "yourselves" "he"         "him"       
##  [16] "his"        "himself"    "she"        "her"        "hers"      
##  [21] "herself"    "it"         "its"        "itself"     "they"      
##  [26] "them"       "their"      "theirs"     "themselves" "what"      
##  [31] "which"      "who"        "whom"       "this"       "that"      
##  [36] "these"      "those"      "am"         "is"         "are"       
##  [41] "was"        "were"       "be"         "been"       "being"     
##  [46] "have"       "has"        "had"        "having"     "do"        
##  [51] "does"       "did"        "doing"      "would"      "should"    
##  [56] "could"      "ought"      "i'm"        "you're"     "he's"      
##  [61] "she's"      "it's"       "we're"      "they're"    "i've"      
##  [66] "you've"     "we've"      "they've"    "i'd"        "you'd"     
##  [71] "he'd"       "she'd"      "we'd"       "they'd"     "i'll"      
##  [76] "you'll"     "he'll"      "she'll"     "we'll"      "they'll"   
##  [81] "isn't"      "aren't"     "wasn't"     "weren't"    "hasn't"    
##  [86] "haven't"    "hadn't"     "doesn't"    "don't"      "didn't"    
##  [91] "won't"      "wouldn't"   "shan't"     "shouldn't"  "can't"     
##  [96] "cannot"     "couldn't"   "mustn't"    "let's"      "that's"    
## [101] "who's"      "what's"     "here's"     "there's"    "when's"    
## [106] "where's"    "why's"      "how's"      "a"          "an"        
## [111] "the"        "and"        "but"        "if"         "or"        
## [116] "because"    "as"         "until"      "while"      "of"        
## [121] "at"         "by"         "for"        "with"       "about"     
## [126] "against"    "between"    "into"       "through"    "during"    
## [131] "before"     "after"      "above"      "below"      "to"        
## [136] "from"       "up"         "down"       "in"         "out"       
## [141] "on"         "off"        "over"       "under"      "again"     
## [146] "further"    "then"       "once"       "here"       "there"     
## [151] "when"       "where"      "why"        "how"        "all"       
## [156] "any"        "both"       "each"       "few"        "more"      
## [161] "most"       "other"      "some"       "such"       "no"        
## [166] "nor"        "not"        "only"       "own"        "same"      
## [171] "so"         "than"       "too"        "very"       "will"

Stop riječi se obično uklanjaju spajanjem s tokeniziranim tekstom:

tokens |>
anti_join(stop_words)
## Joining with `by = join_by(word)`
## # A tibble: 16 × 1
##    word    
##    <chr>   
##  1 data    
##  2 science 
##  3 powerful
##  4 text    
##  5 mining  
##  6 extracts
##  7 insights
##  8 text    
##  9 netwrk  
## 10 models  
## 11 amzing  
## 12 play    
## 13 text    
## 14 mining  
## 15 powerful
## 16 insights

tm

Paket tm (text mining) jedan je od najstarijih i najpoznatijih paketa za analizu teksta u R-u. Razvijen je za klasični pristup text miningu koji se temelji na korpusu i dokument–termin matrici (Document-Term Matrix).

U ovom pristupu tekst se organizira u matricu:

\[ DTM = \begin{bmatrix} f_{1,1} & f_{1,2} & ... & f_{1,m} \\ f_{2,1} & f_{2,2} & ... & f_{2,m} \\ \vdots & \vdots & & \vdots \\ f_{n,1} & f_{n,2} & ... & f_{n,m} \end{bmatrix} \]

gdje je:

  • \(n\) broj dokumenata
  • \(m\) broj jedinstvenih pojmova
  • \(f_{i,j}\) frekvencija pojma \(j\) u dokumentu \(i\)

Ovaj paket omogućuje:

  • izgradnju korpusa
  • izradu dokument–termin matrica
  • osnovno čišćenje teksta
  • statističku analizu pojmova

Primjer:

#not run
library(tm)
texts <- c("Ovo je prvi dokument.", "Ovo je drugi dokument.")
corpus <- Corpus(VectorSource(texts))
dtm <- DocumentTermMatrix(corpus)

Iako se danas često koristi tidytext, paket tm i dalje je važan jer predstavlja klasični pristup analizi teksta.

Checklist za pretprocesiranje: Redoslijed je važan!

Prilikom čišćenja korpusa pomoću tm_map, važno je slijediti logičan redoslijed kako ne biste uništili strukturu informacija koju želite ukloniti. Preporučeni redoslijed:

  1. Uklanjanje URL-ova i HTML tagova (Mora biti prvo, jer se oslanjaju na interpunkciju poput http:// ili <br>).
  2. Pretvaranje u mala slova (tolower) (Osigurava da Stopwords i vlastita imena budu tretirani jednako).
  3. Uklanjanje stop riječi (stopwords) (Često je bolje ukloniti ih dok je interpunkcija još tu, ovisno o jeziku).
  4. Uklanjanje interpunkcije (removePunctuation) (Tek nakon što smo sigurni da nam simboli više ne trebaju za prepoznavanje uzoraka).
  5. Uklanjanje brojeva (removeNumbers) (Osim ako su brojevi ključni za analizu).
  6. Uklanjanje viška praznina (stripWhitespace) (Uvijek na kraju, kako bi se počistile praznine nastale prethodnim koracima).

U R-u, uz paket tm, to bi izgledalo ovako (pazite na redoslijed linija):

library(tm)
# Definiranje funkcije za URL-ove prije svega
removeURL <- function(x) gsub("http[^[:space:]]*", "", x)

# Redoslijed u tm_map
korpus <- tm_map(korpus, content_transformer(removeURL))      # 1. URL-ovi
korpus <- tm_map(korpus, content_transformer(tolower))        # 2. Mala slova
korpus <- tm_map(korpus, removeWords, stopwords("croatian"))  # 3. Stop-riječi
korpus <- tm_map(korpus, removePunctuation)                   # 4. Interpunkcija
korpus <- tm_map(korpus, removeNumbers)                       # 5. Brojevi
korpus <- tm_map(korpus, stripWhitespace)                     # 6. Praznine

tidytext

Paket tidytext omogućuje analizu teksta u okviru tidy data paradigme. Umjesto klasičnih dokument–termin matrica, tekst se transformira u tidy format, gdje svaki red predstavlja jedan token.

To omogućuje jednostavnu integraciju s paketima kao što su:

  • dplyr
  • ggplot2
  • tidyr

Najvažnije funkcije u ovom paketu su:

  • unnest_tokens() – tokenizacija teksta
  • bind_tf_idf() – izračun TF–IDF težina
  • reorder_within() – pomoć pri vizualizaciji pojmova
  • anti_join(stop_words) – uklanjanje stop riječi

Primjer tokenizacije:

#not run
library(tidytext)

tokens <- text_data |>
  unnest_tokens(word, text)
tokens

Prednost ovog pristupa je što omogućuje reproducibilan i transparentan pipeline analize teksta koji je kompatibilan s ostatkom tidyverse ekosustava.


quanteda

Paket quanteda jedan je od najmoćnijih paketa za obradu teksta u R-u i često se koristi u računalnoj lingvistici i društvenim znanostima.

Njegova glavna prednost je izuzetno brza obrada velikih korpusa.

Omogućuje:

  • tokenizaciju
  • izgradnju dokument–termin matrica
  • izračun frekvencija i statistika pojmova
  • analizu n-grama
  • izgradnju značajki (features)

Primjer:

#not run
library(quanteda)

tok <- tokens(texts)
dfm <- dfm(tok)

U praksi se quanteda često koristi kada je potrebno analizirati velike količine tekstualnih podataka, primjerice političke govore, parlamentarne rasprave ili objave na društvenim mrežama.


topicmodels

Paket topicmodels omogućuje modeliranje tema u tekstu pomoću metode Latent Dirichlet Allocation (LDA).

Topic modeling je metoda koja identificira skrivene tematske strukture u korpusu. Pretpostavlja da svaki dokument sadrži kombinaciju više tema, a svaka tema predstavlja distribuciju pojmova.

Formalno, LDA pretpostavlja:

  • dokument = distribucija tema
  • tema = distribucija riječi

Model procjenjuje:

  • vjerojatnost teme u dokumentu
  • vjerojatnost riječi unutar teme

Primjer:

#not run
library(topicmodels)

lda_model <- LDA(dtm, k = 5)

gdje je:

  • dtm dokument–termin matrica
  • k broj tema koje želimo identificirati.

Topic modeling često se koristi u analizi:

  • vijesti
  • znanstvenih članaka
  • društvenih mreža
  • korporativnih komunikacija.

textmineR

Paket textmineR razvijen je za napredniju statističku analizu teksta i često se koristi u istraživačkim projektima.

Podržava metode kao što su:

  • topic modeling (LDA)
  • izgradnja dokument–termin matrice
  • mjere sličnosti dokumenata
  • evaluacija topic modela

Primjer izrade dokument–termin matrice:

#not run
library(textmineR)

dtm <- CreateDtm(
doc_vec = documents,
ngram_window = c(1,2)
)

Ovaj paket posebno je koristan kada se prelazi s eksploratorne analize na modeliranje tema u tekstu.


text

Paket text predstavlja most između R-a i modernih metoda dubokog učenja za obradu prirodnog jezika (NLP). Ovaj paket omogućuje korištenje transformer modela razvijenih u okviru platforme HuggingFace.

Transformer modeli predstavljaju suvremeni pristup analizi teksta jer uvažavaju kontekst riječi unutar rečenice, za razliku od klasičnih metoda poput bag-of-words.

Primjene uključuju:

  • semantičku analizu teksta
  • analizu sentimenta
  • usporedbu semantičke sličnosti dokumenata
  • klasifikaciju tekstova

Jedna od ključnih funkcija je:

#not run
textEmbed()

koja generira semantičke reprezentacije teksta (embeddings) pomoću transformera.

Primjer:

#not run
library(text)

embeddings <- textEmbed(
  texts = c("Data science is useful", "Text mining is powerful"),
  model = "bert-base-uncased"
)

embeddings

Rezultat bi bili numerički vektori koji predstavljaju semantičko značenje teksta i mogu se koristiti u daljnjim analizama, primjerice u klasteriranju ili klasifikaciji.


Od klasičnog text mining-a do modernog NLP-a

Metode predstavljene u ovoj lekciji (tokenizacija, TF i TF-IDF) pripadaju klasičnom pristupu analizi teksta. One se često koriste u eksploratornoj analizi jer su jednostavne, interpretabilne i računalno učinkovite.

S druge strane, moderni pristupi temeljeni na transformer modelima omogućuju dublje razumijevanje značenja teksta jer uzimaju u obzir kontekst i semantičke odnose među riječima.

Zbog toga se u praksi često koristi kombinacija pristupa:

  • klasične metode za eksploratornu analizu korpusa
  • NLP modeli za naprednu semantičku analizu

Vizualizacija tekstualnih podataka

Vizualizacija je važan korak u eksploratornoj analizi korpusa jer omogućuje brzo uočavanje dominantnih pojmova ili tema.

wordcloud

Paket wordcloud omogućuje izradu klasičnih oblaka riječi u kojima veličina riječi odgovara njezinoj frekvenciji u korpusu.

Primjer:

#not run
library(wordcloud)

wordcloud(
words = terms$word,
freq = terms$n
)

Ova metoda daje intuitivan pregled najčešćih pojmova u tekstu.


ggwordcloud

Paket ggwordcloud predstavlja moderniju implementaciju wordcloud vizualizacije temeljenu na ggplot2. Prednost ovog pristupa je veća fleksibilnost u dizajnu i bolja integracija s tidyverse ekosustavom.

Primjer:

#not run
library(ggwordcloud)

ggwordcloud(terms, aes(label = word, size = n)) +
geom_text_wordcloud()

Pregled ekosustava paketa za analizu teksta

Ako se svi spomenuti paketi promatraju zajedno, dobiva se pregled tipičnog text mining ekosustava u R-u:

faza analize paketi
čišćenje teksta stringr, stopwords
tokenizacija tidytext, quanteda
frekvencijska analiza tidytext, tm
dokument–termin matrice tm, quanteda, textmineR
modeliranje tema topicmodels, textmineR
moderni NLP modeli text
vizualizacija wordcloud, ggwordcloud, ggplot2

Pregled tipičnog workflowa

U praksi se paketi koriste u kombinaciji kroz sljedeće faze analize teksta:

  1. Pretprocesiranje teksta
  • stringr, stopwords
  1. Tokenizacija i frekvencijska analiza
  • tidytext
  1. Naprednija analiza i modeliranje
  • textmineR, text
  1. Vizualizacija rezultata
  • wordcloud, ggwordcloud

Takva kombinacija omogućuje izgradnju reproducibilnog i transparentnog pipeline-a analize teksta u R-u, od čišćenja podataka do interpretacije rezultata.

Primjer

Paket gutenbergr omogućuje preuzimanje tekstova iz Project Gutenberga, koja sadrži velik broj književnih djela u javnoj domeni (djela autora kojima su istekla autorska prava), npr.:

Autor Djelo gutenberg_id
Lewis Carroll Alice’s Adventures in Wonderland 11
Mary Shelley Frankenstein; or, The Modern Prometheus 84
Jane Austen Pride and Prejudice 1342
Bram Stoker Dracula 345
Arthur Conan Doyle The Adventures of Sherlock Holmes 1661
Lewis Carroll Through the Looking-Glass 12
Jane Austen Sense and Sensibility 161
H. G. Wells The War of the Worlds 36
H. G. Wells The Time Machine 35
Oscar Wilde The Picture of Dorian Gray 174
Herman Melville Moby Dick 2701
J. M. Barrie Peter Pan 16

U Project Gutenbergu svaka knjiga ima stabilni numerički identifikator (gutenberg_id) putem kojeg možemo preuzeti djelo.


U nastavku ćemo koristiti Alice’s Adventures in Wonderland, ali možete isprobati postupak samostalno na ostalim djelima.

library(gutenbergr)
alice <- gutenberg_download(11) # Alice's Adventures in Wonderland, Lewis Carroll (1865)
## Mirror list unavailable. Falling back to <https://aleph.pglaf.org>.
str(alice)
## tibble [6,760 × 2] (S3: tbl_df/tbl/data.frame)
##  $ gutenberg_id: int [1:6760] 11 11 11 11 11 11 11 11 11 11 ...
##  $ text        : chr [1:6760] "[Illustration]" "[Illustration]" "" "" ...

Rezultat je tibble u kojem svaki redak predstavlja jedan redak teksta knjige, a stupac gutenberg_id označava identifikator izvornog djela.


Nakon što je tekst preuzet iz Project Gutenberga, prvi korak je osnovni uvid u podatke. U ovoj fazi cilj je razumjeti strukturu korpusa i način na koji je tekst organiziran prije početka čišćenja i transformacije.

head(alice, 20)
## # A tibble: 20 × 2
##    gutenberg_id text                                
##           <int> <chr>                               
##  1           11 "[Illustration]"                    
##  2           11 "[Illustration]"                    
##  3           11 ""                                  
##  4           11 ""                                  
##  5           11 ""                                  
##  6           11 ""                                  
##  7           11 ""                                  
##  8           11 ""                                  
##  9           11 ""                                  
## 10           11 ""                                  
## 11           11 "Alice’s Adventures in Wonderland"  
## 12           11 "Alice’s Adventures in Wonderland"  
## 13           11 ""                                  
## 14           11 ""                                  
## 15           11 "by Lewis Carroll"                  
## 16           11 "by Lewis Carroll"                  
## 17           11 ""                                  
## 18           11 ""                                  
## 19           11 "THE MILLENNIUM FULCRUM EDITION 3.0"
## 20           11 "THE MILLENNIUM FULCRUM EDITION 3.0"

Primijetit ćemo da svaki redak predstavlja jedan redak izvornog teksta, a ne cijelu rečenicu ili poglavlje. Osim stvarnog sadržaja knjige, dataset može sadržavati i dodatne elemente poput:

  • oznaka poglavlja

  • praznih redaka

  • uredničkih napomena ili metapodataka.

Zato je prije analize potrebno provesti pretprocesiranje teksta.


Najprije možemo provjeriti osnovne dimenzije skupa podataka.

nrow(alice)
## [1] 6760

Koliko je praznih redaka?

sum(alice$text == "")
## [1] 1772

Prazni retci često se pojavljuju u digitaliziranim knjigama i obično se uklanjaju u fazi čišćenja.

Korisno je pogledati i nasumični uzorak redaka kako bismo dobili bolji dojam o tekstu.

alice |>
  slice_sample(n = 10)
## # A tibble: 10 × 2
##    gutenberg_id text                                                            
##           <int> <chr>                                                           
##  1           11 "“You mean you can’t take _less_,” said the Hatter: “it’s very …
##  2           11 "It was all very well to say “Drink me,” but the wise little Al…
##  3           11 "“It was the _best_ butter,” the March Hare meekly replied."    
##  4           11 ""                                                              
##  5           11 "“But I don’t want to go among mad people,” Alice remarked."    
##  6           11 "living would be like, but it puzzled her too much, so she went…
##  7           11 ""                                                              
##  8           11 "unrolled the parchment scroll, and read as follows:—"          
##  9           11 "March Hare. Visit either you like: they’re both mad.”"         
## 10           11 "Alice was more and more puzzled, but she thought there was no …

Ovakav pregled pomaže uočiti potencijalne probleme poput:

  • interpunkcije

  • velikih i malih slova

  • posebnih znakova

  • oznaka poglavlja

  • dupliciranih redaka.


Prvi korak u čišćenju teksta obično je uklanjanje praznih redaka.

library(dplyr)

alice_clean <- alice |>
  filter(text != "")

head(alice_clean)
## # A tibble: 6 × 2
##   gutenberg_id text                            
##          <int> <chr>                           
## 1           11 [Illustration]                  
## 2           11 [Illustration]                  
## 3           11 Alice’s Adventures in Wonderland
## 4           11 Alice’s Adventures in Wonderland
## 5           11 by Lewis Carroll                
## 6           11 by Lewis Carroll

Provjerimo novu veličinu skupa podataka:

nrow(alice_clean)
## [1] 4988

Nakon osnovnog uvida u podatke sljedeći korak je pretprocesiranje teksta, odnosno uklanjanje elemenata koji ne nose semantičku informaciju i mogu otežati analizu. Tipični koraci uključuju normalizaciju teksta, uklanjanje uredničkih oznaka te filtriranje vrlo čestih riječi male informativnosti.

U ovom primjeru provest ćemo:

  • uklanjanje duplikata

  • normalizaciju na mala slova

  • uklanjanje interpunkcije

  • uklanjanje uredničkih oznaka (npr. [Illustration])

  • uklanjanje stop riječi.


Provjera dupliciranja

alice_clean |>
  slice(1:20)
## # A tibble: 20 × 2
##    gutenberg_id text                                               
##           <int> <chr>                                              
##  1           11 "[Illustration]"                                   
##  2           11 "[Illustration]"                                   
##  3           11 "Alice’s Adventures in Wonderland"                 
##  4           11 "Alice’s Adventures in Wonderland"                 
##  5           11 "by Lewis Carroll"                                 
##  6           11 "by Lewis Carroll"                                 
##  7           11 "THE MILLENNIUM FULCRUM EDITION 3.0"               
##  8           11 "THE MILLENNIUM FULCRUM EDITION 3.0"               
##  9           11 "Contents"                                         
## 10           11 "Contents"                                         
## 11           11 " CHAPTER I.     Down the Rabbit-Hole"             
## 12           11 " CHAPTER I.     Down the Rabbit-Hole"             
## 13           11 " CHAPTER II.    The Pool of Tears"                
## 14           11 " CHAPTER II.    The Pool of Tears"                
## 15           11 " CHAPTER III.   A Caucus-Race and a Long Tale"    
## 16           11 " CHAPTER III.   A Caucus-Race and a Long Tale"    
## 17           11 " CHAPTER IV.    The Rabbit Sends in a Little Bill"
## 18           11 " CHAPTER IV.    The Rabbit Sends in a Little Bill"
## 19           11 " CHAPTER V.     Advice from a Caterpillar"        
## 20           11 " CHAPTER V.     Advice from a Caterpillar"

Uklanjanje uzastopnih duplikata

library(dplyr)

alice_nodup <- alice_clean |>
  filter(text != lag(text, default = ""))

nrow(alice_clean)
## [1] 4988
nrow(alice_nodup)
## [1] 2491
head(alice_nodup, 30)
## # A tibble: 30 × 2
##    gutenberg_id text                                               
##           <int> <chr>                                              
##  1           11 "[Illustration]"                                   
##  2           11 "Alice’s Adventures in Wonderland"                 
##  3           11 "by Lewis Carroll"                                 
##  4           11 "THE MILLENNIUM FULCRUM EDITION 3.0"               
##  5           11 "Contents"                                         
##  6           11 " CHAPTER I.     Down the Rabbit-Hole"             
##  7           11 " CHAPTER II.    The Pool of Tears"                
##  8           11 " CHAPTER III.   A Caucus-Race and a Long Tale"    
##  9           11 " CHAPTER IV.    The Rabbit Sends in a Little Bill"
## 10           11 " CHAPTER V.     Advice from a Caterpillar"        
## # ℹ 20 more rows

Pretvaranje teksta u mala slova

Radi standardizacije sve riječi prevodimo u mala slova.

library(stringr)

alice_nodup <- alice_nodup |>
  mutate(text = str_to_lower(text))

head(alice_nodup, 20)
## # A tibble: 20 × 2
##    gutenberg_id text                                                            
##           <int> <chr>                                                           
##  1           11 "[illustration]"                                                
##  2           11 "alice’s adventures in wonderland"                              
##  3           11 "by lewis carroll"                                              
##  4           11 "the millennium fulcrum edition 3.0"                            
##  5           11 "contents"                                                      
##  6           11 " chapter i.     down the rabbit-hole"                          
##  7           11 " chapter ii.    the pool of tears"                             
##  8           11 " chapter iii.   a caucus-race and a long tale"                 
##  9           11 " chapter iv.    the rabbit sends in a little bill"             
## 10           11 " chapter v.     advice from a caterpillar"                     
## 11           11 " chapter vi.    pig and pepper"                                
## 12           11 " chapter vii.   a mad tea-party"                               
## 13           11 " chapter viii.  the queen’s croquet-ground"                    
## 14           11 " chapter ix.    the mock turtle’s story"                       
## 15           11 " chapter x.     the lobster quadrille"                         
## 16           11 " chapter xi.    who stole the tarts?"                          
## 17           11 " chapter xii.   alice’s evidence"                              
## 18           11 "chapter i."                                                    
## 19           11 "down the rabbit-hole"                                          
## 20           11 "alice was beginning to get very tired of sitting by her sister…

Uklanjanje brojeva

Brojevi se u književnom tekstu rijetko koriste kao nositelji semantičke informacije, pa ih možemo ukloniti.

alice_nodup <- alice_nodup |>
  mutate(text = str_remove_all(text, "[0-9]+"))

head(alice_nodup, 20)
## # A tibble: 20 × 2
##    gutenberg_id text                                                            
##           <int> <chr>                                                           
##  1           11 "[illustration]"                                                
##  2           11 "alice’s adventures in wonderland"                              
##  3           11 "by lewis carroll"                                              
##  4           11 "the millennium fulcrum edition ."                              
##  5           11 "contents"                                                      
##  6           11 " chapter i.     down the rabbit-hole"                          
##  7           11 " chapter ii.    the pool of tears"                             
##  8           11 " chapter iii.   a caucus-race and a long tale"                 
##  9           11 " chapter iv.    the rabbit sends in a little bill"             
## 10           11 " chapter v.     advice from a caterpillar"                     
## 11           11 " chapter vi.    pig and pepper"                                
## 12           11 " chapter vii.   a mad tea-party"                               
## 13           11 " chapter viii.  the queen’s croquet-ground"                    
## 14           11 " chapter ix.    the mock turtle’s story"                       
## 15           11 " chapter x.     the lobster quadrille"                         
## 16           11 " chapter xi.    who stole the tarts?"                          
## 17           11 " chapter xii.   alice’s evidence"                              
## 18           11 "chapter i."                                                    
## 19           11 "down the rabbit-hole"                                          
## 20           11 "alice was beginning to get very tired of sitting by her sister…

Uklanjanje interpunkcije

Interpunkcija se također uklanja prije tokenizacije.

alice_nodup <- alice_nodup |>
  mutate(text = str_remove_all(text, "[[:punct:]]"))

head(alice_nodup, 20)
## # A tibble: 20 × 2
##    gutenberg_id text                                                            
##           <int> <chr>                                                           
##  1           11 "illustration"                                                  
##  2           11 "alices adventures in wonderland"                               
##  3           11 "by lewis carroll"                                              
##  4           11 "the millennium fulcrum edition "                               
##  5           11 "contents"                                                      
##  6           11 " chapter i     down the rabbithole"                            
##  7           11 " chapter ii    the pool of tears"                              
##  8           11 " chapter iii   a caucusrace and a long tale"                   
##  9           11 " chapter iv    the rabbit sends in a little bill"              
## 10           11 " chapter v     advice from a caterpillar"                      
## 11           11 " chapter vi    pig and pepper"                                 
## 12           11 " chapter vii   a mad teaparty"                                 
## 13           11 " chapter viii  the queens croquetground"                       
## 14           11 " chapter ix    the mock turtles story"                         
## 15           11 " chapter x     the lobster quadrille"                          
## 16           11 " chapter xi    who stole the tarts"                            
## 17           11 " chapter xii   alices evidence"                                
## 18           11 "chapter i"                                                     
## 19           11 "down the rabbithole"                                           
## 20           11 "alice was beginning to get very tired of sitting by her sister…

U tidytext pristupu tekst se najčešće najprije tokenizira, pri čemu se istodobno provodi većina ovih koraka.

Funkcija unnest_tokens() iz paketa tidytext razbija tekst na tokene (u ovom slučaju riječi) te automatski (po zadanim postavkama):

  • pretvara tekst u mala slova
  • uklanja interpunkciju.
library(tidytext)
library(dplyr)

alice_tokens <- alice_nodup |>
  unnest_tokens(word, text)

Rezultat je dataset u kojem vrijedi načelo jedan redak = jedna riječ.

Provjerimo prvih nekoliko redaka:

head(alice_tokens, 10)
## # A tibble: 10 × 2
##    gutenberg_id word        
##           <int> <chr>       
##  1           11 illustration
##  2           11 alices      
##  3           11 adventures  
##  4           11 in          
##  5           11 wonderland  
##  6           11 by          
##  7           11 lewis       
##  8           11 carroll     
##  9           11 the         
## 10           11 millennium

Uklanjanje stop riječi

Stop riječi su vrlo česte riječi (npr. the, and, to, of) koje obično nose malo semantičke informacije.

U paketu tidytext dostupna je lista stop riječi stop_words.

data(stop_words)

alice_tokens <- alice_tokens |>
  anti_join(stop_words, by = "word")

head(alice_tokens, 10)
## # A tibble: 10 × 2
##    gutenberg_id word        
##           <int> <chr>       
##  1           11 illustration
##  2           11 alices      
##  3           11 adventures  
##  4           11 wonderland  
##  5           11 lewis       
##  6           11 carroll     
##  7           11 millennium  
##  8           11 fulcrum     
##  9           11 edition     
## 10           11 contents

Funkcija anti_join() uklanja sve riječi koje se nalaze u listi stop riječi.

Rezultat je tokenizirani i očišćeni korpus u kojem su:

  • uklonjene stop riječi
  • uklonjena interpunkcija
  • tekst normaliziran na mala slova.

Takva struktura predstavlja standardni oblik podataka za većinu metoda analize teksta.


Najprije možemo izračunati koliko se puta svaka riječ pojavljuje u korpusu.

Ako je \(f_i\) frekvencija riječi \(i\), tada vrijedi

\[ TF_i = f_i \]

gdje je:

  • \(TF_i\) — frekvencija pojma (term frequency)
  • \(f_i\) — broj pojavljivanja riječi \(i\) u korpusu.

U ovom slučaju računamo frekvenciju u cijelom tekstu.

library(dplyr)

word_freq <- alice_tokens |>
count(word, sort = TRUE)

head(word_freq, 20)
## # A tibble: 20 × 2
##    word         n
##    <chr>    <int>
##  1 alice      385
##  2 queen       68
##  3 time        68
##  4 king        61
##  5 dont        60
##  6 im          57
##  7 mock        57
##  8 turtle      56
##  9 gryphon     55
## 10 hatter      55
## 11 head        48
## 12 voice       47
## 13 looked      45
## 14 rabbit      44
## 15 round       41
## 16 tone        40
## 17 dormouse    39
## 18 duchess     39
## 19 mouse       38
## 20 cat         35

Takva lista često otkriva:

  • glavne likove
  • tematske pojmove
  • ključne motive.

Grafički prikaz najčešćih riječi često je pregledniji od tablice.

top_words <- word_freq |>
  slice_max(n, n = 20)

library(ggplot2)

top_words |>
ggplot(aes(x = reorder(word, n), y = n)) +
geom_col() +
coord_flip() +
labs(
x = "Riječ",
y = "Frekvencija",
title = "Najčešće riječi u romanu Alice in Wonderland"
)

Ovakav graf omogućuje brzi uvid u dominantne pojmove u tekstu.


Wordcloud je vizualizacija u kojoj je veličina riječi proporcionalna frekvenciji pojavljivanja.

library(wordcloud)
## Loading required package: RColorBrewer
wordcloud(
words = word_freq$word,
freq = word_freq$n,
max.words = 100
)

Iako wordcloud nije analitički vrlo precizan, često je atraktivan i daje intuitivan pregled vokabulara.


Nakon tokenizacije tekst možemo promatrati kroz bag-of-words reprezentaciju. U tom pristupu dokument se opisuje samo frekvencijama riječi, bez uzimanja u obzir redoslijeda u kojem se riječi pojavljuju.

U našem primjeru bag-of-words reprezentacija izgleda ovako:

word_freq |>
slice_head(n = 10)
## # A tibble: 10 × 2
##    word        n
##    <chr>   <int>
##  1 alice     385
##  2 queen      68
##  3 time       68
##  4 king       61
##  5 dont       60
##  6 im         57
##  7 mock       57
##  8 turtle     56
##  9 gryphon    55
## 10 hatter     55

Svaki red predstavlja:

riječ | frekvencija

Ovakva reprezentacija vrlo je jednostavna, ali ima jedno važno ograničenje: gubi se red riječi i lokalni kontekst.

Primjerice, rečenice

alice likes the rabbit
the rabbit likes alice

u bag-of-words pristupu izgledaju identično.


Jedan od načina da se djelomično očuva red riječi je korištenje n-grama.

N-gram je sekvenca od \(n\) uzastopnih tokena.

Najčešći su:

  • bigram (\(n=2\))
  • trigram (\(n=3\))

Bigrami u tekstu mogu izgledati ovako:

white rabbit
mad hatter
march hare

U paketu tidytext n-grami se mogu dobiti funkcijom unnest_tokens().

alice_bigrams <- alice_nodup |>
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

head(alice_bigrams, 10)
## # A tibble: 10 × 2
##    gutenberg_id bigram            
##           <int> <chr>             
##  1           11 <NA>              
##  2           11 alices adventures 
##  3           11 adventures in     
##  4           11 in wonderland     
##  5           11 by lewis          
##  6           11 lewis carroll     
##  7           11 the millennium    
##  8           11 millennium fulcrum
##  9           11 fulcrum edition   
## 10           11 <NA>

Sada svaki red predstavlja par uzastopnih riječi. Isprobajmo trigrame

alice_trigrams <- alice_nodup |>
  unnest_tokens(trigram, text, token = "ngrams", n = 3)

head(alice_trigrams, 10)
## # A tibble: 10 × 2
##    gutenberg_id trigram                   
##           <int> <chr>                     
##  1           11 <NA>                      
##  2           11 alices adventures in      
##  3           11 adventures in wonderland  
##  4           11 by lewis carroll          
##  5           11 the millennium fulcrum    
##  6           11 millennium fulcrum edition
##  7           11 <NA>                      
##  8           11 chapter i down            
##  9           11 i down the                
## 10           11 down the rabbithole

Možemo izračunati i najčešće bi/trigrame:

trigram_freq <- alice_trigrams |>
  count(trigram, sort = TRUE)

trigram_freq |>
slice_head(n = 20)
## # A tibble: 20 × 2
##    trigram                  n
##    <chr>                <int>
##  1 <NA>                   143
##  2 the mock turtle         48
##  3 the march hare          29
##  4 said the king           28
##  5 said the hatter         21
##  6 said the mock           19
##  7 said the caterpillar    18
##  8 the white rabbit        18
##  9 she went on             16
## 10 one of the              15
## 11 said the duchess        15
## 12 said the gryphon        15
## 13 said to herself         15
## 14 as she could            14
## 15 said the cat            14
## 16 she said to             14
## 17 said the queen          12
## 18 it said the             11
## 19 said to the             11
## 20 there was a             11

Rezultat često otkriva:

  • imena likova
  • ustaljene izraze
  • česte kombinacije riječi.

Ako se pri izradi trigrama pojave NA vrijednosti, one najčešće upućuju na to da neki retci nakon čišćenja ne sadrže dovoljno riječi za formiranje trigrama ili sadrže prazne ili nespecifične zapise.

Za razliku od bag-of-words pristupa, n-grami djelomično čuvaju kontekst, ali imaju i nedostatke:

  • broj mogućih značajki brzo raste
  • dokument–termin matrica postaje još rjeđa (sparsity).

Zbog toga se u praksi često kombiniraju:

  • bag-of-words za osnovnu analizu
  • n-grami za hvatanje lokalnih obrazaca u tekstu.

Još jedan jednostavan, ali zanimljiv pokazatelj je veličina vokabulara, odnosno broj jedinstvenih riječi.

Ako je

\[ V = |{w_1, w_2, \ldots, w_k}| \]

gdje je:

  • \(V\) — veličina vokabulara
  • \(w_i\) — jedinstveni token u korpusu.

Možemo ga izračunati ovako:

vocab_size <- n_distinct(alice_tokens$word)

vocab_size
## [1] 2332

U većini tekstova vrijedi Zipfov zakon, prema kojem mali broj riječi ima vrlo visoku frekvenciju, dok se većina riječi pojavljuje rijetko. To je jedna od temeljnih empirijskih zakonitosti u analizi teksta, koji opisuje raspodjelu frekvencija riječi u prirodnom jeziku.

Zipfov zakon kaže da je frekvencija riječi obrnuto proporcionalna njezinu rangu u listi frekvencija. Drugim riječima, ako riječi sortiramo prema učestalosti pojavljivanja, tada približno vrijedi:

\[f(r) \propto \frac{1}{r}\]

gdje je:

\(f(r)\) — frekvencija riječi ranga \(r\)

\(r\) — rang riječi u listi sortiranoj prema frekvenciji

\(\propto\) — proporcionalnost.

To znači da:

  • najčešća riječ pojavljuje se vrlo često

  • druga riječ pojavljuje se otprilike dvostruko rjeđe

  • treća riječ približno tri puta rjeđe, itd.

Drugim riječima, mali broj riječi pojavljuje se vrlo često, dok se velika većina riječi pojavljuje rijetko.

U prirodnim jezicima to dovodi do raspodjele u kojoj vrijedi:

  • nekoliko riječi (the, and, to) čini velik dio teksta

  • velik broj riječi pojavljuje se samo jednom ili nekoliko puta.

To možemo vizualizirati:

word_freq |>
ggplot(aes(x = n)) +
geom_histogram(bins = 50) +
labs(
x = "Frekvencija riječi",
y = "Broj riječi",
title = "Distribucija frekvencija riječi"
)

No, ovdje smo samo vidjeli frekvencije riječi, a ne Zipfov zakon. Za to imamo još par koraka.

zipf_data <- word_freq |>
  arrange(desc(n)) |>
  mutate(rank = row_number())

head(zipf_data)
## # A tibble: 6 × 3
##   word      n  rank
##   <chr> <int> <int>
## 1 alice   385     1
## 2 queen    68     2
## 3 time     68     3
## 4 king     61     4
## 5 dont     60     5
## 6 im       57     6

Sad možemo prikazati odnos između ranga riječi i njezine frekvencije. Zipfov zakon najjasnije se uočava na grafu s logaritamskim skalama.

library(ggplot2)

zipf_data |>
  ggplot(aes(x = rank, y = n)) +
  geom_point(alpha = 0.6) +
  scale_x_log10() +
  scale_y_log10() +
  labs(
    x = "Rang riječi",
    y = "Frekvencija riječi",
    title = "Zipfov zakon u tekstu Alice in Wonderland"
  )

Ako riječi u tekstu približno slijede Zipfov zakon, odnos između ranga i frekvencije na log-log grafu bit će približno linearan. To znači da frekvencija riječi opada vrlo brzo: nekoliko riječi dominira tekstom, dok se većina riječi pojavljuje rijetko. Na grafu se vidi vrlo velik pad nakon prve najčešće riječi, zatim blaže opadanje među riječima najvišeg ranga, a potom postupno i približno pravilno smanjivanje frekvencije. Od približno 10. ranga nadalje odnos između ranga i frekvencije može se aproksimirati pravcem na log-log skali, što je u skladu s očekivanjem prema Zipfovu zakonu. To znači da mali broj riječi dominira tekstom, dok se velika većina riječi pojavljuje rijetko.

Zipfov zakon važan je za analizu teksta jer objašnjava:

  • zašto je potrebno uklanjati stop riječi

  • zašto su dokument–termin matrice često rijetke (sparse)

  • zašto metode poput TF-IDF naglašavaju riječi koje nisu česte u cijelom korpusu.


Nastavljamo s TF-IDF analizom. Za ovu analizu dokument ćemo definirati kao poglavlje.

Najprije iz teksta izdvajamo poglavlja. Ovdje su poglavlja pisana velikim štampanim slovima, a numerirana su rimskim brojevima.

Prvo ćemo izdvojiti sve izraze koji sadrže chapter kako bismo dobili uvid.

library(dplyr)
library(stringr)

alice_nodup |>
  mutate(row_id = row_number()) |>
  filter(str_detect(text, "chapter")) |>
  select(row_id, text)
## # A tibble: 24 × 2
##    row_id text                                              
##     <int> <chr>                                             
##  1      6 " chapter i     down the rabbithole"              
##  2      7 " chapter ii    the pool of tears"                
##  3      8 " chapter iii   a caucusrace and a long tale"     
##  4      9 " chapter iv    the rabbit sends in a little bill"
##  5     10 " chapter v     advice from a caterpillar"        
##  6     11 " chapter vi    pig and pepper"                   
##  7     12 " chapter vii   a mad teaparty"                   
##  8     13 " chapter viii  the queens croquetground"         
##  9     14 " chapter ix    the mock turtles story"           
## 10     15 " chapter x     the lobster quadrille"            
## # ℹ 14 more rows

Iz ovog ispisa sada je jasno što se događa: svako poglavlje pojavljuje se dvaput, ali to nije zato što je svaki redak dupliciran, nego zato što se u tekstu nalaze dva bloka poglavlja:

  • prvi blok je sadržaj (table of contents)

  • drugi blok je stvarni tekst romana.

Prvo ćemo izvući sve pojave poglavlja.

chapter_rows <- alice_nodup |>
  mutate(row_id = row_number()) |>
  filter(str_detect(text, "^\\s*chapter"))

chapter_rows$row_id
##  [1]    6    7    8    9   10   11   12   13   14   15   16   17   18  201  374
## [16]  531  748  962 1198 1431 1663 1888 2096 2278
chapter_rows[1:24,]
## # A tibble: 24 × 3
##    gutenberg_id text                                               row_id
##           <int> <chr>                                               <int>
##  1           11 " chapter i     down the rabbithole"                    6
##  2           11 " chapter ii    the pool of tears"                      7
##  3           11 " chapter iii   a caucusrace and a long tale"           8
##  4           11 " chapter iv    the rabbit sends in a little bill"      9
##  5           11 " chapter v     advice from a caterpillar"             10
##  6           11 " chapter vi    pig and pepper"                        11
##  7           11 " chapter vii   a mad teaparty"                        12
##  8           11 " chapter viii  the queens croquetground"              13
##  9           11 " chapter ix    the mock turtles story"                14
## 10           11 " chapter x     the lobster quadrille"                 15
## # ℹ 14 more rows

Najjednostavnije rješenje je uzeti drugu polovicu, nakon sadržaja u kojem se pojavljuje popis svih poglavlja.

chapter_rows <- alice_nodup |>
  mutate(row_id = row_number()) |>
  filter(str_detect(text, "^\\s*chapter"))

start_text <- chapter_rows$row_id[13]

start_text 
## [1] 18
library(dplyr)
library(stringr)

alice_main <- alice_nodup |>
  slice(start_text:n())

alice_chapters <- alice_main |>
  mutate(chapter = cumsum(str_detect(text, "^chapter [ivxlcdm]+")))

head(alice_chapters,10)
## # A tibble: 10 × 3
##    gutenberg_id text                                                     chapter
##           <int> <chr>                                                      <int>
##  1           11 chapter i                                                      1
##  2           11 down the rabbithole                                            1
##  3           11 alice was beginning to get very tired of sitting by her…       1
##  4           11 bank and of having nothing to do once or twice she had …       1
##  5           11 the book her sister was reading but it had no pictures …       1
##  6           11 conversations in it and what is the use of a book thoug…       1
##  7           11 without pictures or conversations                              1
##  8           11 so she was considering in her own mind as well as she c…       1
##  9           11 hot day made her feel very sleepy and stupid whether th…       1
## 10           11 making a daisychain would be worth the trouble of getti…       1
tail(alice_chapters, 10)
## # A tibble: 10 × 3
##    gutenberg_id text                                                     chapter
##           <int> <chr>                                                      <int>
##  1           11 lastly she pictured to herself how this same little sis…      12
##  2           11 would in the aftertime be herself a grown woman and how…      12
##  3           11 keep through all her riper years the simple and loving …      12
##  4           11 childhood and how she would gather about her other litt…      12
##  5           11 and make their eyes bright and eager with many a strang…      12
##  6           11 perhaps even with the dream of wonderland of long ago a…      12
##  7           11 would feel with all their simple sorrows and find a ple…      12
##  8           11 their simple joys remembering her own childlife and the…      12
##  9           11 days                                                          12
## 10           11 the end                                                       12

Ovdje:

  • str_detect() prepoznaje početak poglavlja
  • cumsum() numerira poglavlja.

Tokenizacija

library(tidytext)

alice_words <- alice_chapters |>
unnest_tokens(word, text) |>
anti_join(stop_words, by = "word")

head(alice_words)
## # A tibble: 6 × 3
##   gutenberg_id chapter word      
##          <int>   <int> <chr>     
## 1           11       1 chapter   
## 2           11       1 rabbithole
## 3           11       1 alice     
## 4           11       1 beginning 
## 5           11       1 tired     
## 6           11       1 sitting

Rezultat je dataset:

chapter | word

što znači:

\[ \text{jedan redak} = \text{jedna riječ u jednom poglavlju} \]

Frekvencije riječi po poglavlju

word_counts <- alice_words |>
count(chapter, word, sort = TRUE)

head(word_counts)
## # A tibble: 6 × 3
##   chapter word       n
##     <int> <chr>  <int>
## 1       7 alice     50
## 2       9 alice     47
## 3       6 alice     43
## 4       8 alice     39
## 5       5 alice     35
## 6       7 hatter    32

Ovdje dobivamo:

chapter | word | n

gdje je \(n\) frekvencija riječi u poglavlju.

Izračun TF-IDF

Paket tidytext ima funkciju bind_tf_idf() koja automatski računa TF, IDF i TF-IDF.

tfidf_words <- word_counts |>
  bind_tf_idf(word, chapter, n)

head(tfidf_words)
## # A tibble: 6 × 6
##   chapter word       n     tf   idf tf_idf
##     <int> <chr>  <int>  <dbl> <dbl>  <dbl>
## 1       7 alice     50 0.0714  0    0     
## 2       9 alice     47 0.0659  0    0     
## 3       6 alice     43 0.0575  0    0     
## 4       8 alice     39 0.0524  0    0     
## 5       5 alice     35 0.0567  0    0     
## 6       7 hatter    32 0.0457  1.39 0.0634

Rezultat sadrži tri nove varijable:

  • tf
  • idf
  • tf_idf.

Najvažnije riječi po poglavlju

tfidf_top <- tfidf_words |>
group_by(chapter) |>
slice_max(tf_idf, n = 5) |>
ungroup()

tfidf_top
## # A tibble: 64 × 6
##    chapter word           n      tf   idf tf_idf
##      <int> <chr>      <int>   <dbl> <dbl>  <dbl>
##  1       1 bats           4 0.00683 2.48  0.0170
##  2       1 key            6 0.0102  1.39  0.0142
##  3       1 cake           3 0.00512 2.48  0.0127
##  4       1 candle         3 0.00512 2.48  0.0127
##  5       1 dark           3 0.00512 2.48  0.0127
##  6       1 poison         3 0.00512 2.48  0.0127
##  7       1 rabbithole     3 0.00512 2.48  0.0127
##  8       2 mouse         16 0.0257  0.875 0.0225
##  9       2 swam           5 0.00804 2.48  0.0200
## 10       2 pool           8 0.0129  1.39  0.0178
## # ℹ 54 more rows

To daje riječi koje su najkarakterističnije za svako poglavlje.

Grafički prikaz često je vrlo informativan.

library(ggplot2)

tfidf_top |>
ggplot(aes(x = reorder(word, tf_idf), y = tf_idf)) +
geom_col() +
coord_flip() +
facet_wrap(~ chapter, scales = "free") +
labs(
x = "Riječ",
y = "TF-IDF",
title = "Najkarakterističnije riječi po poglavljima"
)

Rezultat TF-IDF analize pokazuje riječi koje su specifične za pojedina poglavlja. Takve riječi često označavaju:

  • likove koji se pojavljuju u određenom dijelu priče
  • specifične događaje
  • tematske elemente poglavlja.

Za razliku od jednostavne frekvencije riječi, TF-IDF omogućuje identificiranje pojmova koji najbolje razlikuju pojedine dijelove teksta.


U ovom primjeru vidjeli smo kako iz teksta možemo dobiti:

  • frekvencije riječi
  • veličinu vokabulara
  • najčešće pojmove u tekstu
  • osnovne vizualizacije.

Takva analiza opisuje statističku strukturu teksta, ali ne govori mnogo o njegovom značenju ili emocijama. U sljedećoj lekciji proširit ćemo analizu metodama sentiment analize, koje omogućuju procjenu emocionalnog tona teksta.




Memento

Korpus: Skup dokumenata za analizu.

Dokument: Jedna jedinica teksta (objava, članak, komentar).

Token: Osnovna jedinica nakon tokenizacije (riječ, bigram).

Tokenizacija: Razbijanje teksta na tokene.

Pretprocesiranje: Skup koraka prije analize (čišćenje, normalizacija, tokenizacija).

Normalizacija teksta: Usklađivanje zapisa (npr. mala slova, uklanjanje interpunkcije).

Stop riječi: Česte riječi male informativnosti (npr. i, je, da, the, and).

Frekvencija riječi: Broj pojavljivanja tokena u tekstu.

TF (Term Frequency): Koliko je pojam čest u dokumentu.

TF–IDF: Težina pojma koja naglašava riječi česte u jednom dokumentu, ali rijetke u korpusu.

Bag-of-words: Reprezentacija teksta u kojoj se red riječi zanemaruje, a koristi se samo frekvencija pojmova.

N-grami: Sekvence od n uzastopnih tokena (bigram, trigram).

Vokabular: Skup svih jedinstvenih tokena u korpusu.

Sparsity: Velik broj nula u dokument–termin matrici.

Zipfov zakon: Empirijska pravilnost prema kojoj je frekvencija riječi obrnuto proporcionalna njezinu rangu u listi frekvencija.

Vizualizacija teksta: Grafički prikaz rezultata analize (bar grafovi, wordcloud, mreže).

Lematizacija: Svođenje riječi na osnovni oblik (lemma).

Stemming: Svođenje riječi na korijen; grublji postupak od lematizacije.




Pitanja za ponavljanje

Odaberite sve odgovore koje smatrate točnima.

  1. Koje tvrdnje točno opisuju pojam korpusa?
  • Korpus je skup dokumenata koji se analiziraju kao cjelina.
  • Korpus je tablica u kojoj svaki red predstavlja jedan token.
  • Korpus može sadržavati članke, komentare, objave ili poglavlja knjige.
  • Korpus je nužno jedna jedina velika tekstualna datoteka.
  • Korpus uvijek mora sadržavati numeričke i tekstualne varijable zajedno.
  1. Koje tvrdnje točno opisuju tokenizaciju?
  • Tokenizacija razbija tekst na manje jedinice pogodne za analizu.
  • Token može biti riječ, bigram ili druga osnovna jedinica teksta.
  • Tokenizacija automatski provodi sentiment analizu dokumenta.
  • Tokenizacija je isto što i lematizacija.
  • Tokenizacija je temelj za frekvencijsku analizu riječi.
  1. Koje tvrdnje najbolje opisuju bag-of-words reprezentaciju?
  • Bag-of-words zanemaruje red riječi u dokumentu.
  • Bag-of-words opisuje dokument pomoću frekvencija pojmova.
  • Bag-of-words uvijek čuva sintaktičke odnose među riječima.
  • Bag-of-words je jednostavan i često koristan za eksploratornu analizu.
  • Bag-of-words je prikladan samo za vrlo male korpuse.
  1. Koje tvrdnje točno opisuju n-grame?
  • Bigram je sekvenca od dvije uzastopne riječi.
  • Trigram je sekvenca od tri uzastopne riječi.
  • N-grami u odnosu na bag-of-words bolje čuvaju lokalni kontekst.
  • N-grami nužno uklanjaju problem sparsity u matrici.
  • N-grami se koriste isključivo u sentiment analizi.
  1. Koje tvrdnje točno opisuju stop riječi?
  • Stop riječi su vrlo česte riječi male informativnosti.
  • Stop riječi se često uklanjaju kako bi analiza bila fokusiranija.
  • Stop riječi su uvijek iste u svim jezicima i domenama.
  • Riječi poput the, and i to tipični su primjeri stop riječi u engleskom jeziku.
  • Uklanjanje stop riječi uvijek je obavezno, bez iznimke.
  1. Koje tvrdnje točno opisuju TF-IDF?
  • TF-IDF kombinira učestalost riječi u dokumentu i njezinu rijetkost u korpusu.
  • Visok TF-IDF često imaju riječi karakteristične za pojedini dokument.
  • Riječi koje se pojavljuju u gotovo svim dokumentima obično imaju niži TF-IDF.
  • TF-IDF je mjera koja izravno procjenjuje emocionalni ton teksta.
  • TF-IDF se može računati samo ako korpus ima točno dva dokumenta.
  1. Koje tvrdnje najbolje opisuju Zipfov zakon?
  • Nekoliko riječi pojavljuje se vrlo često, a većina riječi rijetko.
  • Frekvencija riječi približno opada s porastom njezina ranga.
  • Zipfov zakon najčešće se prikazuje odnosom rang–frekvencija.
  • Zipfov zakon znači da sve riječi u tekstu imaju približno istu frekvenciju.
  • Zipfov zakon pokazuje zašto dokument–termin matrice često postaju rijetke.
  1. Istraživačica je preuzela književni tekst iz Project Gutenberga. U tekstu primjećuje prazne retke, duplikate redaka i oznake poglavlja. Koji su sljedeći koraci metodološki najrazumniji prije tokenizacije?
  • Ukloniti prazne retke i uzastopne duplikate.
  • Standardizirati tekst, primjerice pretvaranjem u mala slova.
  • Neposredno izračunati TF-IDF bez dodatnog čišćenja.
  • Ukloniti interpunkciju i druge tehničke znakove ako nisu analitički važni.
  • Najprije izraditi mrežu ko-pojavljivanja riječi pa tek onda čistiti tekst.
  1. U tekstu se pojavljuju naslovi poglavlja oblika CHAPTER I., CHAPTER II. i sl. Želite koristiti poglavlje kao dokument za TF-IDF analizu. Koji su koraci primjereni?
  • Identificirati retke koji označavaju početak poglavlja.
  • Numerirati poglavlja tako da svaki redak dobije oznaku pripadnog poglavlja.
  • Spojiti sve retke cijelog romana u jedan jedini dokument.
  • Po potrebi ukloniti sadržaj ili uvodne dijelove koji nisu stvarni tekst poglavlja.
  • Naslove poglavlja zadržati kao obične riječi bez ikakve provjere.
  1. Student želi studentima demonstrirati što se događa s tekstom u svakom koraku pretprocesiranja. Koji je pristup didaktički najprimjereniji?
  • Prikazati čišćenje korak po korak, uz kratki uvid u rezultat nakon svakog koraka.
  • Sve korake sakriti u jednu složenu naredbu kako bi kod bio što kraći.
  • Nakon svake transformacije pokazati nekoliko redaka rezultata.
  • Jasno odvojiti čišćenje prije tokenizacije od filtriranja tokena nakon tokenizacije.
  • Preskočiti prikaz međukoraka jer studenti ionako vide konačan rezultat.
  1. Istraživač želi usporediti najkarakterističnije riječi po poglavljima. Koji je metodološki odabir najprikladniji?
  • Definirati poglavlje kao dokument.
  • Prebrojati riječi unutar svakog poglavlja.
  • Primijeniti bind_tf_idf() nad riječima i identifikatorom poglavlja.
  • Izračunati samo ukupne frekvencije za cijeli roman bez oznake poglavlja.
  • Odabrati nekoliko najviših tf_idf vrijednosti unutar svakog poglavlja.
  1. U korpusu se pojavljuju bigrami poput white rabbit, mock turtle i march hare. Koji je razlog da se u analizi uključe i bigrami, a ne samo pojedinačne riječi?
  • Bigrami mogu otkriti ustaljene izraze i imena sastavljena od dvije riječi.
  • Bigrami djelomično čuvaju red riječi i lokalni kontekst.
  • Bigrami nužno smanjuju broj značajki u odnosu na bag-of-words.
  • Bigrami mogu biti korisni most prema kasnijoj analizi mreža teksta.
  • Bigrami automatski zamjenjuju potrebu za bilo kakvim čišćenjem teksta.
  1. Imate tokenizirani korpus i želite ukloniti vrlo česte riječi male informativnosti. Koji su metodološki opravdani izbori?
  • Primijeniti anti_join(stop_words, by = "word") nakon tokenizacije.
  • Koristiti paket stopwords ili ugrađeni skup stop_words iz tidytext.
  • Uklanjati stop riječi isključivo prije tokenizacije, nikada poslije.
  • Provjeriti odgovara li lista stop riječi jeziku i vrsti teksta.
  • Pretpostaviti da ista lista stop riječi jednako dobro radi za svaki korpus.
  1. Želite pokazati da bag-of-words i n-grami daju različite informacije. Koji bi primjer ili postupak najbolje podržao tu usporedbu?
  • Usporediti tablicu najčešćih riječi s tablicom najčešćih bigrama.
  • Pokazati da se u bag-of-words pristupu gubi red riječi.
  • Izračunati sentiment score i time zamijeniti usporedbu reprezentacija.
  • Istaknuti da bigrami mogu otkriti izraze koji se ne vide iz pojedinačnih riječi.
  • Zaključiti da je jedna reprezentacija uvijek univerzalno bolja od druge.
  1. U tablici frekvencija nakon uklanjanja stop riječi vrh ljestvice čine riječi: alice, queen, rabbit, hatter, gryphon. Koje su interpretacije najprimjerenije?
  • U tekstu dominiraju nazivi likova i ključni motivi pripovijedanja.
  • Nakon uklanjanja stop riječi u vrhu ljestvice ostaju informativniji pojmovi.
  • Ova tablica sama po sebi pokazuje emocionalni ton romana.
  • Frekvencije otkrivaju koje su riječi globalno najzastupljenije u korpusu.
  • Na temelju same tablice možemo zaključiti da roman nužno ima pozitivan sentiment.
  1. U tokeniziranom korpusu dobiveno je vocab_size = 2650. Koje su interpretacije opravdane?
  • Korpus sadrži 2650 jedinstvenih tokena nakon provedenog čišćenja.
  • Veličina vokabulara govori o raznolikosti korištenih riječi u korpusu.
  • Ova vrijednost predstavlja ukupan broj svih pojavljivanja riječi u tekstu.
  • Veličina vokabulara može se promijeniti nakon uklanjanja stop riječi.
  • Sama po sebi ova vrijednost ne govori koje su riječi najvažnije po poglavljima.
  1. Na log-log grafu ranga i frekvencije vidi se izrazit pad za najčešće riječi, a zatim približno linearan obrazac za veći dio preostalih riječi. Koje su interpretacije najprimjerenije?
  • Raspodjela je u skladu s očekivanjem prema Zipfovu zakonu.
  • Mali broj riječi dominira tekstom, a velik broj riječi javlja se rijetko.
  • Takav graf pokazuje da su sve riječi podjednako informativne.
  • Približno linearan odnos na log-log skali tipičan je za raspodjelu rang–frekvencija u tekstovima.
  • Takav rezultat automatski dokazuje da je tekst sentimentno negativan.
  1. Za dva poglavlja dobiveni su sljedeći top pojmovi prema TF-IDF-u:
    Poglavlje 3: caucus, race, dodo, prizes, committee
    Poglavlje 8: queen, croquet, hedgehog, soldiers, garden
    Koje su interpretacije najprimjerenije?
  • TF-IDF izdvaja riječi koje su specifične za sadržaj pojedinog poglavlja.
  • Popisi sugeriraju da su ta dva poglavlja tematski različita.
  • Ovi rezultati znače da su navedene riječi najčešće u cijelom romanu.
  • TF-IDF pomaže razlikovati dokumente čak i kada pripadaju istom korpusu.
  • Ovakav rezultat automatski znači da poglavlje 8 ima više riječi od poglavlja 3.
  1. U tablici najčešćih bigrama nakon filtriranja stop riječi nalaze se white rabbit, mock turtle, march hare, mad hatter. Koje su interpretacije najprimjerenije?
  • Bigramska reprezentacija otkriva višerječne izraze koje unigrami lako rasprše.
  • Takvi bigrami mogu upućivati na likove, entitete ili ustaljene fraze u tekstu.
  • Ovakav rezultat pokazuje prednost n-grama u očuvanju lokalnog konteksta.
  • Ovakav rezultat dokazuje da bag-of-words nema nikakvu analitičku vrijednost.
  • Na temelju bigrama možemo bez daljnje analize izračunati točan sentiment svakog poglavlja.
  1. U korpusu s 12 poglavlja riječ gryphon pojavljuje se 9 puta, ali gotovo isključivo u jednom poglavlju, dok se riječ alice pojavljuje 120 puta raspoređenih kroz gotovo sva poglavlja. Koje su interpretacije najprimjerenije?
  • Riječ gryphon može imati viši TF-IDF u specifičnom poglavlju nego riječ alice.
  • Vrlo česta riječ raspoređena kroz mnoge dokumente ne mora biti najbolji diferencijator.
  • Riječ alice će nužno imati viši TF-IDF u svakom poglavlju jer je globalno češća.
  • TF-IDF nagrađuje riječi koje pomažu razlikovati jedan dokument od ostalih.
  • Ovaj primjer pokazuje razliku između globalne frekvencije i specifične važnosti u dokumentu.



Korištena literatura

Carroll, L. (1865). Alice’s Adventures in Wonderland. London: Macmillan. (Project Gutenberg varijanta korištena u ovoj analizi.)

Kwartler, T. (2017). Text mining in practice with R. John Wiley & Sons.

Silge, J., & Robinson, D. (2016). tidytext: Text mining and analysis using tidy data principles in R. Journal of Open Source Software, 1(3), 37. https://doi.org/10.21105/joss.00037

Robinson, D., & Silge, J. (2023). tidytext: Text mining using dplyr, ggplot2, and other tidy tools. R package version.

Benoit, K., Watanabe, K., Wang, H., Nulty, P., Obeng, A., Müller, S., & Matsuo, A. (2018). quanteda: An R package for the quantitative analysis of textual data. Journal of Open Source Software, 3(30), 774.

Feinerer, I., Hornik, K., & Meyer, D. (2008). Text mining infrastructure in R. Journal of Statistical Software, 25(5), 1–54.

Ljubešić, N., Tercon, L., & Dobrovoljc, K. (2024). CLASSLA-Stanza: The Next Step for Linguistic Processing of South Slavic Languages. https://pypi.org/project/classla/

Manning, C. D., Raghavan, P., & Schütze, H. (2008). Introduction to information retrieval. Cambridge University Press.

Pedersen, T. L. (2023). ggwordcloud: A word cloud geom for ggplot2. R package.

Fellows, I. (2018). wordcloud: Word clouds. R package.

Bengtsson, H. (2023). stopwords: Multilingual stopword lists. R package.

Wickham, H. (2023). stringr: Simple, consistent wrappers for common string operations. R package.

Wijffels, J. (2017). udpipe: Tokenization, Parts of Speech Tagging, Lemmatization and Dependency Parsing with the ‘UDPipe’ ‘NLP’ Toolkit R package.

Leifeld, P., & Roberts, M. E. (2017). text: Analyses of text using transformer models. R package.

Peterson, B., & others. (2023). textmineR: Functions for text mining and topic modeling. R package.

Ključ odgovora

  1. a, c

  2. a, b, e

  3. a, b, d

  4. a, b, c

  5. a, b, d

  6. a, b, c

  7. a, b, c, e

  8. a, b, d

  9. a, b, d

  10. a, c, d

  11. a, b, c, e

  12. a, b, d

  13. a, b, d

  14. a, b, d

  15. a, b, d

  16. a, b, d, e

  17. a, b, d

  18. a, b, d

  19. a, b, c

  20. a, b, d, e